From 1bb6f6650e995dc1be1c50660af6f55c6f7fdbfe Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Fri, 11 Mar 2016 11:26:14 +0100 Subject: [PATCH 001/183] #31 Created new Moddels according to new API --- .gitignore | 46 ++++- .../zalando/nakadi/client/model/Model.scala | 190 ++++++++++++++++++ 2 files changed, 232 insertions(+), 4 deletions(-) create mode 100644 src/main/scala/org/zalando/nakadi/client/model/Model.scala diff --git a/.gitignore b/.gitignore index 49bb490..2160f35 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,42 @@ -/target/ -/project/*/ -/.idea/ -*.iml +*.class +*.log + +# sbt specific +.cache +.history +.lib/ +dist/* +target/ +lib_managed/ +src_managed/ +project/boot/ +project/plugins/project/ + +# Scala-IDE specific +.scala_dependencies +.worksheet + +logs +project/project +project/target +target +tmp +.history +dist +/.idea +/*.iml +/out +/.idea_modules +/.classpath +/.project +/RUNNING_PID +/.settings +/.cache-main +/.cache-tests +/bin +.DS_Store + +#misc +aws_host.txt +scm-source.json +*.notes \ No newline at end of file diff --git a/src/main/scala/org/zalando/nakadi/client/model/Model.scala b/src/main/scala/org/zalando/nakadi/client/model/Model.scala new file mode 100644 index 0000000..ef1c1f7 --- /dev/null +++ b/src/main/scala/org/zalando/nakadi/client/model/Model.scala @@ -0,0 +1,190 @@ +package org.zalando.nakadi.client.model + +/** + * The Event definition will be externalized in future versions of this document. + * A basic payload of an Event. The actual schema is dependent on the information configured for the + * EventType, as is its enforcement (see POST /event-types). Setting of metadata properties are + * dependent on the configured enrichment as well. + * For explanation on default configurations of validation and enrichment, see documentation of + * `EventType.type`. + * For concrete examples of what will be enforced by Nakadi see the objects BusinessEvent and + * DataChangeEvent below. + * @param eventType + * @param additionalProperties Default value is true + * @param title + */ +case class Event( + eventType: AnyRef, + additionalProperties: Boolean, + title: String) + +/** + * Metadata for this Event. Contains commons fields for both Business and DataChange Events. Most are enriched by Nakadi upon reception, but they in general MIGHT be set by the client. + * @param eid Identifier of this Event. Clients are allowed to generate this and this SHOULD be guaranteed to be unique from the perspective of the producer. Consumers MIGHT use this value to assert uniqueness of reception of the Event. + * @param eventType The EventType of this Event. This is enriched by Nakadi on reception of the Event based on the endpoint where the Producer sent the Event to. If provided MUST match the endpoint. Failure to do so will cause rejection of the Event. + * @param occurredAt Timestamp of creation of the Event generated by the producer. + * @param receivedAt Timestamp of the reception of the Event by Nakadi. This is enriched upon reception of the Event. If set by the producer Event will be rejected. + * @param parentEids Event identifier of the Event that caused the generation of this Event. Set by the producer. + * @param flowId The flow-id of the producer of this Event. As this is usually a HTTP header, this is enriched from the header into the metadata by Nakadi to avoid clients having to explicitly copy this. + * @param metadata This Metadata contains common fields unrelated to Nakadi logic. Should be mainly enriched by the Consumer. + * + */ +case class EventMetadata( + eid: String, + eventType: String, + occurredAt: String, + receivedAt: String, + parentEids: Seq[String], + flowId: String, + metadata: Map[String, Any]) + +/** + * A Business Event. Usually represents a status transition in a Business process. + * + * @param metadata Metadata for this Event. Contains commons fields for both Business and DataChange Events. Most are enriched by Nakadi upon reception, but they in general MIGHT be set by the client. + */ +case class BusinessEvent(metadata: EventMetadata) + +/** + * Indicators of a `DataChangeEvent`'s referred data type and the type of operations done on them. + * @param dataType The datatype of the `DataChangeEvent`. + * @param dataOp The type of operation executed on the entity. * C: Creation * U: Update * D: Deletion * S: Snapshot + */ +case class DataChangeEventQualifier( + dataType: String, + dataOperation: DataOperation.Value) + +/** + * A Data change Event. Represents a change on a resource. + * + * @param data The payload of the type + * @param eventQualifier Indicators of a `DataChangeEvent`'s referred data type and the type of operations done on them. + * @param metadata Metadata for this Event. Contains commons fields for both Business and DataChange Events. Most are enriched by Nakadi upon reception, but they in general MIGHT be set by the client + */ +case class DataChangeEvent( + data: AnyRef, + eventQualifier: DataChangeEventQualifier, + metadata: EventMetadata) + +/** + * @ param problemType An absolute URI that identifies the problem type. When dereferenced, it SHOULD provide human-readable API documentation for the problem type (e.g., using HTML). This Problem object is the same as provided by https://github.com/zalando/problem + * @ param title A short, summary of the problem type. Written in English and readable for engineers (usually not suited for non technical stakeholders and not localized) + * @ param status The HTTP status code generated by the origin server for this occurrence of the problem. + * @ param detail A human readable explanation specific to this occurrence of the problem. + * @ param instance An absolute URI that identifies the specific occurrence of the problem. It may or may not yield further information if dereferenced. + */ +case class Problem( + problemType: String, + title: String, + status: Int, + detail: String, + instance: String) + +case class Metrics() //TODO: It is not defined yet! + +/** + * Partition information. Can be helpful when trying to start a stream using an unmanaged API. This information is not related to the state of the consumer clients. + * @param partition The partition's id. + * @param oldestAvailableOffset An offset of the oldest available Event in that partition. This value will be changing upon removal of Events from the partition by the background archiving/cleanup mechanism. + * @param newestAvailableOffset An offset of the newest available Event in that partition. This value will be changing upon reception of new events for this partition by Nakadi. This value can be used to construct a cursor when opening streams (see `GET /event-type/{name}/events` for details). Might assume the special name BEGIN, meaning a pointer to the offset of the oldest available event in the partition. + */ +case class Partition( + partition: String, + oldestAvailableOffset: String, + newestAvailableOffset: String) +/** + * @param partition Id of the partition pointed to by this cursor. + * @param offset Offset of the event being pointed to. + */ +case class Cursor( + partition: String, + offset: String) + +/** + * One chunk of events in a stream. A batch consists of an array of `Event`s plus a `Cursor` pointing to the offset of the last Event in the stream. The size of the array of Event is limited by the parameters used to initialize a Stream. If acting as a keep alive message (see `GET /event-type/{name}/events`) the events array will be omitted. Sequential batches might repeat the cursor if no new events arrive. + * @param cursor The cursor point to an event in the stream. + * @param events The Event definition will be externalized in future versions of this document. A basic payload of an Event. The actual schema is dependent on the information configured for the EventType, as is its enforcement (see POST /event-types). Setting of metadata properties are dependent on the configured enrichment as well. For explanation on default configurations of validation and enrichment, see documentation of `EventType.type`. For concrete examples of what will be enforced by Nakadi see the objects BusinessEvent and DataChangeEvent below. + */ +case class EventStreamBatch( + cursor: Cursor, + events: Seq[Event]) + +/** + * An event type defines the schema and its runtime properties. + * @param name Name of this EventType. Encodes the owner/responsible for this EventType. The name for the EventType SHOULD follow the pattern, but is not enforced 'stups_owning_application.event-type', for example 'gizig.price-change'. The components of the name are: * Organization: the organizational unit where the team is located; can be omitted. * Team name: name of the team responsible for owning application; can be omitted. * Owning application: SHOULD match the field owning_application; indicates * EventType name: name of this EventType; SHOULD end in ChangeEvent for DataChangeEvents; MUST be in the past tense for BusinessEvents. (TBD: how to deal with organizational changes? Should be reflected on the name of the EventType? Isn't it better to omit [organization:team] completely?) + * @param owner Indicator of the Application owning this `EventType`. + * @param category Defines the category of this EventType. The value set will influence, if not set otherwise, the default set of validation-strategies, enrichment-strategies, and the effective_schema in the following way: - `undefined`: No predefined changes apply. `effective_schema` is exactly the same as the `EventTypeSchema`. Default validation_strategy for this `EventType` is `[{name: 'schema-validation'}]`. - `data`: Events of this category will be DataChangeEvents. `effective_schema` contains `metadata`, and adds fields `data_op` and `data_type`. The passed EventTypeSchema defines the schema of `data`. Default validation_strategy for this `EventType` is `[{name: 'datachange-schema-validation'}]`. - `business`: Events of this category will be BusinessEvents. `effective_schema` contains `metadata` and any additionally defined properties passed in the `EventTypeSchema` directly on top level of the Event. If name conflicts arise, creation of this EventType will be rejected. Default validation_strategy for this `EventType` is `[{name: 'schema-validation'}]`. + * @param effectiveSchema The effective schema of this `EventType`. The predefined schema validator will use the value of this field as base for the validation. The creation of EventTypes of different categories implies a distinct general structure for the payload of events of each type. This ultimate format the payload must adhere to is reflected in the `effective_schema`. See description of field `category` for more details of its generation. This field is informative and its contents cannot be manipulated directly. + * @param validationStrategies Determines the validation that has to be executed upon reception of Events of this type. Events are rejected if any of the rules fail (see details of Problem response on the Event publishing methods). Rule evaluation order is the same as in this array. If not explicitly set, default value will respect the definition of the `EventType.category`. + * @param enrichmentStrategies Determines the enrichment to be performed on an Event upon reception. Enrichment is performed once upon reception (and after validation) of an Event and is only possible on fields that are not defined on the incoming Event. See documentation for the write operation for details on behaviour in case of unsuccessful enrichment. + * @param partitionResolutionStrategy Determines how the assignment of the event to a Partition should be handled. + * @param schema The schema for this EventType. This is expected to be a json schema in yaml format (other formats might be added in the future). + * @param dataKeyFields Indicators of the path of the properties that constitute the primary key (identifier) of the data within this Event. If set MUST be a valid required field as defined in the schema. (TBD should be required? Is applicable only to both Business and DataChange Events?) + * @param partitioningKeyFields Indicator of the field used for guaranteeing the ordering of Events of this type (used by the PartitionResolutionStrategy). If set MUST be a valid required field as defined in the schema. + * + */ +case class EventType( + name: String, + owner: String, + category: String, + effectiveSchema: String, + validationStrategies: Seq[String], + enrichmentStrategies: Seq[String], + partitionResolutionStrategy: PartitionResolutionStrategy, //Different naming + schema: EventTypeSchema, + dataKeyFields: Seq[String], + partitioningKeyFields: Seq[String]) + +/** + * The schema for an EventType, expected to be a json schema in yaml format (other formats might be added in the future). + * @param type The type of schema definition (avro, json-schema, etc). + * @param schema The schema as string in the syntax defined in the field type. Failure to respect the syntax will fail any operation on an EventType. + */ +case class EventTypeSchema( + schemaType: String, + schema: String) + +/** + * Defines a rule for validation of an incoming Event. Rules might require additional parameters; see the `doc` field of the existing rules for details. See GET /registry/validation-strategies for a list of available rules. + * @param name Name of the strategy. + * @param doc Documentation for the validation. + */ +case class EventValidationStrategy( + name: String, + doc: String) + +/** + * Defines a rule for the resolution of incoming Events into partitions. Rules might require additional parameters; see the `doc` field of the existing rules for details. See `GET /registry/partition-strategies` for a list of available rules. + * @param name Name of the strategy. + * @param doc Documentation for the partition resolution. + */ +case class PartitionResolutionStrategy( + name: String, + doc: String) + +/** + * Defines a rule for transformation of an incoming Event. No existing fields might be modified. In practice this is used to set automatically values in the Event upon reception (i.e. set a reception timestamp on the Event). Rules might require additional parameters; see the `doc` field of the existing rules for details. See GET /registry/enrichment-strategies for a list of available rules. + * @param name Name of the strategy. + * @param doc Documentation for the enrichment. + */ +case class EventEnrichmentStrategy( + name: String, + doc: String) + +/** + * Identifier for the type of operation when conducting an Entity. + * @param CREATE + * @param UPDATE + * @param DELETE + * @param SNAPSHOT + */ +case object DataOperation extends Enumeration { + type DataOperation = Value + val CREATE = Value("C") + val UPDATE = Value("U") + val DELETE = Value("D") + val SNAPSHOT = Value("S") + +} + + From 1eb72ffa3f970bfd2ca5ce6691ea61760c4e22fb Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Wed, 23 Mar 2016 12:54:42 +0100 Subject: [PATCH 002/183] WIP --- build.sbt | 39 +- .../org/zalando/nakadi/client/Client.java | 99 ---- .../org/zalando/nakadi/client/JListener.java | 99 ---- .../nakadi/client/JListenerWrapper.java | 41 -- .../zalando/nakadi/client/JavaClientImpl.java | 95 ---- src/main/resources/reference.conf | 40 +- .../org/zalando/nakadi/client/Conf.scala | 53 -- .../zalando/nakadi/client/Connection.scala | 182 ++++++ .../org/zalando/nakadi/client/Klient.scala | 125 ----- .../zalando/nakadi/client/KlientBuilder.scala | 120 ---- .../zalando/nakadi/client/KlientImpl.scala | 240 -------- .../org/zalando/nakadi/client/Listener.scala | 10 - .../org/zalando/nakadi/client/Model.scala | 24 - .../org/zalando/nakadi/client/Utils.scala | 92 ---- .../client/actor/KlientSupervisor.scala | 125 ----- .../nakadi/client/actor/ListenerActor.scala | 42 -- .../client/actor/PartitionReceiver.scala | 203 ------- .../org/zalando/nakadi/client/package.scala | 30 +- .../client/publisher/EventPublisher.scala | 43 ++ .../client/publisher/EventSubscriber.scala | 29 + .../client/publisher/EventTransformer.scala | 49 ++ .../nakadi/client/publisher/Main.scala | 20 + .../org/zalando/nakadi/client/JEventTest.java | 74 --- .../nakadi/client/JListenerWrapperTest.java | 143 ----- .../nakadi/client/JavaClientImplTest.java | 71 --- .../org/zalando/nakadi/client/utils/Main.java | 68 --- .../nakadi/client/KlientBuilderSpec.scala | 120 ---- .../zalando/nakadi/client/KlientSpec.scala | 516 ------------------ .../org/zalando/nakadi/client/Main.scala | 43 -- .../nakadi/client/utils/KlientMock.scala | 51 -- .../nakadi/client/utils/TestUtils.scala | 8 - src/test/scala/unit/ConfTest.scala | 11 +- 32 files changed, 388 insertions(+), 2517 deletions(-) delete mode 100644 src/main/java/org/zalando/nakadi/client/Client.java delete mode 100644 src/main/java/org/zalando/nakadi/client/JListener.java delete mode 100644 src/main/java/org/zalando/nakadi/client/JListenerWrapper.java delete mode 100644 src/main/java/org/zalando/nakadi/client/JavaClientImpl.java delete mode 100644 src/main/scala/org/zalando/nakadi/client/Conf.scala create mode 100644 src/main/scala/org/zalando/nakadi/client/Connection.scala delete mode 100644 src/main/scala/org/zalando/nakadi/client/Klient.scala delete mode 100644 src/main/scala/org/zalando/nakadi/client/KlientBuilder.scala delete mode 100644 src/main/scala/org/zalando/nakadi/client/KlientImpl.scala delete mode 100644 src/main/scala/org/zalando/nakadi/client/Listener.scala delete mode 100644 src/main/scala/org/zalando/nakadi/client/Model.scala delete mode 100644 src/main/scala/org/zalando/nakadi/client/Utils.scala delete mode 100644 src/main/scala/org/zalando/nakadi/client/actor/KlientSupervisor.scala delete mode 100644 src/main/scala/org/zalando/nakadi/client/actor/ListenerActor.scala delete mode 100644 src/main/scala/org/zalando/nakadi/client/actor/PartitionReceiver.scala create mode 100644 src/main/scala/org/zalando/nakadi/client/publisher/EventPublisher.scala create mode 100644 src/main/scala/org/zalando/nakadi/client/publisher/EventSubscriber.scala create mode 100644 src/main/scala/org/zalando/nakadi/client/publisher/EventTransformer.scala create mode 100644 src/main/scala/org/zalando/nakadi/client/publisher/Main.scala delete mode 100644 src/test/java/org/zalando/nakadi/client/JEventTest.java delete mode 100644 src/test/java/org/zalando/nakadi/client/JListenerWrapperTest.java delete mode 100644 src/test/java/org/zalando/nakadi/client/JavaClientImplTest.java delete mode 100644 src/test/java/org/zalando/nakadi/client/utils/Main.java delete mode 100644 src/test/scala/org/zalando/nakadi/client/KlientBuilderSpec.scala delete mode 100644 src/test/scala/org/zalando/nakadi/client/KlientSpec.scala delete mode 100644 src/test/scala/org/zalando/nakadi/client/Main.scala delete mode 100644 src/test/scala/org/zalando/nakadi/client/utils/KlientMock.scala delete mode 100644 src/test/scala/org/zalando/nakadi/client/utils/TestUtils.scala diff --git a/build.sbt b/build.sbt index a03b3c1..6d812e2 100644 --- a/build.sbt +++ b/build.sbt @@ -25,23 +25,26 @@ scalacOptions ++= Seq( resolvers += Resolver.mavenLocal resolvers += "Maven Central Server" at "http://repo1.maven.org/maven2" -libraryDependencies ++= Seq( - "com.typesafe.akka" %% "akka-actor" % "2.4.1", - "com.google.guava" % "guava" % "19.0", - "com.typesafe.scala-logging" %% "scala-logging" % "3.1.0", - "com.fasterxml.jackson.core" % "jackson-core" % "2.7.0", - "com.fasterxml.jackson.module" %% "jackson-module-scala" % "2.6.3", - "ch.qos.logback" % "logback-classic" % "1.1.3", - "com.typesafe.akka" %% "akka-http-experimental" % "2.0.2", - "com.typesafe" % "config" % "1.3.0", // good to mention to fix the version (though Akka brings it in as a transitive dependency) - - "com.typesafe.akka" %% "akka-testkit" % "2.3.11" % "test", - "org.scalatest" %% "scalatest" % "2.2.6" % "test", - "io.undertow" % "undertow-core" % "1.2.12.Final" % "test", - "io.undertow" % "undertow-servlet" % "1.2.12.Final" % "test", - "org.apache.commons" % "commons-io" % "1.3.2" % "test", - "com.google.code.findbugs" % "jsr305" % "1.3.9" % "test", - "junit" % "junit" % "4.12" % "test" -) +libraryDependencies ++= { + val akkaVersion = "2.4.2" + Seq( + "com.typesafe.akka" %% "akka-actor" % akkaVersion, + "com.google.guava" % "guava" % "19.0", + "com.typesafe.scala-logging" %% "scala-logging" % "3.1.0", + "com.fasterxml.jackson.core" % "jackson-core" % "2.7.0", + "com.fasterxml.jackson.module" %% "jackson-module-scala" % "2.6.3", + "ch.qos.logback" % "logback-classic" % "1.1.3", + "com.typesafe.akka" %% "akka-http-experimental" % akkaVersion withSources() withJavadoc(), + "com.typesafe" % "config" % "1.3.0", // good to mention to fix the version (though Akka brings it in as a transitive dependency) + "com.typesafe.akka" %% "akka-cluster-metrics" % akkaVersion withSources() withJavadoc(), + "com.typesafe.akka" %% "akka-testkit" % akkaVersion % "test", + "org.scalatest" %% "scalatest" % "2.2.6" % "test", + "io.undertow" % "undertow-core" % "1.2.12.Final" % "test", + "io.undertow" % "undertow-servlet" % "1.2.12.Final" % "test", + "org.apache.commons" % "commons-io" % "1.3.2" % "test", + "com.google.code.findbugs" % "jsr305" % "1.3.9" % "test", + "junit" % "junit" % "4.12" % "test" + ) +} git.baseVersion := "0.0.0" diff --git a/src/main/java/org/zalando/nakadi/client/Client.java b/src/main/java/org/zalando/nakadi/client/Client.java deleted file mode 100644 index 063d297..0000000 --- a/src/main/java/org/zalando/nakadi/client/Client.java +++ /dev/null @@ -1,99 +0,0 @@ -package org.zalando.nakadi.client; - -import akka.actor.Terminated; - -import java.util.List; -import java.util.Map; -import java.util.concurrent.Future; - -public interface Client { - /** - * Gets monitoring metrics. - * NOTE: metrics format is not defined / fixed - * - * @return immutable map of metrics data (value can be another Map again) - */ - Future> getMetrics(); - - /** - * Lists all known `Topics` in Event Store. - * - * @return immutable list of known topics - */ - Future> getTopics(); - - /** - * Get partition information of a given topic - * - * @param topic target topic - * @return immutable list of topic's partitions information - */ - Future> getPartitions(String topic); - - /** - * Post a single event to the given topic. Partition selection is done using the defined partition resolution. - * The partition resolution strategy is defined per topic and is managed by event store (currently resolved from - * hash over Event.orderingKey). - * @param topic target topic - * @param event event to be posted - * @return Void in case of success - */ - Future postEvent(final String topic, final Event event); - - /** - * Get specific partition - * - * @param topic topic where the partition is located - * @param partitionId id of the target partition - * @return Either error message or TopicPartition in case of success - */ - Future getPartition(String topic, String partitionId); - - /** - * Post event to specific partition. - * NOTE: not implemented by Nakadi yet - * - * @param topic topic where the partition is located - * @param partitionId id of the target partition - * @param event event to be posted - * @return Void in case of success - */ - Future postEventToPartition(String topic, String partitionId, Event event); - - /** - * Blocking subscription to events of specified topic and partition. - * (batchLimit is set to 1, batch flush timeout to 1, and streamLimit to 0 -> infinite streaming receiving 1 event per poll) - * - * @param parameters listen parameters - * @param listener listener consuming all received events - * @return Either error message or connection was closed and reconnect is set to false - */ - void listenForEvents(String topic, - String partitionId, - ListenParameters parameters, - Listener listener, - boolean autoReconnect); - - - /** - * Non-blocking subscription to a topic requires a `EventListener` implementation. The event listener must be thread-safe because - * the listener listens to all partitions of a topic (one thread each). - * - * @param parameters listen parameters - * @param listener listener consuming all received events - * @return {Future} instance of listener threads - */ - void subscribeToTopic(String topic, - ListenParameters parameters, - Listener listener, - boolean autoReconnect); - - - void unsubscribeTopic(String topic, Listener listener); - - - /** - * Shuts down the communication system of the client - */ - Future stop(); -} diff --git a/src/main/java/org/zalando/nakadi/client/JListener.java b/src/main/java/org/zalando/nakadi/client/JListener.java deleted file mode 100644 index be9fc77..0000000 --- a/src/main/java/org/zalando/nakadi/client/JListener.java +++ /dev/null @@ -1,99 +0,0 @@ -package org.zalando.nakadi.client; - - -import com.google.common.base.MoreObjects; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.Maps; -import scala.collection.JavaConversions; - -import java.util.HashMap; -import java.util.Map; -import java.util.Optional; - -import static com.google.common.base.Preconditions.checkNotNull; - -public interface JListener { - String id(); - void onReceive(String topic, String partition, Cursor cursor, JEvent event); - void onConnectionOpened(String topic, String partition); - void onConnectionFailed(String topic, String partition, int status, String error); - void onConnectionClosed(String topic, String partition, Optional lastCursor); - - final class JEvent{ - - private final String eventType; - private final String orderingKey; - private final Map metaData; - private final Map body; - - public JEvent(String eventType, String orderingKey, Map metaData, Map body) { - checkNotNull(eventType, "eventType must not be null"); - checkNotNull(orderingKey, "orderingKey must not be null"); - checkNotNull(metaData, "metaData must not be null"); - checkNotNull(body, "body must not be null"); - - this.eventType = eventType; - this.orderingKey = orderingKey; - this.metaData = metaData; - this.body = body; - } - - public JEvent(final Event event) { - checkNotNull(event, "event must not be null"); - - eventType = event.eventType(); - orderingKey = event.orderingKey(); - metaData = ImmutableMap.copyOf(JavaConversions.mapAsJavaMap(event.metadata())); - - final scala.collection.immutable.Map bodyMap = - (scala.collection.immutable.Map) event.body(); - body = ImmutableMap.copyOf(JavaConversions.mapAsJavaMap(bodyMap)); - } - - public String getEventType() { - return eventType; - } - - public String getOrderingKey() { - return orderingKey; - } - - public Map getMetaData() { - return mapDeeply(metaData); - } - - - private Map mapDeeply(final Map m) { - final HashMap newMap = Maps.newHashMap(); - - for(Map.Entry entry : m.entrySet()) { - final String key = entry.getKey(); - final Object value = entry.getValue(); - - if(value instanceof scala.collection.Map) { - final scala.collection.Map scalaMap = (scala.collection.Map) value; - newMap.put(key, mapDeeply((JavaConversions.mapAsJavaMap(scalaMap)))); - } - else { - newMap.put(key, value); - } - } - - return newMap; - } - - public Map getBody() { - return mapDeeply(body); - } - - @Override - public String toString() { - return MoreObjects.toStringHelper(this) - .add("eventType", eventType) - .add("orderingKey", orderingKey) - .add("metaData", metaData) - .add("body", body) - .toString(); - } - } -} diff --git a/src/main/java/org/zalando/nakadi/client/JListenerWrapper.java b/src/main/java/org/zalando/nakadi/client/JListenerWrapper.java deleted file mode 100644 index c3f8464..0000000 --- a/src/main/java/org/zalando/nakadi/client/JListenerWrapper.java +++ /dev/null @@ -1,41 +0,0 @@ -package org.zalando.nakadi.client; - - -import com.google.common.base.Preconditions; -import scala.Option; - -public class JListenerWrapper implements Listener { - - private final JListener listener; - - public JListenerWrapper(final JListener listener) { - Preconditions.checkNotNull("listener must not be null"); - this.listener = listener; - } - - @Override - public String id() { - return listener.id(); - } - - @Override - public void onReceive(String topic, String partition, Cursor cursor, Event event) { - listener.onReceive(topic, partition, cursor, new JListener.JEvent(event)); - } - - @Override - public void onConnectionOpened(String topic, String partition) { - listener.onConnectionOpened(topic, partition); - } - - @Override - public void onConnectionFailed(String topic, String partition, int status, String error) { - listener.onConnectionFailed(topic, partition, status, error); - } - - @Override - public void onConnectionClosed(String topic, String partition, Option lastCursor) { - listener.onConnectionClosed(topic, partition, Utils.convertToOptional(lastCursor)); - } - -} diff --git a/src/main/java/org/zalando/nakadi/client/JavaClientImpl.java b/src/main/java/org/zalando/nakadi/client/JavaClientImpl.java deleted file mode 100644 index 93528bc..0000000 --- a/src/main/java/org/zalando/nakadi/client/JavaClientImpl.java +++ /dev/null @@ -1,95 +0,0 @@ -package org.zalando.nakadi.client; - -import akka.actor.Terminated; -import com.google.common.base.MoreObjects; - -import java.util.List; -import java.util.Map; -import java.util.concurrent.Future; - -import static com.google.common.base.Preconditions.checkNotNull; - - -class JavaClientImpl implements Client { - - private final Klient klient; - - public JavaClientImpl(final Klient klient){ - this.klient = checkNotNull(klient, "Klient instance must not be null"); - } - - - @Override - public Future> getMetrics() { - return Utils.convertForMapRetrieval(klient.getMetrics()); - } - - - @Override - public Future> getTopics() { - return Utils.convertForListRetrieval((scala.concurrent.Future) klient.getTopics()); - } - - - @Override - public Future> getPartitions(String topic) { - return Utils.convertForListRetrieval((scala.concurrent.Future) klient.getPartitions(topic)); - } - - - @Override - public Future postEvent(final String topic, final Event event) { - return Utils.convert((scala.concurrent.Future) klient.postEvent(topic, event)); - } - - - @Override - public Future getPartition(final String topic, final String partitionId) { - return Utils.convert((scala.concurrent.Future) klient.getPartition(topic, partitionId)); - } - - - @Override - public Future postEventToPartition(final String topic, final String partitionId, final Event event) { - return Utils.convert((scala.concurrent.Future) klient.postEventToPartition(topic, partitionId, event)); - } - - - @Override - public void listenForEvents(final String topic, - final String partitionId, - final ListenParameters parameters, - final Listener listener, - final boolean autoReconnect) { - klient.listenForEvents(topic, partitionId, parameters, listener, autoReconnect); - } - - - @Override - public void subscribeToTopic(final String topic, - final ListenParameters parameters, - final Listener listener, - final boolean autoReconnect) { - klient.subscribeToTopic(topic, parameters, listener, autoReconnect); - } - - - @Override - public void unsubscribeTopic(final String topic, final Listener listener) { - klient.unsubscribeTopic(topic, listener); - } - - - @Override - public Future stop() { - return Utils.convertSimple(klient.stop()); - } - - - @Override - public String toString() { - return MoreObjects.toStringHelper(this) - .add("klient", klient) - .toString(); - } -} diff --git a/src/main/resources/reference.conf b/src/main/resources/reference.conf index ed14a2b..9c1f394 100644 --- a/src/main/resources/reference.conf +++ b/src/main/resources/reference.conf @@ -5,24 +5,26 @@ nakadi.client { // tbd. better names & organization into groups, maybe? - noListenerReconnectDelay = 10 seconds - pollParallelism = 100 - receiveBufferSize = 1024 bytes - - defaultBatchFlushTimeout = 5 seconds // was client.DEFAULT_BATCH_FLUSH_TIMEOUT_IN_SECONDS - defaultBatchLimit = 1 // was client.DEFAULT_BATCH_LIMIT - defaultStreamLimit = 0 // was client.DEFAULT_STREAM_LIMIT - - scoopListener { - selectorField = "id" - } - - supervisor { - // note: Supervisor strategy parameter names are from the Akka - keep them like this - maxNrOfRetries = 100 - withinTimeRange = 5 minutes - - resolveActorTimeout = 1 second - } + // noListenerReconnectDelay = 10 seconds + // pollParallelism = 100 + // receiveBufferSize = 1024 bytes + // + // defaultBatchFlushTimeout = 5 seconds // was client.DEFAULT_BATCH_FLUSH_TIMEOUT_IN_SECONDS + // defaultBatchLimit = 1 // was client.DEFAULT_BATCH_LIMIT + // defaultStreamLimit = 0 // was client.DEFAULT_STREAM_LIMIT + // + // scoopListener { + // selectorField = "id" + // } + // + // supervisor { + // // note: Supervisor strategy parameter names are from the Akka - keep them like this + // maxNrOfRetries = 100 + // withinTimeRange = 5 minutes + // + // resolveActorTimeout = 1 second + // } } + +akka.cluster.metrics.enabled=off diff --git a/src/main/scala/org/zalando/nakadi/client/Conf.scala b/src/main/scala/org/zalando/nakadi/client/Conf.scala deleted file mode 100644 index f7f1db8..0000000 --- a/src/main/scala/org/zalando/nakadi/client/Conf.scala +++ /dev/null @@ -1,53 +0,0 @@ -package org.zalando.nakadi.client - -import akka.util.Timeout -import com.typesafe.config.{Config, ConfigFactory} - -import scala.concurrent.duration.Duration -import scala.language.implicitConversions - -/* -* Access to the configuration in a typed fashion. -* -* Also ensures that the configuration is healthy during the process run. Any parsing errors or inconsistencies will -* be found during launch. -* -* Note: We're exposing durations as 'scala.concurrent.duration.Duration' (instead of 'java.time.Duration') for example -* because the Scala version has '.toSeconds' but Java version doesn't. AKa110216 -*/ -object Conf { - private - val root = ConfigFactory.load.getConfig("nakadi.client") - - // Typesafe Config is Java/Scala compatible and thus stores durations as 'java.time.Duration'. - // See -> http://stackoverflow.com/questions/32076311/converting-java-to-scala-durations - // - private implicit - def convDuration(v: java.time.Duration) = scala.concurrent.duration.Duration.fromNanos(v.toNanos) - - val noListenerReconnectDelay: Duration = root.getDuration("noListenerReconnectDelay") - val pollParallelism = root.getInt("pollParallelism") - val receiveBufferSize = root.getMemorySize("receiveBufferSize") - - - val defaultBatchFlushTimeout: Duration = root.getDuration("defaultBatchFlushTimeout") - val defaultBatchLimit = root.getInt("defaultBatchLimit") - val defaultStreamLimit = root.getInt("defaultStreamLimit") - - class cSupervisor(cfg: Config) { - val maxNrOfRetries= cfg.getInt("maxNrOfRetries") - val withinTimeRange: Duration = cfg.getDuration("withinTimeRange") - - val resolveActorTimeout = Timeout( cfg.getDuration("resolveActorTimeout") ) - } - val supervisor= new cSupervisor( root.getConfig("supervisor") ) - - // ^^^ new entries above, please ^^^ - - // Note: If we need to check config value ranges or consistency between multiple values, here's the place. - // Just throw an exception if something's not right. - // - if (false) { - throw new RuntimeException( "Bad config: ..." ) - } -} \ No newline at end of file diff --git a/src/main/scala/org/zalando/nakadi/client/Connection.scala b/src/main/scala/org/zalando/nakadi/client/Connection.scala new file mode 100644 index 0000000..9da8f68 --- /dev/null +++ b/src/main/scala/org/zalando/nakadi/client/Connection.scala @@ -0,0 +1,182 @@ +package org.zalando.nakadi.client + +import akka.http.scaladsl.Http +import akka.http.scaladsl.model._ +import akka.stream.ActorMaterializer +import akka.stream.scaladsl._ +import scala.concurrent.Future +import akka.actor.ActorSystem +import akka.http.scaladsl.model.Uri.apply +import scala.concurrent.ExecutionContext.Implicits.global +import org.slf4j.LoggerFactory +import com.typesafe.scalalogging.Logger +import org.slf4j.LoggerFactory +import akka.http.scaladsl.HttpsConnectionContext +import akka.http.scaladsl.model.headers.OAuth2BearerToken +import akka.http.scaladsl.model.MediaTypes.`application/json` +import javax.net.ssl.SSLContext +import java.security.SecureRandom +import java.security.cert.X509Certificate +import javax.net.ssl.{ SSLContext, TrustManager, X509TrustManager } +import scala.concurrent.duration.DurationInt +import akka.actor.Terminated + +trait Client { + + /** + * Returns monitoring metrics + */ + def metrics(): Future[HttpResponse] + + /** + * Returns a list of all registered EventTypes. + */ + def eventTypes(): Future[HttpResponse] + + /** + * Creates a new EventType. + * + * @param event - The event to create. + * + */ + def newEventType(eventType: Any): Future[Boolean] + + /** + * Returns the EventType identified by its name. + * @param name - Name of the EventType + */ + def eventType(name: String): Future[HttpResponse] + /** + * Updates the EventType identified by its name. + * + * + * @param name - Name of the EventType + * @param event - Event to update + */ + def updateEventType(name: String, event: Any): Future[HttpResponse] + /** + * Deletes an EventType identified by its name. + * + * + * @param name - Name of the EventType + */ + def deleteEventType(name: String): Future[HttpResponse] + + /** + * Starts a stream delivery for the specified partitions of the given EventType. + */ + def eventsOfEventType(eventType: String): Future[HttpResponse] + + def partitionsOfEventType(eventType: String): Future[HttpResponse] + + def partitionByName(eventType: String, partitionName: String): Future[HttpResponse] + + def validationStrategies(): Future[HttpResponse] + + def enrichmentStrategies(): Future[HttpResponse] + + def partitionStrategies(): Future[HttpResponse] + + /** + * Shuts down the communication system of the client + */ + + def stop(): Future[Terminated] + +} + +object Connection { + + def getNewSslContext(secured: Boolean, verified: Boolean): Option[HttpsConnectionContext] = { + (secured, verified) match { + case (true, true) => Some(new HttpsConnectionContext(SSLContext.getDefault)) + case (true, false) => + val permissiveTrustManager: TrustManager = new X509TrustManager() { + override def checkClientTrusted(x$1: Array[java.security.cert.X509Certificate], x$2: String): Unit = {} + override def checkServerTrusted(x$1: Array[java.security.cert.X509Certificate], x$2: String): Unit = {} + override def getAcceptedIssuers(): Array[X509Certificate] = Array.empty + } + val sslContext = SSLContext.getInstance("TLS") + sslContext.init(Array.empty, Array(permissiveTrustManager), new SecureRandom()) + Some(new HttpsConnectionContext(sslContext)) + case _ => None + } + } +} + +/** + * Class for handling the configuration and connections only. + */ +private[client] class Connection(host: String, port: Int, tokenProvider: () => String, securedConnection: Boolean, verifySSlCertificate: Boolean) extends Client { + import Connection._ + private implicit val actorSystem = ActorSystem("Nakadi-client") + private implicit val http = Http(actorSystem) + implicit val materializer = ActorMaterializer() + + private val timeout = 5.seconds + + val logger = Logger(LoggerFactory.getLogger(this.getClass)) + + def eventTypes(): Future[HttpResponse] = { + get(URI_EVENT_TYPES) + } + + def stop(): Future[Terminated] = actorSystem.terminate() + + private val connectionFlow: Flow[HttpRequest, HttpResponse, Future[Http.OutgoingConnection]] = { + getNewSslContext(securedConnection, verifySSlCertificate) match { + case Some(result) => http.outgoingConnectionHttps(host, port, result) + case None => + logger.warn("Disabling HTTPS") + http.outgoingConnection(host, port) + } + } + + private def get(endpoint: String): Future[HttpResponse] = { + logger.info("Calling {}", endpoint) + val response: Future[HttpResponse] = + Source.single(DefaultHttpRequest(endpoint)) + .via(connectionFlow). + runWith(Sink.head) + logError(response) + response + } + + private def logError(future: Future[Any]) { + future recover { + case e: Throwable => logger.error("Failed to call endpoint with: ", e.getMessage) + } + } + + private def DefaultHttpRequest(url: String): HttpRequest = { + HttpRequest(uri = url).withHeaders(headers.Authorization(OAuth2BearerToken(tokenProvider())), + headers.Accept(MediaRange(`application/json`))) + } +} + +object Main extends App { + + val host = "nakadi-sandbox.aruha-test.zalan.do" + val OAuth2Token = () => "5748c4f2-8673-410e-b02d-7fbdd71ac095" + val port = 443 + val client = new Connection(host, port, OAuth2Token, true, false) + val response = client.eventTypes() + implicit val materializer = client.materializer + // response.map { x => + // println("########################") + // println("########################") + // println("Client= " + x.toString) + // } + + response.map { response => + response.status match { + case status if (response.status.isSuccess()) => + println("" + response.toString()) + println("" + response.entity.toStrict(5.seconds).map(x => Some(x.data.decodeString("UTF-8")))) + println("" + response.entity.getContentType()) + client.stop() + case _ => + println("") + } + } +} \ No newline at end of file diff --git a/src/main/scala/org/zalando/nakadi/client/Klient.scala b/src/main/scala/org/zalando/nakadi/client/Klient.scala deleted file mode 100644 index c7be7a9..0000000 --- a/src/main/scala/org/zalando/nakadi/client/Klient.scala +++ /dev/null @@ -1,125 +0,0 @@ -package org.zalando.nakadi.client - -import akka.actor.Terminated - -import scala.concurrent.Future - - -/* - * Parameters for listening on Nakadi - * - * @param startOffset start position in 'queue' from which events are received - * @param batchLimit number of events which should be received at once (per poll). Must be > 0 - * @param batchFlushTimeoutInSeconds maximum time in seconds to wait for the flushing of each chunk; - * if the `batch_limit` is reached before this time is reached the messages are - * immediately flushed to the client - * @param streamLimit maximum number of events which can be consumed with this stream (consumption finishes after - * {streamLimit} events). If 0 or undefined, will stream indefinitely. Must be > -1 - */ -case class ListenParameters(startOffset: Option[String] = None, - batchLimit: Option[Int] = Some( Conf.defaultBatchLimit ), - batchFlushTimeoutInSeconds: Option[Int] = Some( Conf.defaultBatchFlushTimeout.toSeconds.toInt ), - streamLimit: Option[Int] = Some( Conf.defaultStreamLimit )) - -object ListenParametersUtils{ - /** - * Convenience method for Java clients - */ - def defaultInstance = new ListenParameters() -} - -trait Klient { - /** - * Gets monitoring metrics. - * NOTE: metrics format is not defined / fixed - * - * @return immutable map of metrics data (value can be another Map again) - */ - def getMetrics: Future[Either[String, Map[String, Any]]] - - /** - * Lists all known `Topics` in Event Store. - * - * @return immutable list of known topics - */ - def getTopics: Future[Either[String, List[Topic]]] - - /** - * Get partition information of a given topic - * - * @param topic target topic - * @return immutable list of topic's partitions information - */ - def getPartitions(topic: String): Future[Either[String, List[TopicPartition]]] - - /** - * Post a single event to the given topic. Partition selection is done using the defined partition resolution. - * The partition resolution strategy is defined per topic and is managed by event store (currently resolved from - * hash over Event.orderingKey). - * @param topic target topic - * @param event event to be posted - * @return Option representing the error message or None in case of success - */ - def postEvent(topic: String, event: Event): Future[Either[String,Unit]] - - /** - * Get specific partition - * - * @param topic topic where the partition is located - * @param partitionId id of the target partition - * @return Either error message or TopicPartition in case of success - */ - def getPartition(topic: String, partitionId: String): Future[Either[String, TopicPartition]] - - /** - * Post event to specific partition. - * NOTE: not implemented by Nakadi yet - * - * @param topic topic where the partition is located - * @param partitionId id of the target partition - * @param event event to be posted - * @return Option representing the error message or None in case of success - */ - def postEventToPartition(topic: String, partitionId: String, event: Event): Future[Either[String,Unit]] - - /** - * Blocking subscription to events of specified topic and partition. - * (batchLimit is set to 1, batch flush timeout to 1, and streamLimit to 0 -> infinite streaming receiving 1 event per poll) - * - * @param parameters listen parameters - * @param listener listener consuming all received events - * @return Either error message or connection was closed and reconnect is set to false - */ - def listenForEvents(topic: String, - partitionId: String, - parameters: ListenParameters, - listener: Listener, - autoReconnect: Boolean = false): Unit - - - /** - * Non-blocking subscription to a topic requires a `EventListener` implementation. The event listener must be thread-safe because - * the listener listens to all partitions of a topic (one thread each). - * - * @param parameters listen parameters - * @param listener listener consuming all received events - * @return {Future} instance of listener threads - */ - def subscribeToTopic(topic: String, - parameters: ListenParameters, - listener: Listener, - autoReconnect: Boolean = true): Future[Unit] - - - - def unsubscribeTopic(topic: String, listener: Listener): Unit - - /** - * Shuts down the communication system of the client - */ - def stop(): Future[Terminated] -} - -object Klient { - class KlientException(message: String, cause: Throwable = null) extends RuntimeException(message, cause) -} diff --git a/src/main/scala/org/zalando/nakadi/client/KlientBuilder.scala b/src/main/scala/org/zalando/nakadi/client/KlientBuilder.scala deleted file mode 100644 index c8fa344..0000000 --- a/src/main/scala/org/zalando/nakadi/client/KlientBuilder.scala +++ /dev/null @@ -1,120 +0,0 @@ -package org.zalando.nakadi.client - -import java.net.URI -import java.util.function.Supplier -import com.fasterxml.jackson.core.JsonParser -import com.fasterxml.jackson.databind.deser.DeserializationProblemHandler -import com.fasterxml.jackson.databind._ -import com.fasterxml.jackson.module.scala.DefaultScalaModule -import com.typesafe.scalalogging.LazyLogging - -object KlientBuilder{ - def apply(endpoint: URI = null, - port: Int = DEFAULT_PORT, - securedConnection: Boolean = false, - tokenProvider: () => String = null, - objectMapper: Option[ObjectMapper] = None) = - new KlientBuilder(endpoint, port, securedConnection, tokenProvider, objectMapper) - - private val DEFAULT_PORT = 8080 // having such a default is probably a bad idea. Rework later. tbd AKa120216 -} - -class KlientBuilder private (val endpoint: URI = null, - val port: Int, - val securedConnection: Boolean, - val tokenProvider: () => String = null, - val objectMapper: Option[ObjectMapper] = None) - extends LazyLogging -{ - def this() = this(null, KlientBuilder.DEFAULT_PORT, false, null, None) - - private def checkNotNull[T](subject: T): T = - if(Option(subject).isEmpty) throw new NullPointerException else subject - - - private def checkState[T](subject: T, predicate: (T) => Boolean, msg: String): T = - if(predicate(subject)) subject else throw new IllegalStateException() - - - def withEndpoint(endpoint: URI): KlientBuilder = - new KlientBuilder( - checkNotNull(endpoint), - port, - securedConnection, - tokenProvider, - objectMapper) - - - def withTokenProvider(tokenProvider: () => String): KlientBuilder = - new KlientBuilder( - endpoint, - port, - securedConnection, - checkNotNull(tokenProvider), - objectMapper) - - - def withJavaTokenProvider(tokenProvider: Supplier[String]) = withTokenProvider(() => tokenProvider.get()) - - - // TODO param check - def withPort(port: Int): KlientBuilder = - new KlientBuilder( - endpoint, - port, - securedConnection, - tokenProvider, - objectMapper) - - - def withSecuredConnection(securedConnection: Boolean = true) = - new KlientBuilder( - endpoint, - port, - securedConnection, - tokenProvider, - objectMapper) - - - def withObjectMapper(objectMapper: Option[ObjectMapper]): KlientBuilder = { - if(objectMapper.isDefined) objectMapper.get.registerModule(new DefaultScalaModule) - - new KlientBuilder(endpoint, port, securedConnection, tokenProvider, objectMapper) - } - - - def defaultObjectMapper: ObjectMapper = { - val mapper = new ObjectMapper - mapper.registerModule(new DefaultScalaModule) - mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) - mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) - mapper.setPropertyNamingStrategy(PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES) - mapper.addHandler(new DeserializationProblemHandler() { - override def handleUnknownProperty(ctxt: DeserializationContext, - jp: JsonParser, deserializer: - JsonDeserializer[_], - beanOrClass: AnyRef, - propertyName: String): Boolean = { - logger.warn(s"unknown property occurred in JSON representation: [beanOrClass=$beanOrClass, property=$propertyName]") - true - } - }) - mapper - } - - - def build(): Klient = - new KlientImpl( - checkState(endpoint, (s: URI) => Option(s).isDefined, "endpoint is not set -> try withEndpoint()"), - checkState(port, (s: Int) => port > 0, s"port $port is invalid"), - securedConnection, - checkState(tokenProvider, (s: () => String) => Option(s).isDefined, - "tokenProvider is not set -> try withTokenProvider()"), - objectMapper.getOrElse(defaultObjectMapper)) - - - def buildJavaClient(): Client = new JavaClientImpl(build()) - - - override def toString = s"KlientBuilder($endpoint, $tokenProvider, $objectMapper)" -} diff --git a/src/main/scala/org/zalando/nakadi/client/KlientImpl.scala b/src/main/scala/org/zalando/nakadi/client/KlientImpl.scala deleted file mode 100644 index 9f4e490..0000000 --- a/src/main/scala/org/zalando/nakadi/client/KlientImpl.scala +++ /dev/null @@ -1,240 +0,0 @@ -package org.zalando.nakadi.client - -import java.net.URI - -import akka.actor._ -import akka.http.scaladsl.model.MediaTypes.`application/json` -import akka.http.scaladsl.model.HttpMethods.POST -import akka.http.scaladsl.model.headers.OAuth2BearerToken -import akka.http.scaladsl.model._ -import akka.http.scaladsl.unmarshalling.{PredefinedFromEntityUnmarshallers, Unmarshaller} -import akka.stream.ActorMaterializer -import akka.stream.scaladsl.{Sink, Source} -import com.fasterxml.jackson.core.`type`.TypeReference -import com.fasterxml.jackson.databind.ObjectMapper -import com.typesafe.scalalogging.Logger -import org.slf4j.LoggerFactory -import org.zalando.nakadi.client.Klient.KlientException -import org.zalando.nakadi.client.Utils.outgoingHttpConnection -import org.zalando.nakadi.client.actor.KlientSupervisor._ -import org.zalando.nakadi.client.actor._ -import scala.concurrent.duration.Duration -import scala.concurrent.{Await, Future} -import scala.concurrent.ExecutionContext.Implicits.global -import scala.util.{Success, Failure} - - -protected class KlientImpl(val endpoint: URI, - val port: Int, - val securedConnection: Boolean, - val tokenProvider: () => String, - val objectMapper: ObjectMapper, - val klientSystem: Option[ActorSystem] = None) extends Klient{ - checkNotNull(endpoint, "endpoint must not be null") - checkNotNull(tokenProvider, "tokenProvider must not be null") - checkNotNull(objectMapper, "objectMapper must not be null") - - implicit val system = klientSystem.getOrElse(ActorSystem("nakadi-client")) - - val supervisor = system.actorOf(KlientSupervisor.props(endpoint, port, securedConnection, tokenProvider, objectMapper), - "klient-supervisor") - - implicit val materializer = ActorMaterializer() - - val logger = Logger(LoggerFactory.getLogger("KlientImpl")) - - - def checkNotNull(subject: Any, message: String) = if(Option(subject).isEmpty) throw new IllegalArgumentException(message) - def checkExists(subject: Option[Any], message: String) = if(subject.isEmpty) throw new IllegalArgumentException(message) - - - /** - * Gets monitoring metrics. - * NOTE: metrics format is not defined / fixed - * - * @return immutable map of metrics data (value can be another Map again) - */ - override def getMetrics: Future[Either[String, Map[String, Any]]] = - performDefaultGetRequest(URI_METRICS, new TypeReference[Map[String, Any]]{}) - - - /** - * Get partition information of a given topic - * - * @param topic target topic - * @return immutable list of topic's partitions information - */ - override def getPartitions(topic: String): Future[Either[String, List[TopicPartition]]] = { - checkNotNull(topic, "topic must not be null") - performDefaultGetRequest(String.format(URI_PARTITIONS,topic), new TypeReference[List[TopicPartition]]{}) - } - - - private def performDefaultGetRequest[T](uriPart: String, expectedType: TypeReference[T]): Future[Either[String, T]] = { - val request = HttpRequest(uri = uriPart) - .withHeaders(headers.Authorization(OAuth2BearerToken(tokenProvider.apply())), - headers.Accept(MediaRange(`application/json`))) - - logger.debug("sending [request={}]", request) - - Source - .single(request) - .via(outgoingHttpConnection(endpoint, port, securedConnection)) - .runWith(Sink.head) - .map(evaluateResponse(_, expectedType)) - } - - - - private def evaluateResponse[T](response: HttpResponse, expectedType: TypeReference[T]) :Either[String,T] = { - logger.debug("received [response={}]", response) - - if(response.status.intValue() < 200 || response.status.intValue() > 299) - Left(response.status + " - " + response.entity) - else - // TODO better way? - Await.result( - Unmarshaller.byteArrayUnmarshaller(response.entity).map(bytes => { - Right(objectMapper.readValue[T](bytes, expectedType)) - }), Duration.Inf) - - } - - - /** - * Get specific partition - * - * @param topic topic where the partition is located - * @param partitionId id of the target partition - * @return Either error message or TopicPartition in case of success - */ - override def getPartition(topic: String, partitionId: String): Future[Either[String, TopicPartition]] = { - checkNotNull(topic, "topic must not be null") - checkNotNull(partitionId, "partitionId must not be null") - performDefaultGetRequest(String.format(URI_PARTITION, topic, partitionId), new TypeReference[TopicPartition]{}) - } - - - /** - * Lists all known `Topics` in Event Store. - * - * @return immutable list of known topics - */ - def getTopics: Future[Either[String, List[Topic]]] = performDefaultGetRequest(URI_TOPICS, new TypeReference[List[Topic]]{}) - - - /** - * Blocking subscription to events of specified topic and partition. - * (batchLimit is set to 1, batch flush timeout to 1, and streamLimit to 0 -> infinite streaming receiving 1 event per poll) - * - * @param parameters listen parameters - * @param listener listener consuming all received events - * @return Either error message or connection was closed and reconnect is set to false - */ - override def listenForEvents(topic: String, partitionId: String, parameters: ListenParameters, listener: Listener, autoReconnect: Boolean = false): Unit = { - - checkNotNull(topic, "topic must not be null") - checkNotNull(partitionId, "partitionId must not be null") - checkNotNull(parameters, "list parameters must not be null") - checkNotNull(listener, "listener must not be null") - checkNotNull(autoReconnect, "autoReconnect must not be null") - - checkExists(parameters.batchFlushTimeoutInSeconds, "batchFlushTimeoutInSeconds is not set") - checkExists(parameters.batchLimit, "batchLimit is not set") - checkExists(parameters.startOffset, "startOffset is not specified") - checkExists(parameters.streamLimit, "streamLimit is not specified") - - supervisor ! NewSubscription(topic, partitionId, parameters, autoReconnect, listener) - } - - - /** - * Non-blocking subscription to a topic requires a `EventListener` implementation. The event listener must be thread-safe because - * the listener listens to all partitions of a topic (one thread each). - * - * @param parameters listen parameters - * @param listener listener consuming all received events - */ - // TODO earlier parameter check - override def subscribeToTopic(topic: String, parameters: ListenParameters, listener: Listener, autoReconnect: Boolean): Future[Unit] = { - getPartitions(topic).map{_ match { - case Left(errorMessage) => - throw new KlientException(s"a problem occurred while subscribing to [topic=$topic]: $errorMessage") - case Right(partitions: List[TopicPartition]) => - partitions.foreach(p => listenForEvents(topic, - p.partitionId, - ListenParameters( - Option(p.newestAvailableOffset), - parameters.batchLimit, - parameters.batchFlushTimeoutInSeconds, - parameters.streamLimit), - listener, - autoReconnect)) - } } - } - - - def unsubscribeTopic(topic: String, listener: Listener): Unit = system.eventStream.publish(Unsubscription(topic, listener)) - - - /** - * Post a single event to the given topic. Partition selection is done using the defined partition resolution. - * The partition resolution strategy is defined per topic and is managed by event store (currently resolved from - * hash over Event.orderingKey). - * - * @param topic target topic - * @param event event to be posted - * @return Option representing the error message or None in case of success - */ - override def postEvent(topic: String, event: Event): Future[Either[String,Unit]] = { - checkNotNull(topic, "topic must not be null") - performEventPost(String.format(URI_EVENT_POST, topic), event) - } - - - private def performEventPost(uriPart: String, event: Event): Future[Either[String,Unit]] = { - checkNotNull(event, "event must not be null") - - val request = HttpRequest(uri = uriPart, method = POST) - .withHeaders(headers.Authorization(OAuth2BearerToken(tokenProvider.apply()))) - .withEntity(ContentType(`application/json`), objectMapper.writeValueAsBytes(event)) - - logger.debug("sending [request={}]", request) - - import scala.concurrent.duration._ - import scala.language.postfixOps - - Source - .single(request) - .via(outgoingHttpConnection(endpoint, port, securedConnection)) - .runWith(Sink.head) - .flatMap(response => if(response.status.intValue() < 200 || response.status.intValue() > 299) - response.entity.toStrict(5 seconds).map( x => Some(x.data.decodeString("UTF-8"))) - else Future.successful(None)) - .map(_ match { - case None => Right(()) - case Some(v) => println(v); Left(v.asInstanceOf[String]) - }) - } - - - /** - * Post event to specific partition. - * NOTE: not implemented by Nakadi yet - * - * @param topic topic where the partition is located - * @param partitionId id of the target partition - * @param event event to be posted - * @return Option representing the error message or None in case of success - */ - override def postEventToPartition(topic: String, partitionId: String, event: Event): Future[Either[String,Unit]] = { - checkNotNull(topic, "topic must not be null") - performEventPost(String.format(URI_EVENTS_ON_PARTITION, topic, partitionId), event) - } - - - /** - * Shuts down the communication system of the client - */ - override def stop(): Future[Terminated] = system.terminate() -} diff --git a/src/main/scala/org/zalando/nakadi/client/Listener.scala b/src/main/scala/org/zalando/nakadi/client/Listener.scala deleted file mode 100644 index 234c1d0..0000000 --- a/src/main/scala/org/zalando/nakadi/client/Listener.scala +++ /dev/null @@ -1,10 +0,0 @@ -package org.zalando.nakadi.client - - -trait Listener { - def id: String - def onReceive(topic: String, partition: String, cursor: Cursor, event: Event): Unit - def onConnectionOpened(topic: String, partition: String): Unit - def onConnectionFailed(topic: String, partition: String, status: Int, error: String): Unit - def onConnectionClosed(topic: String, partition: String, lastCursor: Option[Cursor]): Unit -} diff --git a/src/main/scala/org/zalando/nakadi/client/Model.scala b/src/main/scala/org/zalando/nakadi/client/Model.scala deleted file mode 100644 index 8c79657..0000000 --- a/src/main/scala/org/zalando/nakadi/client/Model.scala +++ /dev/null @@ -1,24 +0,0 @@ -package org.zalando.nakadi.client - -import scala.collection.JavaConversions._ - -case class Cursor(partition: String, offset: String) - -case class Event(eventType: String, orderingKey: String, metadata: Map[String, Any], body: AnyRef) { - def this(eventType: String, orderingKey: String, metadata: java.util.Map[String, Any], body: AnyRef) = - this(eventType, orderingKey, metadata.toMap, body) -} - -case class Topic(name: String) - -case class TopologyItem (clientId: String, partitions: List[String]) { - def this(clientId: String, partitions: java.util.List[String]) = - this(clientId, partitions.toList) -} - -case class TopicPartition(partitionId: String, oldestAvailableOffset: String, newestAvailableOffset: String) - -case class SimpleStreamEvent(cursor: Cursor, events: List[Event], topology: List[TopologyItem]) { - def this(cursor: Cursor, events: java.util.List[Event], topology: java.util.List[TopologyItem]) = - this(cursor, events.toList, topology.toList) -} \ No newline at end of file diff --git a/src/main/scala/org/zalando/nakadi/client/Utils.scala b/src/main/scala/org/zalando/nakadi/client/Utils.scala deleted file mode 100644 index df3f906..0000000 --- a/src/main/scala/org/zalando/nakadi/client/Utils.scala +++ /dev/null @@ -1,92 +0,0 @@ -package org.zalando.nakadi.client - -import java.net.URI -import java.util -import java.util.Optional -import java.util.concurrent.TimeUnit -import javax.net.ssl.SSLContext -import akka.actor.ActorSystem -import akka.http.scaladsl.Http.OutgoingConnection -import akka.http.scaladsl.model.{HttpResponse, HttpRequest} -import akka.http.scaladsl.{Http, HttpsContext} -import akka.stream.scaladsl.Flow -import com.google.common.collect.Iterators - -import scala.collection.JavaConversions -import scala.concurrent.{Future, Await} -import scala.concurrent.duration.Duration -import collection.JavaConversions._ - -object Utils { - - private def extract[T](either: Either[String, T]): T = either match { - case Left(error) => throw new RuntimeException(error) - case Right(t) => t - } - - - def convert[T](x: scala.concurrent.Future[Either[String, T]]): java.util.concurrent.Future[T] = - new MFuture[Either[String, T], T](x, a => extract(a)) - - - def convertSimple[T](x: scala.concurrent.Future[T]): java.util.concurrent.Future[T] = new MFuture[T, T](x, a => a) - - - def convertForMapRetrieval[A](x: scala.concurrent.Future[Either[String, Map[A, Any]]]): java.util.concurrent.Future[java.util.Map[A, Any]] = { - - def map(m: java.util.Map[A, Any]): java.util.Map[A, Any] = { - val newMap = new util.HashMap[A, Any]() - val iter = m.entrySet().iterator(); - while (iter.hasNext) { - val e = iter.next() - val v = e.getValue - if(! v.isInstanceOf[String]) - newMap.put(e.getKey, map(mapAsJavaMap(v.asInstanceOf[Map[A, Any]])).asInstanceOf[Any]) - else - newMap.put(e.getKey, v) - } - newMap - } - - new MFuture[Either[String, Map[A, Any]], java.util.Map[A, Any]](x, a => map(mapAsJavaMap(extract(a)))) - } - - - - def convertForListRetrieval[A](x: scala.concurrent.Future[Either[String, List[A]]]): java.util.concurrent.Future[java.util.List[A]] = - new MFuture[Either[String, List[A]], java.util.List[A]](x, a => { - val l: List[A] = extract(a) - new util.ArrayList[A](l) - }) - - - def convertToOptional[A](option: Option[A]): Optional[A] = - option match { - case Some(v) => Optional.of(v) - case None => Optional.empty() - } - - - def outgoingHttpConnection(endpoint: URI, port: Int, securedConnection: Boolean)(implicit system: ActorSystem): Flow[HttpRequest, HttpResponse, Future[OutgoingConnection]] = { - val http = Http(system) - if(securedConnection) { - http.setDefaultClientHttpsContext(HttpsContext(SSLContext.getDefault)) - http.outgoingConnectionTls(endpoint.toString, port) - } - - else http.outgoingConnection(endpoint.toString, port) - } -} - - -private class MFuture[A, B](f: scala.concurrent.Future[A], converter: A => B) extends java.util.concurrent.Future[B]{ - override def isCancelled: Boolean = throw new UnsupportedOperationException - - override def get(): B = converter.apply(Await.result(f, Duration.Inf)) - - override def get(timeout: Long, unit: TimeUnit): B = converter.apply(Await.result(f, Duration.create(timeout, unit))) - - override def cancel(mayInterruptIfRunning: Boolean): Boolean = throw new UnsupportedOperationException - - override def isDone: Boolean = f.isCompleted -} diff --git a/src/main/scala/org/zalando/nakadi/client/actor/KlientSupervisor.scala b/src/main/scala/org/zalando/nakadi/client/actor/KlientSupervisor.scala deleted file mode 100644 index 70e3fe2..0000000 --- a/src/main/scala/org/zalando/nakadi/client/actor/KlientSupervisor.scala +++ /dev/null @@ -1,125 +0,0 @@ -package org.zalando.nakadi.client.actor - -import java.net.URI -import java.util.concurrent.TimeUnit - -import akka.actor._ -import akka.util.Timeout -import com.fasterxml.jackson.databind.ObjectMapper -import org.zalando.nakadi.client.Klient.KlientException -import org.zalando.nakadi.client.actor.PartitionReceiver._ -import org.zalando.nakadi.client.{Conf, Klient, Listener, ListenParameters} -import scala.concurrent.ExecutionContext.Implicits.global -import scala.concurrent.Future -import scala.util.{Failure, Success} - -object KlientSupervisor{ - private val MAX_NR_OF_RETRIES = Conf.supervisor.maxNrOfRetries // 100 - private val WITHIN_TIME_RANGE = Conf.supervisor.withinTimeRange // 5 minutes - - private val RESOLVE_ACTOR_TIMEOUT = Conf.supervisor.resolveActorTimeout // Timeout(1L, TimeUnit.SECONDS) - - case class NewSubscription(topic: String, - partitionId: String, - parameters: ListenParameters, - autoReconnect: Boolean, - listener: Listener) - - - case class Unsubscription(topic: String, listener: Listener) - - def props(endpoint: URI, port: Int, securedConnection: Boolean, tokenProvider: () => String, objectMapper: ObjectMapper) = - Props(new KlientSupervisor(endpoint, port, securedConnection, tokenProvider, objectMapper)) -} - - -class KlientSupervisor private (val endpoint: URI, val port: Int, val securedConnection: Boolean, val tokenProvider: () => String, val objectMapper: ObjectMapper) - extends Actor with ActorLogging{ - - import akka.actor.SupervisorStrategy._ - import scala.concurrent.duration._ - import scala.language.postfixOps - import org.zalando.nakadi.client.actor.KlientSupervisor._ - import KlientSupervisor._ - - var listenerMap: Map[String, ActorRef] = Map() - - override val supervisorStrategy = AllForOneStrategy(maxNrOfRetries = MAX_NR_OF_RETRIES, withinTimeRange = WITHIN_TIME_RANGE) { - case e: ArithmeticException => Resume - case e: NullPointerException => Restart - case e: IllegalArgumentException => Stop - case e: Exception => Resume - } - - - override def receive: Receive = { - case NewSubscription(topic, partitionId, parameters, autoReconnect, listener) => - subscribe(topic, - partitionId, - parameters, - autoReconnect, - listener, - (l: Listener)=> context.actorOf(ListenerActor.props(l))) - - case Terminated(actor) => - listenerMap = listenerMap.filterNot(_._2 == actor) - } - - - def subscribe(topic: String, partitionId: String, parameters: ListenParameters, autoReconnect: Boolean, listener: Listener, createListenerActor: (Listener) => ActorRef): Unit = { - if(autoReconnect) { - resolveActor("partition-" + partitionId).onComplete(_ match { - case Success(receiverActor) => - receiverActor ! NewListener(listener.id, createListenerActor(listener)) - case Failure(e: ActorNotFound) => - subscribeToPartition(topic, partitionId, parameters, autoReconnect, listener, Some(s"partition-$partitionId"),createListenerActor) - case Failure(e: Throwable) => throw new KlientException(e.getMessage, e) - }) - } - else subscribeToPartition(topic, partitionId, parameters, autoReconnect, listener, None, createListenerActor) - } - - - def asListenerPath(topic: String, listener: Listener): String = s"$topic-${listener.id}" - - - def subscribeToPartition(topic: String, partitionId: String, parameters: ListenParameters, autoReconnect: Boolean, listener: Listener, actorNameOption: Option[String], createListenerActor: (Listener) => ActorRef) = { - val receiverActor = actorNameOption match { - case Some(actorName) => context.actorOf(PartitionReceiver.props(endpoint, - port, - securedConnection, - topic, - partitionId, - parameters, - tokenProvider, - autoReconnect, - objectMapper), - actorName) - case None => context.actorOf(PartitionReceiver.props( endpoint, - port, - securedConnection, - topic, - partitionId, - parameters, - tokenProvider, - autoReconnect, - objectMapper)) - } - - val lActor = listenerMap.get(listener.id) match { - case Some(listenerActor) => listenerActor - case None => val listenerActor = createListenerActor(listener) - context.watch(listenerActor) - listenerMap += ((listener.id, listenerActor)) - listenerActor - } - - receiverActor ! NewListener(listener.id, lActor) - } - - - def resolveActor(actorSelectionPath: String): Future[ActorRef] = { - val receiverSelection = context.actorSelection(actorSelectionPath) - receiverSelection.resolveOne()( RESOLVE_ACTOR_TIMEOUT ) - } -} diff --git a/src/main/scala/org/zalando/nakadi/client/actor/ListenerActor.scala b/src/main/scala/org/zalando/nakadi/client/actor/ListenerActor.scala deleted file mode 100644 index c9ad4cb..0000000 --- a/src/main/scala/org/zalando/nakadi/client/actor/ListenerActor.scala +++ /dev/null @@ -1,42 +0,0 @@ -package org.zalando.nakadi.client.actor - -import akka.actor.{PoisonPill, Props, ActorLogging, Actor} -import org.zalando.nakadi.client.actor.KlientSupervisor._ -import org.zalando.nakadi.client.actor.PartitionReceiver._ -import org.zalando.nakadi.client.{ Cursor, Event, Listener} - - -object ListenerActor{ - case class ListenerSubscription(topic: String, partitionId: String) - - def props(listener: Listener) = Props(new ListenerActor(listener)) -} - -class ListenerActor protected (val listener: Listener) extends Actor with ActorLogging{ - import ListenerActor._ - - context.system.eventStream.subscribe(self, classOf[Unsubscription]) - - var topicSubscriptions: Set[String] = Set() - - override def receive: Receive = { - case (topic: String, partition: String, cursor: Cursor, event: Event) => - log.debug(s"received [topic=$topic, partition=$partition, cursor=$cursor, event=$event]") - listener.onReceive(topic, partition, cursor, event) - case ListenerSubscription(topic: String, partition: String) => - topicSubscriptions += topic - log.info("[listener={}] subscribed to [topic={}, partition={}]", listener.id, topic, partition) - case ConnectionOpened(topic, partition) => listener.onConnectionOpened(topic, partition) - case ConnectionClosed(topic, partition, lastCursor) => listener.onConnectionClosed(topic, partition, lastCursor) - case ConnectionFailed(topic, partition, status, error) => listener.onConnectionFailed(topic, partition, status, error) - case Unsubscription(topic, _listener) => if(topicSubscriptions.contains(topic) && _listener.id == listener.id) { - log.info("[listener={}] is unsubscribing [topic={}]", _listener.id, topic) - topicSubscriptions -= topic - if(topicSubscriptions.isEmpty) { - log.info("[listener={}] is not subscribed to any topic -> shutting down", _listener.id) - self ! PoisonPill.getInstance - } - } - } - -} diff --git a/src/main/scala/org/zalando/nakadi/client/actor/PartitionReceiver.scala b/src/main/scala/org/zalando/nakadi/client/actor/PartitionReceiver.scala deleted file mode 100644 index 3e0ed06..0000000 --- a/src/main/scala/org/zalando/nakadi/client/actor/PartitionReceiver.scala +++ /dev/null @@ -1,203 +0,0 @@ -package org.zalando.nakadi.client.actor - -import java.io.ByteArrayOutputStream -import java.net.URI - -import akka.actor._ -import akka.http.scaladsl.model.MediaTypes._ -import akka.http.scaladsl.model.headers.OAuth2BearerToken -import akka.http.scaladsl.model.{MediaRange, headers, HttpResponse, HttpRequest} -import akka.stream.ActorMaterializer -import akka.stream.scaladsl.{Sink, Source} -import com.fasterxml.jackson.databind.ObjectMapper -import org.zalando.nakadi.client -import org.zalando.nakadi.client.Utils.outgoingHttpConnection -import org.zalando.nakadi.client.actor.KlientSupervisor._ -import org.zalando.nakadi.client.actor.ListenerActor._ -import org.zalando.nakadi.client.{Conf, Cursor, SimpleStreamEvent, ListenParameters} -import scala.concurrent.Await -import scala.concurrent.ExecutionContext.Implicits.global -import scala.concurrent.duration.Duration - - -object PartitionReceiver{ - - val NO_LISTENER_RECONNECT_DELAY_IN_S: Int = Conf.noListenerReconnectDelay.toSeconds.toInt - val POLL_PARALLELISM: Int = Conf.pollParallelism - val RECEIVE_BUFFER_SIZE: Long = Conf.receiveBufferSize.toBytes // 1024 - - /** - * Triggers new event polling request - */ - private object Init - - /** - * Shutdown actor without PoisonPill. The reason is that PoisonPill shuts down - * the actor immediately without having all messages in the queue processed. - */ - private object Shutdown - - case class NewListener(listenerId: String, listener: ActorRef) - case class ConnectionOpened(topic: String, partition: String) - case class ConnectionFailed(topic: String, partition: String, status: Int, error: String) - case class ConnectionClosed(topic: String, partition: String, lastCursor: Option[Cursor]) - - - def props(endpoint: URI, - port: Int, - securedConnection: Boolean, - topic: String, - partitionId: String, - parameters: ListenParameters, - tokenProvider: () => String, - automaticReconnect: Boolean, - objectMapper: ObjectMapper) = - Props(new PartitionReceiver(endpoint, port, securedConnection, topic, partitionId, parameters, tokenProvider, automaticReconnect, objectMapper) ) -} - -class PartitionReceiver private (val endpoint: URI, - val port: Int, - val securedConnection: Boolean, - val topic: String, - val partitionId: String, - val parameters: ListenParameters, - val tokenProvider: () => String, - val automaticReconnect: Boolean, - val objectMapper: ObjectMapper) extends Actor with ActorLogging -{ - import PartitionReceiver._ - - var listeners: Map[String, ActorRef] = Map() - - var lastCursor: Option[Cursor] = None - implicit val materializer = ActorMaterializer() - - context.system.eventStream.subscribe(self, classOf[Unsubscription]) - - override def preStart() = self ! Init - - override def receive: Receive = { - case Init => lastCursor match { - case None => listen(parameters) - case Some(cursor) => listen(ListenParameters(Option(cursor.offset), - parameters.batchLimit, - parameters.batchFlushTimeoutInSeconds, - parameters.streamLimit)) - } - case NewListener(listenerId, listener) => - context.watch(listener) - listener ! ListenerSubscription(topic, partitionId) - listeners = listeners + ((listenerId, listener)) - case streamEvent: SimpleStreamEvent => streamEvent.events.foreach{event => - lastCursor = Some(streamEvent.cursor) - listeners.values.foreach(listener => listener ! Tuple4(topic, partitionId, streamEvent.cursor, event)) - } - case Unsubscription(_topic, _listener) => if(_topic == topic) listeners -= _listener.id - case Terminated(actor) => listeners = listeners.filterNot(_._2 == actor) - case Shutdown => context.stop(self) - } - - - // TODO check earlier ListenParameters - def listen(parameters: ListenParameters) = { - val request = HttpRequest(uri = buildRequestUri(parameters)) - .withHeaders(headers.Authorization(OAuth2BearerToken(tokenProvider.apply())), - headers.Accept(MediaRange(`application/json`))) - - if(listeners.isEmpty) { - log.info("no listeners registered -> not establishing connection to Nakadi") - reconnectIfActivated(NO_LISTENER_RECONNECT_DELAY_IN_S) - } - else { - log.debug("listening via [request={}]", request) - Source - .single(request) - .via(outgoingHttpConnection(endpoint, port, securedConnection)(context.system)) - .runWith(Sink.foreachParallel(POLL_PARALLELISM)(response => - response.status match { - case status if status.isSuccess() => - listeners.values.foreach(_ ! ConnectionOpened(topic, partitionId)) - Await.ready(consumeStream(response), Duration.Inf)// TODO is there a better way? - case status => - listeners.values.foreach(_ ! ConnectionFailed(topic, partitionId, response.status.intValue(), response.entity.toString)) - reconnectIfActivated() - })) - .onComplete(_ => { - log.info("connection closed to [topic={}, partition={}]", topic, partitionId) - listeners.values.foreach(_ ! ConnectionClosed(topic, partitionId, lastCursor)) - - if(automaticReconnect) reconnectIfActivated() - else { - log.info("stream to [topic={}, partition={}] has been closed and [automaticReconnect={}] -> shutting down", - topic, partitionId, automaticReconnect) - self ! Shutdown - } - }) - } - } - - def reconnectIfActivated(numerOfSeconds: Int = 1) = { - if (automaticReconnect) { - log.info("[automaticReconnect={}] -> reconnecting", automaticReconnect) - import scala.concurrent.duration._ - import scala.language.postfixOps - context.system.scheduler.scheduleOnce(numerOfSeconds seconds, self, Init) - } - else log.info("[automaticReconnect={}] -> no reconnect", automaticReconnect) - } - - def buildRequestUri(parameters: ListenParameters) = - String.format(client.URI_EVENT_LISTENING, - topic, - partitionId, - parameters.startOffset match { - case Some(offset) => s"start_from=$offset" - case None => "" - }, - parameters.batchLimit.getOrElse(throw new IllegalStateException("no batchLimit set")).toString, - parameters.batchFlushTimeoutInSeconds.getOrElse(throw new IllegalStateException("no batchFlushTimeoutInSeconds set")).toString, - parameters.streamLimit.getOrElse(throw new IllegalStateException("no streamLimit set")).toString) - - - def consumeStream(response: HttpResponse) = { - /* - * We can not simply rely on EOL for the end of each JSON object as - * Nakadi puts the in the middle of the response body sometimes. // <-- "puts the"... what? in the middle AKa120216 - * For this reason, we need to apply some very simple JSON parsing logic. - * - * See also http://json.org/ for string parsing semantics - */ - var depth: Int = 0 - var hasOpenString: Boolean = false - val bout = new ByteArrayOutputStream(RECEIVE_BUFFER_SIZE.toInt) - - response.entity.dataBytes.runForeach { - byteString => { - byteString.foreach(byteItem => { - bout.write(byteItem.asInstanceOf[Int]) - - if (byteItem == '"') hasOpenString = !hasOpenString - else if (!hasOpenString && byteItem == '{') depth += 1 - else if (!hasOpenString && byteItem == '}') { - depth -= 1 - - if (depth == 0 && bout.size != 0) { - val streamEvent = objectMapper.readValue(bout.toByteArray, classOf[SimpleStreamEvent]) - - if (Option(streamEvent.events).isDefined && streamEvent.events.nonEmpty){ - log.info("received non-empty [streamEvent={}]", streamEvent) - self ! streamEvent - } - else log.debug(s"received empty [streamingEvent={}] --> ignored", streamEvent) - - bout.reset() - } - } - }) - } - } - } - - - override def toString = s"PartitionReceiver(listeners=$listeners, lastCursor=$lastCursor, endpoint=$endpoint, topic=$topic, partitionId=$partitionId, parameters=$parameters, automaticReconnect=$automaticReconnect)" -} diff --git a/src/main/scala/org/zalando/nakadi/client/package.scala b/src/main/scala/org/zalando/nakadi/client/package.scala index 1011448..69126c7 100644 --- a/src/main/scala/org/zalando/nakadi/client/package.scala +++ b/src/main/scala/org/zalando/nakadi/client/package.scala @@ -1,16 +1,22 @@ package org.zalando.nakadi - package object client { - - // Benjamin on PR21 comments (11-Feb-2016): - // "I would not make the URIs configurable as their are fixed by the REST API. However, I would change the default values" - // + + val URI_METRICS = "/metrics" - val URI_TOPICS = "/topics" - val URI_EVENT_POST = "/topics/%s/events" - val URI_PARTITIONS = "/topics/%s/partitions" - val URI_PARTITION = "/topics/%s/partitions/%s" - val URI_EVENTS_ON_PARTITION = "/topics/%s/partitions/%s/events" - val URI_EVENT_LISTENING = "/topics/%s/partitions/%s/events?%s&batch_limit=%s&batch_flush_timeout=%s&stream_limit=%s" -} + + /*Events*/ + val URI_EVENT_TYPES = "/event-types" + val URI_EVENT_TYPE_BY_NAME = "/event-types/%s" + val URI_EVENTS_OF_EVENT_TYPE = "/event-types/%s/events" + + /*Partitions*/ + val URI_PARTITIONS_OF_EVENT_TYPE = "/event-types/%s/partitions" + val URI_PARTITION_BY_NAME = "/event-types/%s/partitions/%s" + + /*Strategies*/ + val URI_VALIDATION_STRATEGIES = "/registry/validation-strategies" + val URI_ENRICHMENT_STRATEGIES = "/registry/enrichment-strategies" + val URI_PARTITIONING_STRATEGIES = "/registry/partitioning-strategies" + +} \ No newline at end of file diff --git a/src/main/scala/org/zalando/nakadi/client/publisher/EventPublisher.scala b/src/main/scala/org/zalando/nakadi/client/publisher/EventPublisher.scala new file mode 100644 index 0000000..59025a9 --- /dev/null +++ b/src/main/scala/org/zalando/nakadi/client/publisher/EventPublisher.scala @@ -0,0 +1,43 @@ +package org.zalando.nakadi.client.publisher + +import java.math.BigInteger + +import akka.actor.ActorLogging +import akka.stream.actor.ActorPublisher +import akka.stream.actor.ActorPublisherMessage.{ Cancel, Request } + +class EventPublisher extends ActorPublisher[BigInteger] with ActorLogging { + + var prev = BigInteger.ZERO + var curr = BigInteger.ZERO + + def receive = { + case Request(cnt) => // 2 + log.debug("[EventPublisher] Received Request ({}) from Subscriber", cnt) + sendFibs() + case Cancel => // 3 + log.info("[EventPublisher] Cancel Message Received -- Stopping") + context.stop(self) + case _ => + log.info("[EventPublisher] Unknown Message") + } + + def sendFibs() { + while (isActive && totalDemand > 0) { // 4 + onNext(nextFib()) + } + } + + def nextFib(): BigInteger = { + if (curr == BigInteger.ZERO) { + curr = BigInteger.ONE + } else { + val tmp = prev.add(curr) + prev = curr + curr = tmp + } + curr + } + + +} \ No newline at end of file diff --git a/src/main/scala/org/zalando/nakadi/client/publisher/EventSubscriber.scala b/src/main/scala/org/zalando/nakadi/client/publisher/EventSubscriber.scala new file mode 100644 index 0000000..ba7480f --- /dev/null +++ b/src/main/scala/org/zalando/nakadi/client/publisher/EventSubscriber.scala @@ -0,0 +1,29 @@ +package org.zalando.nakadi.client.publisher + +import akka.stream.actor.WatermarkRequestStrategy +import java.math.BigInteger +import akka.actor.ActorLogging +import akka.stream.actor.ActorSubscriber +import akka.stream.actor.ActorSubscriberMessage.OnError +import akka.stream.actor.ActorSubscriberMessage.OnNext +import akka.stream.actor.ActorSubscriberMessage.OnComplete + +class SlowSubscriber(delay: Long) extends ActorSubscriber with ActorLogging { // 1 + val requestStrategy = WatermarkRequestStrategy(50) // 2 + + def receive = { + case OnNext(fib: BigInteger) => // 3 + log.debug("[SlowSubscriber] Received Fibonacci Number: {}", fib) + Thread.sleep(delay) + case OnError(err: Exception) => // 4 + log.error(err, "[SlowSubscriber] Receieved Exception in Fibonacci Stream") + context.stop(self) + case OnComplete => // 5 + log.info("[SlowSubscriber] Fibonacci Stream Completed!") + context.stop(self) + case _ => + log.info("[SlowSubscriber] Unknown!") + } + + +} diff --git a/src/main/scala/org/zalando/nakadi/client/publisher/EventTransformer.scala b/src/main/scala/org/zalando/nakadi/client/publisher/EventTransformer.scala new file mode 100644 index 0000000..0450eb1 --- /dev/null +++ b/src/main/scala/org/zalando/nakadi/client/publisher/EventTransformer.scala @@ -0,0 +1,49 @@ +package org.zalando.nakadi.client.publisher + +import java.math.BigInteger +import akka.stream.actor.ActorSubscriber +import akka.stream.actor.MaxInFlightRequestStrategy +import akka.stream.actor.ActorPublisherMessage.Request +import akka.stream.actor.ActorSubscriberMessage.OnError +import akka.stream.actor.ActorSubscriberMessage.OnComplete +import akka.stream.actor.ActorSubscriberMessage.OnNext +import akka.stream.actor.ActorPublisher +import scala.collection.mutable.{ Queue => MQueue } +import akka.stream.actor.ActorPublisherMessage.Cancel + +class EventTransformer extends ActorSubscriber with ActorPublisher[BigInteger] { // 1 + val dos = BigInteger.valueOf(2L) + val doubledQueue = MQueue[BigInteger]() // 2 + + def receive = { + case OnNext(biggie: BigInteger) => // 3 + doubledQueue.enqueue(biggie.multiply(dos)) + sendDoubled() + case OnError(err: Exception) => // 4 + onError(err) + context.stop(self) + case OnComplete => // 5 + onComplete() + context.stop(self) + case Request(cnt) => // 6 + sendDoubled() + case Cancel => // 7 + cancel() + context.stop(self) + case _ => + } + + def sendDoubled() { + while (isActive && totalDemand > 0 && !doubledQueue.isEmpty) { // 8 + onNext(doubledQueue.dequeue()) + } + } + + + val requestStrategy = new MaxInFlightRequestStrategy(50) { // 9 + override def inFlightInternally = doubledQueue.size + } +} + + + diff --git a/src/main/scala/org/zalando/nakadi/client/publisher/Main.scala b/src/main/scala/org/zalando/nakadi/client/publisher/Main.scala new file mode 100644 index 0000000..49b5e5c --- /dev/null +++ b/src/main/scala/org/zalando/nakadi/client/publisher/Main.scala @@ -0,0 +1,20 @@ +package org.zalando.nakadi.client.publisher + +import java.math.BigInteger +import akka.actor.ActorSystem +import akka.stream.actor.ActorSubscriber +import akka.stream.actor.ActorPublisher +import akka.actor.Props + +//object MainApp extends App { +// implicit val system = ActorSystem("test") +// +// println("Creating EventPublisher!") +// val pubRef = system.actorOf(Props[EventPublisher]) +// val publisher = ActorPublisher[BigInteger](pubRef) // => org.reactivestreams.Publisher +// +// println("Creating SlowSubscriber!") +// val subRef = system.actorOf(Props(new SlowSubscriber(500L))) // 500 ms delay +// val subscriber = ActorSubscriber[BigInteger](subRef) // => org.reactivestreams.Subscriber +// publisher.subscribe(subscriber) +//} \ No newline at end of file diff --git a/src/test/java/org/zalando/nakadi/client/JEventTest.java b/src/test/java/org/zalando/nakadi/client/JEventTest.java deleted file mode 100644 index c46711a..0000000 --- a/src/test/java/org/zalando/nakadi/client/JEventTest.java +++ /dev/null @@ -1,74 +0,0 @@ -package org.zalando.nakadi.client; - -import com.google.common.collect.Maps; -import org.junit.Before; -import org.junit.Test; -import org.zalando.nakadi.client.utils.TestUtils; - -import java.util.HashMap; -import java.util.Map; - -import static org.junit.Assert.assertEquals; - -public class JEventTest { - private JListener.JEvent event; - - @Before - public void setup() throws Exception { - final TestUtils utils = new TestUtils(); - - final Map meta = Maps.newHashMap(); - final HashMap metaTmp = Maps.newHashMap(); - metaTmp.put("b", "c"); - meta.put("a", utils.toScalaMap(metaTmp)); - - final Map body = Maps.newHashMap(); - body.put("a", "v1"); - final HashMap bodyTmp = Maps.newHashMap(); - bodyTmp.put("b", "v2"); - body.put("c", utils.toScalaMap(bodyTmp)); - - - final Event actualEvent = new Event("eventType", - "orderingKey", - utils.toScalaMap(meta), - utils.toScalaMap(body)); - - event = new JListener.JEvent(actualEvent); - } - - - @Test - public void testEventType() throws Exception { - assertEquals("wrong event type", "eventType", event.getEventType()); - } - - - @Test - public void testOrderingKey() throws Exception { - assertEquals("wrong ordering key", "orderingKey", event.getOrderingKey()); - } - - - @Test - public void testMeta() throws Exception { - final Map meta = Maps.newHashMap(); - final HashMap metaTmp = Maps.newHashMap(); - metaTmp.put("b", "c"); - meta.put("a", metaTmp); - - assertEquals("wrong meta data", meta, event.getMetaData()); - } - - - @Test - public void testBody() throws Exception { - final Map body = Maps.newHashMap(); - body.put("a", "v1"); - final HashMap bodyTmp = Maps.newHashMap(); - bodyTmp.put("b", "v2"); - body.put("c", bodyTmp); - - assertEquals("wrong body", body, event.getBody()); - } -} diff --git a/src/test/java/org/zalando/nakadi/client/JListenerWrapperTest.java b/src/test/java/org/zalando/nakadi/client/JListenerWrapperTest.java deleted file mode 100644 index 02ac058..0000000 --- a/src/test/java/org/zalando/nakadi/client/JListenerWrapperTest.java +++ /dev/null @@ -1,143 +0,0 @@ -package org.zalando.nakadi.client; - - -import com.google.common.collect.Maps; -import org.junit.Before; -import org.junit.Test; -import org.zalando.nakadi.client.utils.TestUtils; -import scala.Some; - -import java.util.Optional; - -import static org.junit.Assert.assertEquals; - -public class JListenerWrapperTest { - - private JListenerWrapper wrapper; - private MyListener listener; - - @Before - public void setup() throws Exception { - listener = new MyListener(); - wrapper = new JListenerWrapper(listener); - } - - - @Test - public void testId() throws Exception { - assertEquals(MyListener.ID, wrapper.id()); - } - - - @Test - public void testOnReceive() throws Exception { - final TestUtils utils = new TestUtils(); - - final String topic = "topic"; - final String partition = "partition"; - final Cursor cursor = new Cursor("partition", "offset"); - final Event event = new Event("eventType", - "orderingKey", - utils.toScalaMap(Maps.newHashMap()), - utils.toScalaMap(Maps.newHashMap())); - - wrapper.onReceive(topic, partition, cursor, event); - - assertEquals("wrong topic", topic, listener.topic); - assertEquals("wrong partition", partition, listener.partition); - assertEquals("wrong cursor", cursor, listener.cursor); - assertEquals(event.eventType(), listener.event.getEventType()); - assertEquals(event.orderingKey(), listener.event.getOrderingKey()); - } - - - @Test - public void testOnConnectionOpened() throws Exception { - final String topic = "topic"; - final String partition = "partition"; - - wrapper.onConnectionOpened(topic, partition); - - assertEquals("wrong topic", topic, listener.topic); - assertEquals("wrong partition", partition, listener.partition); - } - - - @Test - public void testOnConnectionFailed() throws Exception { - final String topic = "topic"; - final String partition = "partition"; - final int status = 200; - final String error = "error"; - - wrapper.onConnectionFailed(topic, partition, status, error); - - assertEquals("wrong topic", topic, listener.topic); - assertEquals("wrong partition", partition, listener.partition); - assertEquals("wrong status", status, listener.status); - assertEquals("wrong error", error, listener.error); - } - - - @Test - public void testOnConnectionClosed() throws Exception { - final String topic = "topic"; - final String partition = "partition"; - final Cursor cursor = new Cursor("partition", "offset"); - final Optional lastCursor = Optional.of(new Cursor("partition", "offset")); - - wrapper.onConnectionClosed(topic, partition, new Some<>(cursor)); - - assertEquals("wrong topic", topic, listener.topic); - assertEquals("wrong partition", partition, listener.partition); - assertEquals("wrong cursor", lastCursor, listener.lastCursor); - } - - - private static final class MyListener implements JListener{ - - String topic; - String partition; - Cursor cursor; - JListener.JEvent event; - int status; - String error; - Optional lastCursor; - - public static final String ID = "ID"; - - @Override - public String id() { - return ID; - } - - @Override - public void onReceive(String topic, String partition, Cursor cursor, JEvent event) { - this.topic = topic; - this.partition = partition; - this.cursor = cursor; - this.event = event; - } - - @Override - public void onConnectionOpened(String topic, String partition) { - this.topic = topic; - this.partition = partition; - } - - @Override - public void onConnectionFailed(String topic, String partition, int status, String error) { - this.topic = topic; - this.partition = partition; - this.status = status; - this.error = error; - } - - @Override - public void onConnectionClosed(String topic, String partition, Optional lastCursor) { - this.topic = topic; - this.partition = partition; - this.lastCursor = lastCursor; - } - } -} diff --git a/src/test/java/org/zalando/nakadi/client/JavaClientImplTest.java b/src/test/java/org/zalando/nakadi/client/JavaClientImplTest.java deleted file mode 100644 index 375d0c1..0000000 --- a/src/test/java/org/zalando/nakadi/client/JavaClientImplTest.java +++ /dev/null @@ -1,71 +0,0 @@ -package org.zalando.nakadi.client; - - -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.zalando.nakadi.client.utils.KlientMock; - -import java.util.List; -import java.util.Map; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -public class JavaClientImplTest { - - private Client client; - - @Before - public void setup() throws Exception { - client = new JavaClientImpl(new KlientMock(KlientMock.defaultMockReturnValues())); - } - - - @Test - public void testGetMetrics() throws Exception { - final Future> f = client.getMetrics(); - final Map a = f.get(); - assertTrue("missing key", a.containsKey("a")); - - final Map b = (Map) a.get("a"); - assertTrue("missing key in value map", b.containsKey("b")); - - final Map same = f.get(1L, TimeUnit.SECONDS); - Assert.assertEquals("metrics maps are not equal", a, same); - } - - @Test - public void testGetPartition() throws Exception { - final Future f = client.getPartition("topic", "partitionId"); - final TopicPartition partition = f.get(); - assertEquals("partitionid", partition.partitionId()); - assertEquals("oldestAvailableOffset", partition.oldestAvailableOffset()); - assertEquals("newestAvailableOffset", partition.newestAvailableOffset()); - assertEquals("partitions are not equal", partition, f.get(1L, TimeUnit.SECONDS)); - } - - @Test - public void testGetPartitions() throws Exception { - final Future> partitionsFuture = client.getPartitions("topic"); - final List partitions = partitionsFuture.get(); - assertEquals("illegal partition list size", 1, partitions.size()); - final TopicPartition p = partitions.get(0); - assertEquals("partitionid", p.partitionId()); - assertEquals("oldestAvailableOffset", p.oldestAvailableOffset()); - assertEquals("newestAvailableOffset", p.newestAvailableOffset()); - assertEquals("partition lists are not equal", partitions, partitionsFuture.get(1L, TimeUnit.SECONDS)); - } - - @Test - public void testGetTopics() throws Exception { - final Future> f = client.getTopics(); - final List topics = f.get(); - assertEquals("illegal topic list size", 1, topics.size()); - final Topic topic = topics.get(0); - assertEquals("wrong topic name", "topic",topic.name()); - assertEquals("topic lists are not equal", topics, f.get(1L, TimeUnit.SECONDS)); - } -} diff --git a/src/test/java/org/zalando/nakadi/client/utils/Main.java b/src/test/java/org/zalando/nakadi/client/utils/Main.java deleted file mode 100644 index a02f234..0000000 --- a/src/test/java/org/zalando/nakadi/client/utils/Main.java +++ /dev/null @@ -1,68 +0,0 @@ -package org.zalando.nakadi.client.utils; - - -import com.google.common.collect.Maps; -import org.zalando.nakadi.client.*; - -import java.net.URI; -import java.util.HashMap; -import java.util.Optional; -import java.util.concurrent.Future; - - -public class Main { - - - public static void main(final String[] args) throws Exception { - - final Client client = new KlientBuilder() - .withEndpoint(new URI("localhost")) - .withPort(8080) // test config? - .withSecuredConnection(false) - .withJavaTokenProvider(() -> "b099a178-acf7-467b-b106-4c0656285153") - .buildJavaClient(); - - System.out.println("-METRICS--> " + client.getMetrics().get()); - - - final HashMap meta = Maps.newHashMap(); - meta.put("id", "1234567890"); - - final Future f = client.postEvent("test", new Event("eventType", "orderingKey", meta, "{}")); - - f.get(); - - client.subscribeToTopic("test", ListenParametersUtils.defaultInstance(), new JListenerWrapper(new MyListener()), true); - - - Thread.sleep(Long.MAX_VALUE); - } - - private static final class MyListener implements JListener{ - - @Override - public String id() { - return getClass().getName(); - } - - @Override - public void onReceive(String topic, String partition, Cursor cursor, JEvent event) { - System.out.printf("onReceive -> " + event); - } - - @Override - public void onConnectionOpened(String topic, String partition) { - System.out.println("onConnectionOpened " + topic + " " + partition); - } - - @Override - public void onConnectionFailed(String topic, String partition, int status, String error) { - System.out.println("onConnectionFailed " + topic + " " + partition + " " + status + " " + error); - } - - @Override - public void onConnectionClosed(String topic, String partition, Optional lastCursor) { - System.out.println("onConnectionClosed " + topic + " " + partition + " " + lastCursor); - } - } -} diff --git a/src/test/scala/org/zalando/nakadi/client/KlientBuilderSpec.scala b/src/test/scala/org/zalando/nakadi/client/KlientBuilderSpec.scala deleted file mode 100644 index 98ae710..0000000 --- a/src/test/scala/org/zalando/nakadi/client/KlientBuilderSpec.scala +++ /dev/null @@ -1,120 +0,0 @@ -package org.zalando.nakadi.client - -import java.net.URI - -import com.fasterxml.jackson.databind.ObjectMapper -import org.scalatest.{WordSpec, Matchers} - -class KlientBuilderSpec extends WordSpec with Matchers { - - "A Klient builder" must { - - "build a Klient instance, if everything is set up properly" in { - KlientBuilder() - .withEndpoint(new URI("localhost:8080")) - .withTokenProvider(() => "my-token") - .build() - } - - "build a Java client instance, if everything is set up properly" in { - KlientBuilder() - .withEndpoint(new URI("localhost:8080")) - .withTokenProvider(() => "my-token") - .buildJavaClient() - } - - "throw an exception, if not all mandatory arguments are provided" in { - an [IllegalStateException] must be thrownBy { - KlientBuilder() - .withTokenProvider(() => "my-token") - .build() - } - an [IllegalStateException] must be thrownBy { - KlientBuilder() - .withEndpoint(new URI("localhost:8080")) - .build() - } - } - - "use the specified ObjectMapper" in { - val objectMapper = new ObjectMapper() - - val klient: KlientImpl = KlientBuilder() - .withEndpoint(new URI("localhost:8080")) - .withTokenProvider(() => "my-token") - .withObjectMapper(Some(objectMapper)) - .build().asInstanceOf[KlientImpl] - - klient.objectMapper == objectMapper should be(true) - } - - "create a new KlientBuilder instance containing all original settings together with changed endpoint" in { - val initialBuilder = KlientBuilder() - - val newURI = new URI("www.zalando.de") - val nextBuilder = initialBuilder.withEndpoint(newURI) - - nextBuilder.endpoint should be(newURI) - compareKlientBuilderInstance(initialBuilder, nextBuilder, compareEndpoint = false) - } - - "create a new KlientBuilder instance containing all original settings together with changed port" in { - val initialBuilder = KlientBuilder() - - val newPort = 9999 - val nextBuilder = initialBuilder.withPort(newPort) - - nextBuilder.port should be (newPort) - compareKlientBuilderInstance(initialBuilder, nextBuilder, comparePort = false) - } - - "create a new KlientBuilder instance containing all original settings together with changed ObjectMapper" in { - val initialBuilder = KlientBuilder() - - val newObjectMapper = Some(new ObjectMapper()) - val nextBuilder = initialBuilder.withObjectMapper(newObjectMapper) - nextBuilder.objectMapper should be (newObjectMapper) - compareKlientBuilderInstance(initialBuilder, nextBuilder, compareObjectMapper = false) - } - - "create a new KlientBuilder instance containing all original settings together with changed Secured Connection setting" in { - val initialBuilder = KlientBuilder() - - val hasSecuredConnection = ! initialBuilder.securedConnection - val nextBuilder = initialBuilder.withSecuredConnection(hasSecuredConnection) - nextBuilder.securedConnection should be (hasSecuredConnection) - compareKlientBuilderInstance(initialBuilder, nextBuilder, compareSecuredConnection = false) - } - - "create a new KlientBuilder instance containing all original settings together with changed token provider" in { - val initialBuilder = KlientBuilder() - - val newTokenProvider = () => "some token" - val nextBuilder = initialBuilder.withTokenProvider(newTokenProvider) - nextBuilder.tokenProvider should be (newTokenProvider) - compareKlientBuilderInstance(initialBuilder, nextBuilder, compareTokenProvider = false) - } - } - - - def compareKlientBuilderInstance(initialBuilder: KlientBuilder, - nextBuilder: KlientBuilder, - compareEndpoint: Boolean = true, - comparePort: Boolean = true, - compareObjectMapper: Boolean = true, - compareSecuredConnection: Boolean = true, - compareTokenProvider: Boolean = true) = { - - if(compareEndpoint) nextBuilder.endpoint should be(initialBuilder.endpoint) - if(comparePort) nextBuilder.port should be (initialBuilder.port) - if(compareObjectMapper) nextBuilder.objectMapper should be (initialBuilder.objectMapper) - if(compareSecuredConnection) nextBuilder.securedConnection should be(initialBuilder.securedConnection) - if(compareTokenProvider) nextBuilder.tokenProvider should be(initialBuilder.tokenProvider) - - } - - - - -} - diff --git a/src/test/scala/org/zalando/nakadi/client/KlientSpec.scala b/src/test/scala/org/zalando/nakadi/client/KlientSpec.scala deleted file mode 100644 index cba45eb..0000000 --- a/src/test/scala/org/zalando/nakadi/client/KlientSpec.scala +++ /dev/null @@ -1,516 +0,0 @@ -package org.zalando.nakadi.client - -import java.net.URI -import java.util -import java.util.concurrent.atomic.AtomicReference - -import com.fasterxml.jackson.databind.{PropertyNamingStrategy, SerializationFeature, DeserializationFeature, ObjectMapper} -import com.fasterxml.jackson.module.scala.DefaultScalaModule -import com.google.common.collect.Iterators -import com.typesafe.scalalogging.LazyLogging -import io.undertow.util.{HeaderValues, HttpString, Headers} -import org.scalatest.concurrent.PatienceConfiguration.{Timeout => ScalaTestTimeout} -import org.scalatest.concurrent.ScalaFutures -import org.scalatest._ -import org.zalando.nakadi.client.actor.PartitionReceiver -import org.zalando.nakadi.client.utils.NakadiTestService -import org.zalando.nakadi.client.utils.NakadiTestService.Builder -import org.zalando.nakadi.client.utils.{Request => NakadiRequest} - -import scala.concurrent.Await -import scala.concurrent.duration._ -import scala.language.postfixOps -import scala.language.implicitConversions - - -class KlientSpec extends WordSpec with Matchers with BeforeAndAfterEach with LazyLogging with ScalaFutures { - import KlientSpec._ - - var klient: Klient = null - var service: NakadiTestService = null - - override def beforeEach() = { - klient = KlientBuilder() - .withEndpoint(new URI(HOST)) // TODO if no scheme is specified, the library utilized by the client breaks with a NullpointeException... - .withPort(PORT) - .withTokenProvider(() => TOKEN) - .build() - } - - override def afterEach(): Unit = { - klient.stop() - klient = null - - if(Option(service).isDefined) { - service.stop() - service = null - } - } - - private def performStandardRequestChecks(expectedRequestPath: String, expectedRequestMethod: HttpString): NakadiRequest = { - val collectedRequestsMap = service.getCollectedRequests - val requests = collectedRequestsMap.get(expectedRequestPath) - requests should not be null - - val request = Iterators.getLast(requests.iterator) - request.getRequestPath shouldBe expectedRequestPath - request.getRequestMethod shouldBe expectedRequestMethod - - val headerMap = request.getRequestHeaders - - if(request.getRequestMethod.equals(new HttpString("GET"))){ - headerMap.get(Headers.ACCEPT).getFirst shouldBe MEDIA_TYPE - } else { - headerMap.get(Headers.CONTENT_TYPE).getFirst shouldBe MEDIA_TYPE - } - - headerMap.get(Headers.AUTHORIZATION).getFirst shouldBe s"Bearer $TOKEN" - - request - } - - "A Klient" must { - "retrieve Nakadi metrics" in { - val expectedResponse = Map("post_event" -> Map("calls_per_second" -> "0.005", - "count" -> "5", - "status_codes" -> Map("201" -> 5)), - "get_metrics" -> Map("calls_per_second" -> "0.001", - "count" -> "1", - "status_codes" -> Map("401" -> 1))) - - // --- - val expectedResponseAsString = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(expectedResponse) - val requestMethod = new HttpString("GET") - val requestPath = "/metrics" - val responseStatusCode: Int = 200 - - val builder= new Builder - service = builder.withHost(HOST) - .withPort(PORT) - .withHandler(requestPath) - .withRequestMethod(requestMethod) - .withResponseContentType(MEDIA_TYPE) - .withResponseStatusCode(responseStatusCode) - .withResponsePayload(expectedResponseAsString) - .build - service.start() - - whenReady( klient.getMetrics, 5 seconds) { - case Left(error) => fail(s"could not retrieve metrics: $error") - case Right(metrics) => logger.debug(s"metrics => $metrics") - performStandardRequestChecks(requestPath, requestMethod) - } - } - - "retrieve Nakadi topics" in { - val expectedResponse = List(Topic("test-topic-1"), Topic("test-topic-2")) - - - val expectedResponseAsString = objectMapper.writeValueAsString(expectedResponse) - val requestMethod = new HttpString("GET") - val requestPath = "/topics" - val responseStatusCode = 200 - - val builder = new Builder - service = builder.withHost(HOST) - .withPort(PORT) - .withHandler(requestPath) - .withRequestMethod(requestMethod) - .withResponseContentType(MEDIA_TYPE) - .withResponseStatusCode(responseStatusCode) - .withResponsePayload(expectedResponseAsString) - .build - service.start() - - whenReady( klient.getTopics, 10 seconds ) { - case Left(error) => fail(s"could not retrieve topics: $error") - case Right(topics) => - logger.info(s"topics => $topics") - topics shouldBe expectedResponse - performStandardRequestChecks(requestPath, requestMethod) - - } - } - - "post events to Nakadi topics" in { - val event = Event("http://test.zalando.net/my_type", - "ARTICLE:123456", - Map("tenant-id" -> "234567", - "flow-id" -> "123456789" ), - Map("greeting" -> "hello", - "target" -> "world")) - - - val topic = "test-topic-1" - val requestMethod = new HttpString("POST") - val requestPath = s"/topics/$topic/events" - val responseStatusCode = 201 - - val builder = new Builder - service = builder.withHost(HOST) - .withPort(PORT) - .withHandler(requestPath) - .withRequestMethod(requestMethod) - .withResponseContentType(MEDIA_TYPE) - .withResponseStatusCode(responseStatusCode) - .withResponsePayload("") - .build - service.start() - - whenReady( klient.postEvent(topic, event), 10 seconds ) { - case Left(error) => fail(s"an error occurred while posting event to topic $topic") - case Right(_) => logger.debug("event post request was successful") - } - - // Not exactly clear to me what's happening here; how the 'objectMapper' is connected...? AKa280116 - - val request = performStandardRequestChecks(requestPath, requestMethod) - val sentEvent = objectMapper.readValue(request.getRequestBody, classOf[Event]) - sentEvent shouldBe event - } - - "retreive partitions of a topic" in { - val expectedPartitions = List(TopicPartition("111", "0", "0"), TopicPartition("222", "0", "1")) - val expectedResponse = objectMapper.writeValueAsString(expectedPartitions) - - - val topic = "test-topic-1" - val requestMethod = new HttpString("GET") - val requestPath = s"/topics/$topic/partitions" - val responseStatusCode = 200 - - val builder = new Builder() - service = builder.withHost(HOST) - .withPort(PORT) - .withHandler(requestPath) - .withRequestMethod(requestMethod) - .withResponseContentType(MEDIA_TYPE) - .withResponseStatusCode(responseStatusCode) - .withResponsePayload(expectedResponse) - .build - service.start() - - val receivedPartitions = whenReady( klient.getPartitions(topic), 10 seconds) { - case Left(error: String) => throw new RuntimeException(s"could not retrieve partitions: $error") - case Right(partitions) => partitions - } - receivedPartitions shouldBe expectedPartitions - performStandardRequestChecks(requestPath, requestMethod) - } - - "retrieve a particular partition" in { - val expectedPartition = TopicPartition("111", "0", "0") - val expectedResponse = objectMapper.writeValueAsString(expectedPartition) - - - val partitionId = "111" - val topic = "test-topic-1" - val requestMethod = new HttpString("GET") - val requestPath = s"/topics/$topic/partitions/" + partitionId - val responseStatusCode: Int = 200 - - val builder = new Builder() - service = builder.withHost(HOST) - .withPort(PORT) - .withHandler(requestPath) - .withRequestMethod(requestMethod) - .withResponseContentType(MEDIA_TYPE) - .withResponseStatusCode(responseStatusCode) - .withResponsePayload(expectedResponse) - .build() - service.start() - - val receivedPartition = whenReady(klient.getPartition(topic, partitionId), 10 seconds) { - case Left(error) => throw new RuntimeException(s"could not retrieve partition: $error") - case Right(receivedTopic) => receivedTopic - } - receivedPartition shouldBe expectedPartition - - performStandardRequestChecks(requestPath, requestMethod) - } - - - "subscribe to topic" in { - val partition = TopicPartition("p1", "0", "4") - val partition2 = TopicPartition("p2", "1", "1") - val partitions = List(partition, partition2) - val partitionsAsString = objectMapper.writeValueAsString(partitions) - - val event = Event("type-1", - "ARTICLE:123456", - Map("tenant-id" -> "234567", "flow-id" -> "123456789"), - Map("greeting" -> "hello", "target" -> "world")) - - val streamEvent1 = SimpleStreamEvent(Cursor("p1", partition.newestAvailableOffset), List(event), List()) - - val streamEvent1AsString = objectMapper.writeValueAsString(streamEvent1) + "\n" - - //-- - - val event2 = Event("type-2", - "ARTICLE:123456", - Map("tenant-id" -> "234567", "flow-id" -> "123456789"), - Map("greeting" -> "hello", "target" -> "world")) - - val streamEvent2 = SimpleStreamEvent(Cursor("p2", partition2.newestAvailableOffset), List(event2), List()) - - val streamEvent2AsString = objectMapper.writeValueAsString(streamEvent2) + "\n" - - //-- - - val topic = "test-topic-1" - val partitionsRequestPath = s"/topics/$topic/partitions" - val partition1EventsRequestPath = s"/topics/$topic/partitions/p1/events" - val partition2EventsRequestPath = s"/topics/$topic/partitions/p2/events" - - val httpMethod = new HttpString("GET") - val statusCode = 200 - - val builder = new Builder() - service = builder.withHost(HOST) - .withPort(PORT) - .withHandler(partitionsRequestPath) - .withRequestMethod(httpMethod) - .withResponseContentType(MEDIA_TYPE) - .withResponseStatusCode(statusCode) - .withResponsePayload(partitionsAsString) - .and - .withHandler(partition1EventsRequestPath) - .withRequestMethod(httpMethod) - .withResponseContentType(MEDIA_TYPE) - .withResponseStatusCode(statusCode) - .withResponsePayload(streamEvent1AsString) - .and - .withHandler(partition2EventsRequestPath) - .withRequestMethod(httpMethod) - .withResponseContentType(MEDIA_TYPE) - .withResponseStatusCode(statusCode) - .withResponsePayload(streamEvent2AsString) - .build - service.start() - - val listener = new TestListener - Await.ready( - klient.subscribeToTopic(topic, ListenParameters(Some("0")), listener, autoReconnect = true), - 5 seconds) - - Thread.sleep(PartitionReceiver.NO_LISTENER_RECONNECT_DELAY_IN_S * 1000L + 2000L) - - //-- check received events - - val receivedEvents = listener.receivedEvents.get - receivedEvents should contain(event) - receivedEvents should contain(event2) - - listener.onConnectionOpened should be > 0 - listener.onConnectionClosed should be > 0 // no long polling activated by test mock - - - val collectedRequests = service.getCollectedRequests - collectedRequests.size shouldBe 3 - - //-- check header and query parameters - - performStandardRequestChecks(partitionsRequestPath, httpMethod) - - val request = performStandardRequestChecks(partition1EventsRequestPath, httpMethod) - - // Q: what is the purpose of this logic? - - if(request.getRequestPath.contains(partition1EventsRequestPath)) { - var queryParameters = request.getRequestQueryParameters - checkQueryParameter(queryParameters, "start_from", partition.newestAvailableOffset) - checkQueryParameter(queryParameters, "batch_limit", "1") - checkQueryParameter(queryParameters, "stream_limit", "0") - checkQueryParameter(queryParameters, "batch_flush_timeout", "5") - - val request2 = performStandardRequestChecks(partition2EventsRequestPath, httpMethod) - queryParameters = request2.getRequestQueryParameters - checkQueryParameter(queryParameters, "start_from", partition2.newestAvailableOffset) - checkQueryParameter(queryParameters, "batch_limit", "1") - checkQueryParameter(queryParameters, "stream_limit", "0") - checkQueryParameter(queryParameters, "batch_flush_timeout", "5") - } - else { - var queryParameters = request.getRequestQueryParameters - checkQueryParameter(queryParameters, "start_from", partition2.newestAvailableOffset) - checkQueryParameter(queryParameters, "batch_limit", "1") - checkQueryParameter(queryParameters, "stream_limit", "0") - checkQueryParameter(queryParameters, "batch_flush_timeout", "5") - - val request2 = performStandardRequestChecks(partition2EventsRequestPath, httpMethod) - queryParameters = request2.getRequestQueryParameters - checkQueryParameter(queryParameters, "start_from", partition.newestAvailableOffset) - checkQueryParameter(queryParameters, "batch_limit", "1") - checkQueryParameter(queryParameters, "stream_limit", "0") - checkQueryParameter(queryParameters, "batch_flush_timeout", "5") - } - } - - "reconnect, if autoReconnect = true and stream was closed by Nakadi" in { - val partition = TopicPartition("p1", "0", "4") - val partition2 = TopicPartition("p2", "1", "1") - val partitions = List(partition, partition2) - val partitionsAsString = objectMapper.writeValueAsString(partitions) - - val event = Event("type-1", - "ARTICLE:123456", - Map("tenant-id" -> "234567", "flow-id" -> "123456789"), - Map("greeting" -> "hello", "target" -> "world")) - - val streamEvent1 = SimpleStreamEvent(Cursor("p1", "0"), List(event), List()) - - val streamEvent1AsString = objectMapper.writeValueAsString(streamEvent1) + "\n" - - val topic = "test-topic-1" - val partitionsRequestPath = s"/topics/$topic/partitions" - val partition1EventsRequestPath = s"/topics/$topic/partitions/p1/events" - - val httpMethod = new HttpString("GET") - val statusCode = 200 - - startServiceForEventListening() - - val listener = new TestListener - - Await.ready( - klient.subscribeToTopic(topic, ListenParameters(Some("0")), listener, autoReconnect = true), - 5 seconds) - - Thread.sleep(PartitionReceiver.NO_LISTENER_RECONNECT_DELAY_IN_S * 1000L + 2000L) - - service.stop() - service = null - Thread.sleep(1000L) - - startServiceForEventListening() - - Thread.sleep(2000L) - - listener.onConnectionOpened should be > 1 - listener.onConnectionClosed should be > 1 - listener.onConnectionFailed should be > 0 - } - - "unsubscribe listener" in { - val topic = "test-topic-1" - - startServiceForEventListening() - - val listener = new TestListener - - Await.ready( - klient.subscribeToTopic(topic, ListenParameters(Some("0")), listener, autoReconnect = true), - 5 seconds) - - Await.ready( - klient.subscribeToTopic(topic, ListenParameters(Some("0")), listener, autoReconnect = true), - 5 seconds) - - Thread.sleep(1000L) - - val receivedEvents = listener.receivedEvents - - klient.unsubscribeTopic(topic, listener) - - service.stop() - - Thread.sleep(1000L) - - startServiceForEventListening() - - Thread.sleep(1000L) - - receivedEvents shouldBe listener.receivedEvents - } - } - - - def startServiceForEventListening() = { - - val partition = TopicPartition("p1", "0", "4") - val partition2 = TopicPartition("p2", "1", "1") - val partitions = List(partition, partition2) - val partitionsAsString = objectMapper.writeValueAsString(partitions) - - val event = Event("type-1", - "ARTICLE:123456", - Map("tenant-id" -> "234567", "flow-id" -> "123456789"), - Map("greeting" -> "hello", "target" -> "world")) - - val streamEvent1 = SimpleStreamEvent(Cursor("p1", "0"), List(event), List()) - - val streamEvent1AsString = objectMapper.writeValueAsString(streamEvent1) + "\n" - - val topic = "test-topic-1" - val partitionsRequestPath = s"/topics/$topic/partitions" - val partition1EventsRequestPath = s"/topics/$topic/partitions/p1/events" - - val httpMethod = new HttpString("GET") - val statusCode = 200 - - val builder = new Builder() - service = builder.withHost(HOST) - .withPort(PORT) - .withHandler(partitionsRequestPath) - .withRequestMethod(httpMethod) - .withResponseContentType(MEDIA_TYPE) - .withResponseStatusCode(statusCode) - .withResponsePayload(partitionsAsString) - .and - .withHandler(partition1EventsRequestPath) - .withRequestMethod(httpMethod) - .withResponseContentType(MEDIA_TYPE) - .withResponseStatusCode(statusCode) - .withResponsePayload(streamEvent1AsString) - .build - service.start() - } -} - -object KlientSpec extends WordSpecLike /*info*/ with ShouldMatchers { - - lazy val objectMapper = new ObjectMapper() - .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) - .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) - .setPropertyNamingStrategy(PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES) - .registerModule(new DefaultScalaModule) - - val MEDIA_TYPE = "application/json" - val TOKEN = "" - val HOST = "localhost" - val PORT = 8081 - - private - class TestListener extends Listener { - var receivedEvents = new AtomicReference[List[Event]](List()) - var onConnectionClosed, onConnectionOpened, onConnectionFailed = 0 - - override def id = "test" - - override def onReceive(topic: String, partition: String, cursor: Cursor, event: Event): Unit = { - - var old = List[Event]() - do { - old = receivedEvents.get() - } - while (!receivedEvents.compareAndSet(old, old ++ List(event))) // Q: what is this doing? AKa280116 - } - - override def onConnectionClosed(topic: String, partition: String, lastCursor: Option[Cursor]): Unit = onConnectionClosed += 1 - - override def onConnectionOpened(topic: String, partition: String): Unit = onConnectionOpened += 1 - - override def onConnectionFailed(topic: String, partition: String, status: Int, error: String): Unit = onConnectionFailed += 1 - } - - private - def checkQueryParameter(queryParameters: java.util.Map[String, util.Deque[String]], parameterName: String, expectedValue: String) { - val paramDeque = queryParameters.get(parameterName) - paramDeque should not be null - paramDeque.getFirst shouldBe expectedValue - } - - private - implicit def conv(x: FiniteDuration): ScalaTestTimeout = { new ScalaTestTimeout(x) } -} diff --git a/src/test/scala/org/zalando/nakadi/client/Main.scala b/src/test/scala/org/zalando/nakadi/client/Main.scala deleted file mode 100644 index 10b1701..0000000 --- a/src/test/scala/org/zalando/nakadi/client/Main.scala +++ /dev/null @@ -1,43 +0,0 @@ -package org.zalando.nakadi.client - -import java.net.URI - -import akka.actor.ActorSystem -import akka.stream.ActorMaterializer - - -object Main { - - implicit val system = ActorSystem() - implicit val materializer = ActorMaterializer() - - - def main (args: Array[String]) { - - val klient = KlientBuilder() - .withEndpoint(new URI("localhost")) - .withPort(8080) - .withSecuredConnection(false) - .withTokenProvider(() => "") - .build() - - val listener = new Listener { - - override def id = "test" - - override def onReceive(topic: String, partition: String, cursor: Cursor, event: Event): Unit = println(s">>>>> [event=$event, partition=$partition]") - - override def onConnectionClosed(topic: String, partition: String, lastCursor: Option[Cursor]): Unit = println(s"connection closed [partition=$partition]") - - override def onConnectionOpened(topic: String, partition: String): Unit = println(s"connection opened [partition=$partition]") - - override def onConnectionFailed(topic: String, partition: String, status: Int, error: String): Unit = println(s"connection failed [topic=$topic, partition=$partition, status=$status, error=$error]") - } - - klient.subscribeToTopic("test", ListenParameters(Some("0")), listener, true) - - Thread.sleep(Long.MaxValue) - - - } -} \ No newline at end of file diff --git a/src/test/scala/org/zalando/nakadi/client/utils/KlientMock.scala b/src/test/scala/org/zalando/nakadi/client/utils/KlientMock.scala deleted file mode 100644 index 005bb5d..0000000 --- a/src/test/scala/org/zalando/nakadi/client/utils/KlientMock.scala +++ /dev/null @@ -1,51 +0,0 @@ -package org.zalando.nakadi.client.utils - -import akka.actor.Terminated -import org.zalando.nakadi.client._ -import org.zalando.nakadi.client.utils.KlientMock.MockReturnValues -import scala.concurrent.Future -import scala.concurrent.ExecutionContext.Implicits.global - -object KlientMock { - case class MockReturnValues( getMetrics: Either[String, Map[String, Any]] = Right(Map("a" -> Map("b" -> "c"))), - getPartition: Either[String, TopicPartition] = Right(TopicPartition("partitionid", "oldestAvailableOffset", "newestAvailableOffset")), - postEvent: Either[String, Unit] = Right(()), - stop: Terminated = null, - getPartitions: Either[String, List[TopicPartition]] = Right(List(TopicPartition("partitionid", "oldestAvailableOffset", "newestAvailableOffset"))), - getTopics: Either[String, List[Topic]] = Right(List(Topic("topic"))), - postEventToPartition: Either[String, Unit] = Right(())) - - lazy val defaultMockReturnValues = new MockReturnValues() -} - -class KlientMock(values: MockReturnValues = new MockReturnValues()) extends Klient { - - override def getMetrics: Future[Either[String, Map[String, Any]]] = - Future(values.getMetrics) - - override def subscribeToTopic(topic: String, parameters: ListenParameters, listener: Listener, autoReconnect: Boolean): Future[Unit] = - Future(()) - - override def stop(): Future[Terminated] = - Future(values.stop) - - override def getPartition(topic: String, partitionId: String): Future[Either[String, TopicPartition]] = - Future(values.getPartition) - - override def postEvent(topic: String, event: Event): Future[Either[String, Unit]] = - Future(values.postEvent) - - override def listenForEvents(topic: String, partitionId: String, parameters: ListenParameters, listener: Listener, autoReconnect: Boolean): Unit = () - - override def getPartitions(topic: String): Future[Either[String, List[TopicPartition]]] = - Future(values.getPartitions) - - override def getTopics: Future[Either[String, List[Topic]]] = - Future(values.getTopics) - - override def postEventToPartition(topic: String, partitionId: String, event: Event): Future[Either[String, Unit]] = - Future(values.postEventToPartition) - - - override def unsubscribeTopic(topic: String, listener: Listener): Unit = () -} diff --git a/src/test/scala/org/zalando/nakadi/client/utils/TestUtils.scala b/src/test/scala/org/zalando/nakadi/client/utils/TestUtils.scala deleted file mode 100644 index 2b39e47..0000000 --- a/src/test/scala/org/zalando/nakadi/client/utils/TestUtils.scala +++ /dev/null @@ -1,8 +0,0 @@ -package org.zalando.nakadi.client.utils - -class TestUtils { - def toScalaMap(m: java.util.Map[String, Any]): Map[String, Any] = { - import scala.collection.JavaConversions._ - m.toMap - } -} diff --git a/src/test/scala/unit/ConfTest.scala b/src/test/scala/unit/ConfTest.scala index 1a1ce2c..5afa1ed 100644 --- a/src/test/scala/unit/ConfTest.scala +++ b/src/test/scala/unit/ConfTest.scala @@ -4,13 +4,12 @@ package test.unit * ConfTest */ import org.scalatest._ -import org.zalando.nakadi.client.Conf class ConfTest extends FlatSpec with Matchers { - behavior of "Configuration" - - it should "(be consistent and) not cause an exception when loaded" in { - Conf // initiates the configuration object - } +// behavior of "Configuration" +// +// it should "(be consistent and) not cause an exception when loaded" in { +// Conf // initiates the configuration object +// } } \ No newline at end of file From 81a62f0449448bfcb034e8ac9b1c6c161a4bf124 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Wed, 23 Mar 2016 12:55:19 +0100 Subject: [PATCH 003/183] WIP --- src/main/scala/org/zalando/nakadi/client/Connection.scala | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/scala/org/zalando/nakadi/client/Connection.scala b/src/main/scala/org/zalando/nakadi/client/Connection.scala index 9da8f68..655f2f0 100644 --- a/src/main/scala/org/zalando/nakadi/client/Connection.scala +++ b/src/main/scala/org/zalando/nakadi/client/Connection.scala @@ -61,6 +61,9 @@ trait Client { * @param name - Name of the EventType */ def deleteEventType(name: String): Future[HttpResponse] + + + /** * Starts a stream delivery for the specified partitions of the given EventType. @@ -157,7 +160,7 @@ private[client] class Connection(host: String, port: Int, tokenProvider: () => S object Main extends App { val host = "nakadi-sandbox.aruha-test.zalan.do" - val OAuth2Token = () => "5748c4f2-8673-410e-b02d-7fbdd71ac095" + val OAuth2Token = () => "" val port = 443 val client = new Connection(host, port, OAuth2Token, true, false) val response = client.eventTypes() From 0228d874fb32f61a2e6cb6fbae047b155c2d6e47 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Wed, 30 Mar 2016 10:17:04 +0200 Subject: [PATCH 004/183] WIP LAAS-60 --- .gitignore | 16 +- .../org/zalando/nakadi/client/package.scala | 0 build.sbt => build | 0 .../src}/main/resources/logback.xml | 0 .../src}/main/resources/reference.conf | 0 .../org/zalando/nakadi/client/Client.scala | 218 ++++++++++++++ .../zalando/nakadi/client/Connection.scala | 104 +++++++ .../nakadi/client/event}/EventPublisher.scala | 2 +- .../client/event}/EventSubscriber.scala | 2 +- .../client/event}/EventTransformer.scala | 2 +- .../client/model/DefaultMarshaller.scala | 27 ++ .../org/zalando/nakadi/client/package.scala | 22 ++ .../nakadi/client/utils/ParseHelper.scala | 58 ++++ .../src}/test/resources/application.conf | 0 .../src}/test/resources/logback.xml | 0 .../zalando/nakadi/client/model/Model.scala | 10 +- model/src/test/resources/application.conf | 29 ++ model/src/test/resources/logback.xml | 18 ++ project/Build.scala | 55 ++++ project/Dependencies.scala | 27 ++ project/Resolvers.scala | 10 + .../zalando/nakadi/client/Connection.scala | 185 ------------ .../nakadi/client/publisher/Main.scala | 20 -- .../client/utils/NakadiTestService.java | 267 ------------------ .../zalando/nakadi/client/utils/Request.java | 60 ---- src/test/scala/unit/ConfTest.scala | 15 - 26 files changed, 585 insertions(+), 562 deletions(-) rename {src => api/src}/main/scala/org/zalando/nakadi/client/package.scala (100%) rename build.sbt => build (100%) rename {src => client/src}/main/resources/logback.xml (100%) rename {src => client/src}/main/resources/reference.conf (100%) create mode 100644 client/src/main/scala/org/zalando/nakadi/client/Client.scala create mode 100644 client/src/main/scala/org/zalando/nakadi/client/Connection.scala rename {src/main/scala/org/zalando/nakadi/client/publisher => client/src/main/scala/org/zalando/nakadi/client/event}/EventPublisher.scala (95%) rename {src/main/scala/org/zalando/nakadi/client/publisher => client/src/main/scala/org/zalando/nakadi/client/event}/EventSubscriber.scala (95%) rename {src/main/scala/org/zalando/nakadi/client/publisher => client/src/main/scala/org/zalando/nakadi/client/event}/EventTransformer.scala (96%) create mode 100644 client/src/main/scala/org/zalando/nakadi/client/model/DefaultMarshaller.scala create mode 100644 client/src/main/scala/org/zalando/nakadi/client/package.scala create mode 100644 client/src/main/scala/org/zalando/nakadi/client/utils/ParseHelper.scala rename {src => client/src}/test/resources/application.conf (100%) rename {src => client/src}/test/resources/logback.xml (100%) rename {src => model/src}/main/scala/org/zalando/nakadi/client/model/Model.scala (98%) create mode 100644 model/src/test/resources/application.conf create mode 100644 model/src/test/resources/logback.xml create mode 100644 project/Build.scala create mode 100644 project/Dependencies.scala create mode 100644 project/Resolvers.scala delete mode 100644 src/main/scala/org/zalando/nakadi/client/Connection.scala delete mode 100644 src/main/scala/org/zalando/nakadi/client/publisher/Main.scala delete mode 100644 src/test/java/org/zalando/nakadi/client/utils/NakadiTestService.java delete mode 100644 src/test/java/org/zalando/nakadi/client/utils/Request.java delete mode 100644 src/test/scala/unit/ConfTest.scala diff --git a/.gitignore b/.gitignore index 2160f35..90165c6 100644 --- a/.gitignore +++ b/.gitignore @@ -6,7 +6,7 @@ .history .lib/ dist/* -target/ +**target/ lib_managed/ src_managed/ project/boot/ @@ -27,16 +27,16 @@ dist /*.iml /out /.idea_modules -/.classpath -/.project +**/.classpath +**/.project /RUNNING_PID -/.settings -/.cache-main -/.cache-tests -/bin +**/.settings +**/.cache-main +**/.cache-tests +**/bin .DS_Store #misc aws_host.txt scm-source.json -*.notes \ No newline at end of file +*.notes diff --git a/src/main/scala/org/zalando/nakadi/client/package.scala b/api/src/main/scala/org/zalando/nakadi/client/package.scala similarity index 100% rename from src/main/scala/org/zalando/nakadi/client/package.scala rename to api/src/main/scala/org/zalando/nakadi/client/package.scala diff --git a/build.sbt b/build similarity index 100% rename from build.sbt rename to build diff --git a/src/main/resources/logback.xml b/client/src/main/resources/logback.xml similarity index 100% rename from src/main/resources/logback.xml rename to client/src/main/resources/logback.xml diff --git a/src/main/resources/reference.conf b/client/src/main/resources/reference.conf similarity index 100% rename from src/main/resources/reference.conf rename to client/src/main/resources/reference.conf diff --git a/client/src/main/scala/org/zalando/nakadi/client/Client.scala b/client/src/main/scala/org/zalando/nakadi/client/Client.scala new file mode 100644 index 0000000..3130c5b --- /dev/null +++ b/client/src/main/scala/org/zalando/nakadi/client/Client.scala @@ -0,0 +1,218 @@ +package org.zalando.nakadi.client + +import scala.concurrent.Future +import akka.actor.Terminated +import akka.http.scaladsl.model.HttpResponse +import scala.concurrent.duration.DurationInt +import scala.concurrent.ExecutionContext.Implicits.global +import org.slf4j.LoggerFactory +import com.typesafe.scalalogging.Logger +import akka.http.scaladsl.unmarshalling.Unmarshaller +import akka.protobuf.ByteString +import org.zalando.nakadi.client.model.EventType +import akka.http.scaladsl.unmarshalling._ +import org.zalando.nakadi.client.model.DefaultMarshaller + +trait Client { + + /** + * Retrieve monitoring metrics + * + * {{{ + * curl --request GET /metrics + * }}} + */ + def metrics(): Future[HttpResponse] + + /** + * Returns a list of all registered EventTypes. + * + * {{{ + * curl --request GET /event-types + * }}} + * + */ + def eventTypes()(implicit marshaller: FromEntityUnmarshaller[EventType], charset: String = "UTF-8"): Future[EventType] + + /** + * Creates a new EventType. + * + * {{{ + * curl --request POST -d @fileWithEventType /event-types + * }}} + * + * @param event - The EventType to create. + * + */ + def newEventType(eventType: Any): Future[Boolean] + + /** + * Returns the EventType identified by its name. + * {{{ + * curl --request GET /event-types/{name} + * }}} + * @param name - Name of the EventType + */ + def eventType(name: String): Future[HttpResponse] + /** + * Updates the EventType identified by its name. + * {{{ + * curl --request PUT -d @fileWithEventType /event-types/{name} + * }}} + * @param name - Name of the EventType + * @param event - Event to update + */ + def updateEventType(name: String, event: Any): Future[HttpResponse] + /** + * Deletes an EventType identified by its name. + * + * {{{ + * curl --request DELETE /event-types/{name} + * }}} + * + * @param name - Name of the EventType + */ + def deleteEventType(name: String): Future[HttpResponse] + + /** + * Creates a new Event for the given EventType. + * {{{ + * curl --request POST -d @fileWithEvent /event-types/{name}/events + * }}} + * @param name - Name of the EventType + * @param event - Event to create + */ + def newEvent(name: String, event: Any): Future[HttpResponse] + + /** + * Request a stream delivery for the specified partitions of the given EventType. + * {{{ + * curl --request GET /event-types/{name}/events + * }}} + * @param name - Name of the EventType + * + */ + def events(name: String): Future[HttpResponse] + + /** + * List the partitions for the given EventType. + * {{{ + * curl --request GET /event-types/{name}/partitions + * }}} + * @param name - Name of the EventType + */ + def partitions(name: String): Future[HttpResponse] + + /** + * Returns the Partition for the given EventType. + * {{{ + * curl --request GET /event-types/{name}/partitions/{partition} + * }}} + * @param name - Name of the EventType + * @param partition - Name of the partition + */ + + def partitionByName(name: String, partition: String): Future[HttpResponse] + + /** + * Returns all of the validation strategies supported by this installation of Nakadi. + * + * {{{ + * curl --request GET /registry/validation-strategies + * }}} + */ + def validationStrategies(): Future[HttpResponse] + + /** + * Returns all of the enrichment strategies supported by this installation of Nakadi. + * {{{ + * curl --request GET /registry/enrichment-strategies + * }}} + */ + + def enrichmentStrategies(): Future[HttpResponse] + + /** + * Returns all of the partitioning strategies supported by this installation of Nakadi. + * {{{ + * curl --request GET /registry/partitioning-strategies + * }}} + */ + def partitionStrategies(): Future[HttpResponse] + + /** + * Shuts down the communication system of the client + */ + + def stop(): Future[Terminated] + +} + +object Client { + case class ClientError(msg: String) +} + +private[client] class ClientImpl(connection: Connection) extends Client { + implicit val materializer = connection.materializer + + val logger = Logger(LoggerFactory.getLogger(this.getClass)) + def metrics(): Future[HttpResponse] = ??? + + def eventTypes()(implicit marshaller: FromEntityUnmarshaller[EventType], charset: String = "UTF-8"): Future[EventType] = { + connection.get(URI_EVENT_TYPES).flatMap(evaluateResponse(_)) } + + private def evaluateResponse[T](response: HttpResponse)(implicit unmarshaller: FromEntityUnmarshaller[T], m: Manifest[T], charset: String = "UTF-8"): Future[T] = response.status match { + case status if (status.isSuccess()) => + logger.debug("Result is successfull ({}) ", status.intValue().toString()) + logger.debug("Result =>> ", response.entity.toStrict(5.seconds).map(x => Some(x.data.decodeString("UTF-8")))) + Unmarshal(response.entity).to[T].map { x => + logger.info("YOOOOOOOOOOOOOOOOOOO") + + x } + + case status if (status.isRedirection()) => + logger.info("Result is a redirect with http-status ({}) ", status.intValue().toString()) + //TODO: Implement logic for following redirects + Unmarshal(response.entity).to[T] + case status if (status.isFailure()) => + logger.warn("An error occurred with http-status ({}) and reason:{} ", status.reason(), status.intValue().toString()) + Unmarshal(response.entity).to[T] + } + + def newEventType(eventType: Any): Future[Boolean] = ??? + + def eventType(name: String): Future[HttpResponse] = ??? + + def updateEventType(name: String, event: Any): Future[HttpResponse] = ??? + + def deleteEventType(name: String): Future[HttpResponse] = ??? + + def newEvent(name: String, event: Any): Future[HttpResponse] = ??? + + def events(name: String): Future[HttpResponse] = ??? + + def partitions(name: String): Future[HttpResponse] = ??? + + def partitionByName(name: String, partition: String): Future[HttpResponse] = ??? + + def validationStrategies(): Future[HttpResponse] = ??? + + def enrichmentStrategies(): Future[HttpResponse] = ??? + + def partitionStrategies(): Future[HttpResponse] = ??? + + def stop(): Future[Terminated] = connection.stop() + +} + +object Main extends App with DefaultMarshaller { + val host = "nakadi-sandbox.aruha-test.zalan.do" + val OAuth2Token = () => "" + val port = 443 + val client = new ClientImpl(Connection.newConnection(host, port, OAuth2Token, true, false)) + + val response = client.eventTypes() + response.map(r => + println("######################## " + r)) + +} \ No newline at end of file diff --git a/client/src/main/scala/org/zalando/nakadi/client/Connection.scala b/client/src/main/scala/org/zalando/nakadi/client/Connection.scala new file mode 100644 index 0000000..7157d82 --- /dev/null +++ b/client/src/main/scala/org/zalando/nakadi/client/Connection.scala @@ -0,0 +1,104 @@ +package org.zalando.nakadi.client + +import java.security.SecureRandom +import java.security.cert.X509Certificate + +import scala.concurrent.ExecutionContext.Implicits.global +import scala.concurrent.Future +import scala.concurrent.duration.DurationInt + +import org.slf4j.LoggerFactory + +import com.typesafe.scalalogging.Logger + +import akka.actor.{ ActorSystem, Terminated } +import akka.http.scaladsl.{ Http, HttpsConnectionContext } +import akka.http.scaladsl.model.{ HttpRequest, HttpResponse, MediaRange } +import akka.http.scaladsl.model.MediaTypes.`application/json` +import akka.http.scaladsl.model.Uri.apply +import akka.http.scaladsl.model.headers +import akka.http.scaladsl.model.headers.OAuth2BearerToken +import akka.stream.ActorMaterializer +import akka.stream.scaladsl.{ Flow, Sink, Source } +import javax.net.ssl.{ SSLContext, TrustManager, X509TrustManager } + +trait Connection { + def get(endpoint: String): Future[HttpResponse] + def stop(): Future[Terminated] + def materializer(): ActorMaterializer +} + +/** + * Companion object for factory methods. + */ +object Connection { + + /** + * + */ + def newSslContext(secured: Boolean, verified: Boolean): Option[HttpsConnectionContext] = (secured, verified) match { + case (true, true) => Some(new HttpsConnectionContext(SSLContext.getDefault)) + case (true, false) => + val permissiveTrustManager: TrustManager = new X509TrustManager() { + override def checkClientTrusted(x$1: Array[java.security.cert.X509Certificate], x$2: String): Unit = {} + override def checkServerTrusted(x$1: Array[java.security.cert.X509Certificate], x$2: String): Unit = {} + override def getAcceptedIssuers(): Array[X509Certificate] = Array.empty + } + val sslContext = SSLContext.getInstance("TLS") + sslContext.init(Array.empty, Array(permissiveTrustManager), new SecureRandom()) + Some(new HttpsConnectionContext(sslContext)) + case _ => None + } + + /** + * Creates a new + */ + def newConnection(host: String, port: Int, tokenProvider: () => String, securedConnection: Boolean, verifySSlCertificate: Boolean): Connection = + new ConnectionImpl(host, port, tokenProvider, securedConnection, verifySSlCertificate) +} + +/** + * Class for handling the configuration and most basic calls. + */ + +private[client] class ConnectionImpl(host: String, port: Int, tokenProvider: () => String, securedConnection: Boolean, verifySSlCertificate: Boolean) extends Connection { + import Connection._ + + private implicit val actorSystem = ActorSystem("Nakadi-Connections") + private implicit val http = Http(actorSystem) + implicit val materializer = ActorMaterializer() + private val timeout = 5.seconds + + val logger = Logger(LoggerFactory.getLogger(this.getClass)) + + private val connectionFlow: Flow[HttpRequest, HttpResponse, Future[Http.OutgoingConnection]] = newSslContext(securedConnection, verifySSlCertificate) match { + case Some(result) => http.outgoingConnectionHttps(host, port, result) + case None => + logger.warn("Disabled HTTPS, switching to HTTP only.") + http.outgoingConnection(host, port) + } + + def get(endpoint: String): Future[HttpResponse] = { + logger.info("Calling {}", endpoint) + val response: Future[HttpResponse] = + Source.single(DefaultHttpRequest(endpoint)) + .via(connectionFlow). + runWith(Sink.head) + logError(response) + response + } + + private def logError(future: Future[Any]) { + future recover { + case e: Throwable => logger.error("Failed to call endpoint with: ", e.getMessage) + } + } + + private def DefaultHttpRequest(url: String): HttpRequest = { + HttpRequest(uri = url).withHeaders(headers.Authorization(OAuth2BearerToken(tokenProvider())), + headers.Accept(MediaRange(`application/json`))) + } + + def stop(): Future[Terminated] = actorSystem.terminate() +} + diff --git a/src/main/scala/org/zalando/nakadi/client/publisher/EventPublisher.scala b/client/src/main/scala/org/zalando/nakadi/client/event/EventPublisher.scala similarity index 95% rename from src/main/scala/org/zalando/nakadi/client/publisher/EventPublisher.scala rename to client/src/main/scala/org/zalando/nakadi/client/event/EventPublisher.scala index 59025a9..47500e8 100644 --- a/src/main/scala/org/zalando/nakadi/client/publisher/EventPublisher.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/event/EventPublisher.scala @@ -1,4 +1,4 @@ -package org.zalando.nakadi.client.publisher +package org.zalando.nakadi.client.event import java.math.BigInteger diff --git a/src/main/scala/org/zalando/nakadi/client/publisher/EventSubscriber.scala b/client/src/main/scala/org/zalando/nakadi/client/event/EventSubscriber.scala similarity index 95% rename from src/main/scala/org/zalando/nakadi/client/publisher/EventSubscriber.scala rename to client/src/main/scala/org/zalando/nakadi/client/event/EventSubscriber.scala index ba7480f..30c1f00 100644 --- a/src/main/scala/org/zalando/nakadi/client/publisher/EventSubscriber.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/event/EventSubscriber.scala @@ -1,4 +1,4 @@ -package org.zalando.nakadi.client.publisher +package org.zalando.nakadi.client.event import akka.stream.actor.WatermarkRequestStrategy import java.math.BigInteger diff --git a/src/main/scala/org/zalando/nakadi/client/publisher/EventTransformer.scala b/client/src/main/scala/org/zalando/nakadi/client/event/EventTransformer.scala similarity index 96% rename from src/main/scala/org/zalando/nakadi/client/publisher/EventTransformer.scala rename to client/src/main/scala/org/zalando/nakadi/client/event/EventTransformer.scala index 0450eb1..a9642ae 100644 --- a/src/main/scala/org/zalando/nakadi/client/publisher/EventTransformer.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/event/EventTransformer.scala @@ -1,4 +1,4 @@ -package org.zalando.nakadi.client.publisher +package org.zalando.nakadi.client.event import java.math.BigInteger import akka.stream.actor.ActorSubscriber diff --git a/client/src/main/scala/org/zalando/nakadi/client/model/DefaultMarshaller.scala b/client/src/main/scala/org/zalando/nakadi/client/model/DefaultMarshaller.scala new file mode 100644 index 0000000..4f582a6 --- /dev/null +++ b/client/src/main/scala/org/zalando/nakadi/client/model/DefaultMarshaller.scala @@ -0,0 +1,27 @@ +package org.zalando.nakadi.client.model + +import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport +import spray.json._ +import org.zalando.nakadi.client.utils.ParseHelper + +trait DefaultMarshaller extends SprayJsonSupport with DefaultJsonProtocol { + import ParseHelper._ + + implicit val eventMetadata = jsonFormat7(EventMetadata) + implicit val problem = jsonFormat5(Problem) + implicit val metrics = jsonFormat1(Metrics) + implicit val partition = jsonFormat3(Partition) + implicit val cursor = jsonFormat2(Cursor) + implicit val dataChangeEventQualifier = jsonFormat2(DataChangeEventQualifier) + implicit val partitionResolutionStrategy = jsonFormat2(PartitionResolutionStrategy) + implicit val businessEvent = jsonFormat1(BusinessEvent) + implicit val eventTypeSchema = jsonFormat2(EventTypeSchema) + implicit val eventValidationStrategy = jsonFormat2(EventValidationStrategy) + implicit val eventEnrichmentStrategy = jsonFormat2(EventEnrichmentStrategy) + implicit val eventType = jsonFormat10(EventType) + implicit val event = jsonFormat3(Event) + implicit val eventStreamBatch = jsonFormat2(EventStreamBatch) + +} + + \ No newline at end of file diff --git a/client/src/main/scala/org/zalando/nakadi/client/package.scala b/client/src/main/scala/org/zalando/nakadi/client/package.scala new file mode 100644 index 0000000..69126c7 --- /dev/null +++ b/client/src/main/scala/org/zalando/nakadi/client/package.scala @@ -0,0 +1,22 @@ +package org.zalando.nakadi + +package object client { + + + val URI_METRICS = "/metrics" + + /*Events*/ + val URI_EVENT_TYPES = "/event-types" + val URI_EVENT_TYPE_BY_NAME = "/event-types/%s" + val URI_EVENTS_OF_EVENT_TYPE = "/event-types/%s/events" + + /*Partitions*/ + val URI_PARTITIONS_OF_EVENT_TYPE = "/event-types/%s/partitions" + val URI_PARTITION_BY_NAME = "/event-types/%s/partitions/%s" + + /*Strategies*/ + val URI_VALIDATION_STRATEGIES = "/registry/validation-strategies" + val URI_ENRICHMENT_STRATEGIES = "/registry/enrichment-strategies" + val URI_PARTITIONING_STRATEGIES = "/registry/partitioning-strategies" + +} \ No newline at end of file diff --git a/client/src/main/scala/org/zalando/nakadi/client/utils/ParseHelper.scala b/client/src/main/scala/org/zalando/nakadi/client/utils/ParseHelper.scala new file mode 100644 index 0000000..7155444 --- /dev/null +++ b/client/src/main/scala/org/zalando/nakadi/client/utils/ParseHelper.scala @@ -0,0 +1,58 @@ +package org.zalando.nakadi.client.utils + +import spray.json._ +import DefaultJsonProtocol._ +import org.zalando.nakadi.client.model.DataOperation + +object ParseHelper { + /* + * From (link expired!) -> https://tech.mendix.com/scala/2014/09/28/scala-nested-maps-to-json/ + */ + implicit object MapJsonFormat extends JsonFormat[Map[String, Any]] { + def write(m: Map[String, Any]) = { + JsObject(m.mapValues { + case s: String => JsString(s) + case v: Int => JsNumber(v) // will get output without decimal point + case v: Long => JsNumber(v) + case v: Double => JsNumber(v) + case b: Boolean => JsBoolean(b) + case mm: Map[String @unchecked, Any @unchecked] => write(mm) + case x => serializationError(s"Unexpected value within Map[String,Any] values (cannot convert to JSON): $x") + }) + } + + def read(jsv: JsValue) = jsv match { + case jso: JsObject => readMap(jso) + case v => deserializationError("Expected JsObject, but got: " + v) + } + + // Note: Makes sense having this as a separate function (not embedded in 'read') because our recursion can now + // use 'JsObject's directly. + // + private def readMap(jso: JsObject): Map[String, Any] = jso.fields.mapValues { + case JsString(s) => s + case JsNumber(d) if d.intValue == d => d.intValue + case JsNumber(d) if d.longValue == d => d.longValue + case JsNumber(d) => d + case JsBoolean(b) => b + case v: JsObject => readMap(v) + case v => deserializationError("Unexpected value within JsObject: " + v) + } + } + + implicit object DataOperationFormat extends JsonFormat[DataOperation.Value] { + def write(in: DataOperation.Value) = JsString(in.toString()) + def read(json: JsValue) = json match { + case JsString(s) => DataOperation.withName(s) + case somethingElse => deserializationError("Unexpected value four our Enumerator: " + somethingElse) + } + } + + implicit def listFormat[T :JsonFormat] = new JsonFormat[T] { + def write(in: T) = write(in) + def read(value: JsValue) = value match { + case element:JsObject => element.convertTo[T] + case _ => deserializationError("A JsObject was expected!!") + } + } +} \ No newline at end of file diff --git a/src/test/resources/application.conf b/client/src/test/resources/application.conf similarity index 100% rename from src/test/resources/application.conf rename to client/src/test/resources/application.conf diff --git a/src/test/resources/logback.xml b/client/src/test/resources/logback.xml similarity index 100% rename from src/test/resources/logback.xml rename to client/src/test/resources/logback.xml diff --git a/src/main/scala/org/zalando/nakadi/client/model/Model.scala b/model/src/main/scala/org/zalando/nakadi/client/model/Model.scala similarity index 98% rename from src/main/scala/org/zalando/nakadi/client/model/Model.scala rename to model/src/main/scala/org/zalando/nakadi/client/model/Model.scala index ef1c1f7..f8c88ad 100644 --- a/src/main/scala/org/zalando/nakadi/client/model/Model.scala +++ b/model/src/main/scala/org/zalando/nakadi/client/model/Model.scala @@ -14,10 +14,12 @@ package org.zalando.nakadi.client.model * @param title */ case class Event( - eventType: AnyRef, + eventType: EventType, additionalProperties: Boolean, title: String) + + /** * Metadata for this Event. Contains commons fields for both Business and DataChange Events. Most are enriched by Nakadi upon reception, but they in general MIGHT be set by the client. * @param eid Identifier of this Event. Clients are allowed to generate this and this SHOULD be guaranteed to be unique from the perspective of the producer. Consumers MIGHT use this value to assert uniqueness of reception of the Event. @@ -61,8 +63,8 @@ case class DataChangeEventQualifier( * @param eventQualifier Indicators of a `DataChangeEvent`'s referred data type and the type of operations done on them. * @param metadata Metadata for this Event. Contains commons fields for both Business and DataChange Events. Most are enriched by Nakadi upon reception, but they in general MIGHT be set by the client */ -case class DataChangeEvent( - data: AnyRef, +case class DataChangeEvent[T]( + data: T, eventQualifier: DataChangeEventQualifier, metadata: EventMetadata) @@ -80,7 +82,7 @@ case class Problem( detail: String, instance: String) -case class Metrics() //TODO: It is not defined yet! +case class Metrics(metrics:String) //TODO: It is not defined yet! /** * Partition information. Can be helpful when trying to start a stream using an unmanaged API. This information is not related to the state of the consumer clients. diff --git a/model/src/test/resources/application.conf b/model/src/test/resources/application.conf new file mode 100644 index 0000000..d9376e3 --- /dev/null +++ b/model/src/test/resources/application.conf @@ -0,0 +1,29 @@ +akka { + loglevel = "DEBUG" + stdout-loglevel = "DEBUG" + + actor { + debug { + # enable DEBUG logging of actor lifecycle changes + lifecycle = on + unhandled = on + } + } + + cluster { + seed-nodes = [ + "akka://ClusterSystem@127.0.0.1:2551", + "akka://ClusterSystem@127.0.0.1:2552"] + + auto-down = on + } +} + +akka.cluster.metrics.enabled=off + +# Enable metrics extension in akka-cluster-metrics. +akka.extensions=["akka.cluster.metrics.ClusterMetricsExtension"] + +# Sigar native library extract location during tests. +# Note: use per-jvm-instance folder when running multiple jvm on one host. +akka.cluster.metrics.native-library-extract-folder=${user.dir}/target/native diff --git a/model/src/test/resources/logback.xml b/model/src/test/resources/logback.xml new file mode 100644 index 0000000..faa8e20 --- /dev/null +++ b/model/src/test/resources/logback.xml @@ -0,0 +1,18 @@ + + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + + + + + + \ No newline at end of file diff --git a/project/Build.scala b/project/Build.scala new file mode 100644 index 0000000..46d5763 --- /dev/null +++ b/project/Build.scala @@ -0,0 +1,55 @@ +import sbt._ +import Keys._ +import com.typesafe.sbteclipse.plugin.EclipsePlugin.EclipseKeys +import com.typesafe.sbteclipse.plugin.EclipsePlugin.EclipseCreateSrc +import Dependencies._ + +object NakadiClient extends Build { + +EclipseKeys.createSrc := EclipseCreateSrc.Default + EclipseCreateSrc.Resource + +val defaultOptions= Seq( + "-deprecation", // Emit warning and location for usages of deprecated APIs. + "-feature", // Emit warning and location for usages of features that should be imported explicitly. + "-unchecked", // Enable additional warnings where generated code depends on assumptions. + "-Xfatal-warnings", // Fail the compilation if there are any warnings. + "-Xlint", // Enable recommended additional warnings. + "-Ywarn-adapted-args", // Warn if an argument list is modified to match the receiver. + "-Ywarn-dead-code", // Warn when dead code is identified. + "-Ywarn-inaccessible", // Warn about inaccessible types in method signatures. + "-Ywarn-nullary-override", // Warn when non-nullary overrides nullary, e.g. def foo() over def foo. + "-Ywarn-numeric-widen" // Warn when numerics are widened. +) +lazy val commonSettings = Seq( + organization := "com.example", + version := "0.1.0", + scalaVersion := "2.11.7" +) + +lazy val model = withDefaults( + "nakadi-klients-model", + project.in(file("model")) + ) + +lazy val api = withDefaults( + "nakadi-klients-api", + project.in(file("api")).dependsOn(model) + ) + +lazy val client = withDefaults( + "nakadi-klients", + project.in(file("client")).dependsOn(api, model) + ).settings(libraryDependencies ++= clientDeps) + +def withDefaults(projectName:String, project:sbt.Project)={ + project.settings( + name := projectName, + organization := "org.zalando.nakadi.client", + scalaVersion := "2.11.7", + resolvers += Resolver.mavenLocal, + resolvers += "Maven Central Server" at "http://repo1.maven.org/maven2", + scalacOptions ++= defaultOptions) +} + + +} diff --git a/project/Dependencies.scala b/project/Dependencies.scala new file mode 100644 index 0000000..48cca4a --- /dev/null +++ b/project/Dependencies.scala @@ -0,0 +1,27 @@ +import sbt._ + +object Dependencies { + + val clientDeps = { + val akkaVersion = "2.4.2" + Seq( + "com.typesafe.akka" %% "akka-http-spray-json-experimental" % akkaVersion withSources() withJavadoc(), + "com.typesafe.akka" %% "akka-actor" % akkaVersion withSources() withJavadoc(), + "com.typesafe.akka" %% "akka-http-experimental" % akkaVersion withSources() withJavadoc(), + "com.typesafe.akka" %% "akka-cluster-metrics" % akkaVersion withSources() withJavadoc(), + "com.typesafe.akka" %% "akka-testkit" % akkaVersion % "test" withSources() withJavadoc(), + "com.typesafe" % "config" % "1.3.0" withSources() withJavadoc(), + "com.google.guava" % "guava" % "19.0" withSources() withJavadoc(), + "com.typesafe.scala-logging" %% "scala-logging" % "3.1.0" withSources() withJavadoc(), + "com.fasterxml.jackson.core" % "jackson-core" % "2.7.0" withSources() withJavadoc(), + "com.fasterxml.jackson.module" %% "jackson-module-scala" % "2.6.3" withSources() withJavadoc(), + "ch.qos.logback" % "logback-classic" % "1.1.3" withSources() withJavadoc(), + "org.scalatest" %% "scalatest" % "2.2.6" % "test" withSources() withJavadoc(), + //"io.undertow" % "undertow-core" % "1.2.12.Final" % "test" withSources() withJavadoc(), + //"io.undertow" % "undertow-servlet" % "1.2.12.Final" % "test" withSources() withJavadoc(), + "org.apache.commons" % "commons-io" % "1.3.2" % "test" withSources() withJavadoc(), + //"com.google.code.findbugs" % "jsr305" % "1.3.9" % "test" withSources() withJavadoc(), + "junit" % "junit" % "4.12" % "test" withSources() withJavadoc() + ) + } +} diff --git a/project/Resolvers.scala b/project/Resolvers.scala new file mode 100644 index 0000000..82fbf43 --- /dev/null +++ b/project/Resolvers.scala @@ -0,0 +1,10 @@ +import sbt._ +import Keys._ + +object Resolvers { + + val settings = Seq( + resolvers += Resolver.mavenLocal, + resolvers += "Maven Central Server" at "http://repo1.maven.org/maven2" + ) +} diff --git a/src/main/scala/org/zalando/nakadi/client/Connection.scala b/src/main/scala/org/zalando/nakadi/client/Connection.scala deleted file mode 100644 index 655f2f0..0000000 --- a/src/main/scala/org/zalando/nakadi/client/Connection.scala +++ /dev/null @@ -1,185 +0,0 @@ -package org.zalando.nakadi.client - -import akka.http.scaladsl.Http -import akka.http.scaladsl.model._ -import akka.stream.ActorMaterializer -import akka.stream.scaladsl._ -import scala.concurrent.Future -import akka.actor.ActorSystem -import akka.http.scaladsl.model.Uri.apply -import scala.concurrent.ExecutionContext.Implicits.global -import org.slf4j.LoggerFactory -import com.typesafe.scalalogging.Logger -import org.slf4j.LoggerFactory -import akka.http.scaladsl.HttpsConnectionContext -import akka.http.scaladsl.model.headers.OAuth2BearerToken -import akka.http.scaladsl.model.MediaTypes.`application/json` -import javax.net.ssl.SSLContext -import java.security.SecureRandom -import java.security.cert.X509Certificate -import javax.net.ssl.{ SSLContext, TrustManager, X509TrustManager } -import scala.concurrent.duration.DurationInt -import akka.actor.Terminated - -trait Client { - - /** - * Returns monitoring metrics - */ - def metrics(): Future[HttpResponse] - - /** - * Returns a list of all registered EventTypes. - */ - def eventTypes(): Future[HttpResponse] - - /** - * Creates a new EventType. - * - * @param event - The event to create. - * - */ - def newEventType(eventType: Any): Future[Boolean] - - /** - * Returns the EventType identified by its name. - * @param name - Name of the EventType - */ - def eventType(name: String): Future[HttpResponse] - /** - * Updates the EventType identified by its name. - * - * - * @param name - Name of the EventType - * @param event - Event to update - */ - def updateEventType(name: String, event: Any): Future[HttpResponse] - /** - * Deletes an EventType identified by its name. - * - * - * @param name - Name of the EventType - */ - def deleteEventType(name: String): Future[HttpResponse] - - - - - /** - * Starts a stream delivery for the specified partitions of the given EventType. - */ - def eventsOfEventType(eventType: String): Future[HttpResponse] - - def partitionsOfEventType(eventType: String): Future[HttpResponse] - - def partitionByName(eventType: String, partitionName: String): Future[HttpResponse] - - def validationStrategies(): Future[HttpResponse] - - def enrichmentStrategies(): Future[HttpResponse] - - def partitionStrategies(): Future[HttpResponse] - - /** - * Shuts down the communication system of the client - */ - - def stop(): Future[Terminated] - -} - -object Connection { - - def getNewSslContext(secured: Boolean, verified: Boolean): Option[HttpsConnectionContext] = { - (secured, verified) match { - case (true, true) => Some(new HttpsConnectionContext(SSLContext.getDefault)) - case (true, false) => - val permissiveTrustManager: TrustManager = new X509TrustManager() { - override def checkClientTrusted(x$1: Array[java.security.cert.X509Certificate], x$2: String): Unit = {} - override def checkServerTrusted(x$1: Array[java.security.cert.X509Certificate], x$2: String): Unit = {} - override def getAcceptedIssuers(): Array[X509Certificate] = Array.empty - } - val sslContext = SSLContext.getInstance("TLS") - sslContext.init(Array.empty, Array(permissiveTrustManager), new SecureRandom()) - Some(new HttpsConnectionContext(sslContext)) - case _ => None - } - } -} - -/** - * Class for handling the configuration and connections only. - */ -private[client] class Connection(host: String, port: Int, tokenProvider: () => String, securedConnection: Boolean, verifySSlCertificate: Boolean) extends Client { - import Connection._ - private implicit val actorSystem = ActorSystem("Nakadi-client") - private implicit val http = Http(actorSystem) - implicit val materializer = ActorMaterializer() - - private val timeout = 5.seconds - - val logger = Logger(LoggerFactory.getLogger(this.getClass)) - - def eventTypes(): Future[HttpResponse] = { - get(URI_EVENT_TYPES) - } - - def stop(): Future[Terminated] = actorSystem.terminate() - - private val connectionFlow: Flow[HttpRequest, HttpResponse, Future[Http.OutgoingConnection]] = { - getNewSslContext(securedConnection, verifySSlCertificate) match { - case Some(result) => http.outgoingConnectionHttps(host, port, result) - case None => - logger.warn("Disabling HTTPS") - http.outgoingConnection(host, port) - } - } - - private def get(endpoint: String): Future[HttpResponse] = { - logger.info("Calling {}", endpoint) - val response: Future[HttpResponse] = - Source.single(DefaultHttpRequest(endpoint)) - .via(connectionFlow). - runWith(Sink.head) - logError(response) - response - } - - private def logError(future: Future[Any]) { - future recover { - case e: Throwable => logger.error("Failed to call endpoint with: ", e.getMessage) - } - } - - private def DefaultHttpRequest(url: String): HttpRequest = { - HttpRequest(uri = url).withHeaders(headers.Authorization(OAuth2BearerToken(tokenProvider())), - headers.Accept(MediaRange(`application/json`))) - } -} - -object Main extends App { - - val host = "nakadi-sandbox.aruha-test.zalan.do" - val OAuth2Token = () => "" - val port = 443 - val client = new Connection(host, port, OAuth2Token, true, false) - val response = client.eventTypes() - implicit val materializer = client.materializer - // response.map { x => - // println("########################") - // println("########################") - // println("Client= " + x.toString) - // } - - response.map { response => - response.status match { - case status if (response.status.isSuccess()) => - println("" + response.toString()) - println("" + response.entity.toStrict(5.seconds).map(x => Some(x.data.decodeString("UTF-8")))) - println("" + response.entity.getContentType()) - client.stop() - case _ => - println("") - } - } -} \ No newline at end of file diff --git a/src/main/scala/org/zalando/nakadi/client/publisher/Main.scala b/src/main/scala/org/zalando/nakadi/client/publisher/Main.scala deleted file mode 100644 index 49b5e5c..0000000 --- a/src/main/scala/org/zalando/nakadi/client/publisher/Main.scala +++ /dev/null @@ -1,20 +0,0 @@ -package org.zalando.nakadi.client.publisher - -import java.math.BigInteger -import akka.actor.ActorSystem -import akka.stream.actor.ActorSubscriber -import akka.stream.actor.ActorPublisher -import akka.actor.Props - -//object MainApp extends App { -// implicit val system = ActorSystem("test") -// -// println("Creating EventPublisher!") -// val pubRef = system.actorOf(Props[EventPublisher]) -// val publisher = ActorPublisher[BigInteger](pubRef) // => org.reactivestreams.Publisher -// -// println("Creating SlowSubscriber!") -// val subRef = system.actorOf(Props(new SlowSubscriber(500L))) // 500 ms delay -// val subscriber = ActorSubscriber[BigInteger](subRef) // => org.reactivestreams.Subscriber -// publisher.subscribe(subscriber) -//} \ No newline at end of file diff --git a/src/test/java/org/zalando/nakadi/client/utils/NakadiTestService.java b/src/test/java/org/zalando/nakadi/client/utils/NakadiTestService.java deleted file mode 100644 index 3ef62a8..0000000 --- a/src/test/java/org/zalando/nakadi/client/utils/NakadiTestService.java +++ /dev/null @@ -1,267 +0,0 @@ -package org.zalando.nakadi.client.utils; - - -import com.google.common.base.MoreObjects; -import com.google.common.collect.ArrayListMultimap; -import com.google.common.collect.Lists; -import com.google.common.collect.Multimap; -import com.google.common.collect.Multimaps; -import io.undertow.Undertow; -import io.undertow.server.HttpHandler; -import io.undertow.server.HttpServerExchange; -import io.undertow.util.HeaderMap; -import io.undertow.util.Headers; -import io.undertow.util.HttpString; -import io.undertow.util.StatusCodes; -import org.apache.commons.io.IOUtils; - -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; - -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkNotNull; - -public class NakadiTestService { - - private final String host; - private final int port; - - private ConcurrentHashMap handlerMap; - private Multimap collectedRequests; - - private Undertow server; - - private NakadiTestService(final String host, - final int port, - final List handlerList) { - this.host = host; - this.port = port; - - this.handlerMap = new ConcurrentHashMap<>(); - handlerList.forEach(h -> handlerMap.put(h.getRequestPath(), h)); - - this.collectedRequests = Multimaps.synchronizedListMultimap(ArrayListMultimap.create()); - } - - - public void start() { - server = Undertow.builder() - .addHttpListener(port, host) - .setHandler(new HttpHandler() { - - @Override - public void handleRequest(final HttpServerExchange exchange) throws Exception { - - - final String receivedRequestPath = exchange.getRequestPath(); - final HttpString receivedRequestMethod = exchange.getRequestMethod(); - - - final Handler handler = handlerMap.get(receivedRequestPath); - if(handler == null || ! Objects.equals(handler.getRequestMethod(), receivedRequestMethod)) { - // exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, handler.getResponseContentType()); - exchange.setResponseCode(StatusCodes.NOT_FOUND); - exchange.getResponseSender().send("invalid request: " + receivedRequestPath + " with request method: " + receivedRequestMethod); - } - else { - final HeaderMap requestHeaders = exchange.getRequestHeaders(); - final Map> requestQueryParameters = exchange.getQueryParameters(); - final HttpString requestMethod = exchange.getRequestMethod(); - - exchange.startBlocking(); - final String requestBody = IOUtils.toString(exchange.getInputStream()); - - final Request request = new Request(handler.getRequestPath(), - requestHeaders, - requestQueryParameters, - requestMethod, - requestBody); - - collectedRequests.put(receivedRequestPath, request); - - exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, handler.responseContentType); - exchange.setResponseCode(handler.getResponseStatusCode()); - exchange.getResponseSender().send(handler.getResponsePayload()); - } - } - }).build(); - server.start(); - } - - - public Map> getCollectedRequests() { - return this.collectedRequests.asMap(); - } - - public void stop() { - server.stop(); - server = null; - } - - - //------------- - - - public static class Builder { - - private ArrayList handlerList; - - private String host = "localhost"; - private int port = 8080; - - public Builder() { - this.handlerList = Lists.newArrayList(); - } - - - public Builder withHost(final String host) { - this.host = checkNotNull(host); - return this; - } - - public Builder withPort(final int port) { - this.port = port; - return this; - } - - public HandlerBuilder withHandler(final String requestPath) { - return new HandlerBuilder(this, requestPath); - } - - public Builder finalizeHandler(final HandlerBuilder handlerBuilder) { - handlerList.add(handlerBuilder.buildHandler()); - return this; - } - - public NakadiTestService build() { - return new NakadiTestService(host, port, handlerList); - } - - @Override - public String toString() { - return MoreObjects.toStringHelper(this) - .add("host", host) - .add("port", port) - .add("handlerList", handlerList) - .toString(); - } - } - - //---------------- - - public static final class HandlerBuilder{ - private final String requestPath; - private final Builder mainBuilder; - - private HttpString requestMethod; - private int responseStatusCode = 500; - private String responsePayload = "{ \"content\": \"nix\" }"; - private String responseContentType = "application/json"; - - HandlerBuilder(final Builder mainBuilder, final String requestPath) { - this.mainBuilder = checkNotNull(mainBuilder); - this.requestPath = checkNotNull(requestPath); - } - - public HandlerBuilder withRequestMethod(final HttpString requestMethod) { - this.requestMethod = checkNotNull(requestMethod); - return this; - } - - public HandlerBuilder withResponseStatusCode(final int responseStatusCode) { - checkArgument(responseStatusCode > 0 && responseStatusCode < 1000, - "invalid response status code %s", responseStatusCode); - this.responseStatusCode = responseStatusCode; - return this; - } - - public HandlerBuilder withResponsePayload(final String responsePayload) { - this.responsePayload = checkNotNull(responsePayload); - return this; - } - - public HandlerBuilder withResponseContentType(final String responseContentType) { - this.responseContentType = checkNotNull(responseContentType); - return this; - } - - public Builder and() { - return mainBuilder.finalizeHandler(this); - } - - - public NakadiTestService build() { - return mainBuilder.finalizeHandler(this).build(); - } - - public Handler buildHandler() { - return new Handler( - requestPath, - requestMethod, - responseStatusCode, - responsePayload, - responseContentType); - } - - @Override - public String toString() { - return MoreObjects.toStringHelper(this) - .add("requestPath", requestPath) - .add("requestMethod", requestMethod) - .add("responseStatusCode", responseStatusCode) - .add("responsePayload", responsePayload) - .add("responseContentType", responseContentType) - .toString(); - } - } - - //----------- - - private static final class Handler { - - private final String requestPath; - private final HttpString requestMethod; - private final int responseStatusCode; - private final String responsePayload; - private final String responseContentType; - - Handler(String requestPath, HttpString requestMethod, int responseStatusCode, String responsePayload, String responseContentType) { - this.requestPath = requestPath; - this.requestMethod = requestMethod; - this.responseStatusCode = responseStatusCode; - this.responsePayload = responsePayload; - this.responseContentType = responseContentType; - } - - public String getRequestPath() { - return requestPath; - } - - public HttpString getRequestMethod() { - return requestMethod; - } - - public int getResponseStatusCode() { - return responseStatusCode; - } - - public String getResponsePayload() { - return responsePayload; - } - - public String getResponseContentType() { - return responseContentType; - } - - @Override - public String toString() { - return MoreObjects.toStringHelper(this) - .add("requestPath", requestPath) - .add("requestMethod", requestMethod) - .add("responseStatusCode", responseStatusCode) - .add("responsePayload", responsePayload) - .add("responseContentType", responseContentType) - .toString(); - } - } -} diff --git a/src/test/java/org/zalando/nakadi/client/utils/Request.java b/src/test/java/org/zalando/nakadi/client/utils/Request.java deleted file mode 100644 index dc52b12..0000000 --- a/src/test/java/org/zalando/nakadi/client/utils/Request.java +++ /dev/null @@ -1,60 +0,0 @@ -package org.zalando.nakadi.client.utils; - -import com.google.common.base.MoreObjects; -import io.undertow.util.HeaderMap; -import io.undertow.util.HttpString; - -import java.util.Deque; -import java.util.Map; - -public final class Request { - private final String requestPath; - private final HeaderMap requestHeaders; - private final Map> requestQueryParameters; - private final HttpString requestMethod; - private final String requestBody; - - - public Request( final String requestPath, - final HeaderMap requestHeaders, - final Map> requestQueryParameters, - final HttpString requestMethod, - final String requestBody) { - this.requestPath = requestPath; - this.requestHeaders = requestHeaders; - this.requestQueryParameters = requestQueryParameters; - this.requestMethod = requestMethod; - this.requestBody = requestBody; - } - - public String getRequestPath() { - return requestPath; - } - - public HeaderMap getRequestHeaders() { - return requestHeaders; - } - - public Map> getRequestQueryParameters() { - return requestQueryParameters; - } - - public HttpString getRequestMethod() { - return requestMethod; - } - - public String getRequestBody() { - return requestBody; - } - - @Override - public String toString() { - return MoreObjects.toStringHelper(this) - .add("requestPath", requestPath) - .add("requestHeaders", requestHeaders) - .add("requestQueryParameters", requestQueryParameters) - .add("requestMethod", requestMethod) - .add("requestBody", requestBody) - .toString(); - } -} diff --git a/src/test/scala/unit/ConfTest.scala b/src/test/scala/unit/ConfTest.scala deleted file mode 100644 index 5afa1ed..0000000 --- a/src/test/scala/unit/ConfTest.scala +++ /dev/null @@ -1,15 +0,0 @@ -package test.unit - -/* -* ConfTest -*/ -import org.scalatest._ - -class ConfTest extends FlatSpec with Matchers { - -// behavior of "Configuration" -// -// it should "(be consistent and) not cause an exception when loaded" in { -// Conf // initiates the configuration object -// } -} \ No newline at end of file From 342aa05929757cb974dd5530f9dd06e22f9275fd Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Wed, 30 Mar 2016 16:23:48 +0200 Subject: [PATCH 005/183] LAAS-60 Finished implementing the Default Serializers/Deserializers fixes #31, #35 --- .../org/zalando/nakadi/client/Client.scala | 46 +++++----- .../client/model/DefaultMarshaller.scala | 57 +++++++----- .../nakadi/client/DefaultMarshallerTest.scala | 88 +++++++++++++++++++ .../client/util/SimpleModelFactory.scala | 48 ++++++++++ .../zalando/nakadi/client/util/TestData.scala | 26 ++++++ .../zalando/nakadi/client/model/Model.scala | 6 +- project/Build.scala | 2 + project/Dependencies.scala | 4 +- project/plugins.sbt | 6 ++ 9 files changed, 235 insertions(+), 48 deletions(-) create mode 100644 client/src/test/scala/org/zalando/nakadi/client/DefaultMarshallerTest.scala create mode 100644 client/src/test/scala/org/zalando/nakadi/client/util/SimpleModelFactory.scala create mode 100644 client/src/test/scala/org/zalando/nakadi/client/util/TestData.scala diff --git a/client/src/main/scala/org/zalando/nakadi/client/Client.scala b/client/src/main/scala/org/zalando/nakadi/client/Client.scala index 3130c5b..d1f95bb 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/Client.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/Client.scala @@ -12,6 +12,7 @@ import akka.protobuf.ByteString import org.zalando.nakadi.client.model.EventType import akka.http.scaladsl.unmarshalling._ import org.zalando.nakadi.client.model.DefaultMarshaller +import scala.concurrent.Await trait Client { @@ -44,7 +45,7 @@ trait Client { * @param event - The EventType to create. * */ - def newEventType(eventType: Any): Future[Boolean] + def newEventType(eventType: EventType): Future[Boolean] /** * Returns the EventType identified by its name. @@ -159,27 +160,29 @@ private[client] class ClientImpl(connection: Connection) extends Client { def metrics(): Future[HttpResponse] = ??? def eventTypes()(implicit marshaller: FromEntityUnmarshaller[EventType], charset: String = "UTF-8"): Future[EventType] = { - connection.get(URI_EVENT_TYPES).flatMap(evaluateResponse(_)) } - - private def evaluateResponse[T](response: HttpResponse)(implicit unmarshaller: FromEntityUnmarshaller[T], m: Manifest[T], charset: String = "UTF-8"): Future[T] = response.status match { - case status if (status.isSuccess()) => - logger.debug("Result is successfull ({}) ", status.intValue().toString()) - logger.debug("Result =>> ", response.entity.toStrict(5.seconds).map(x => Some(x.data.decodeString("UTF-8")))) - Unmarshal(response.entity).to[T].map { x => - logger.info("YOOOOOOOOOOOOOOOOOOO") - - x } - - case status if (status.isRedirection()) => - logger.info("Result is a redirect with http-status ({}) ", status.intValue().toString()) - //TODO: Implement logic for following redirects - Unmarshal(response.entity).to[T] - case status if (status.isFailure()) => - logger.warn("An error occurred with http-status ({}) and reason:{} ", status.reason(), status.intValue().toString()) - Unmarshal(response.entity).to[T] + connection.get(URI_EVENT_TYPES).flatMap(evaluateResponse(_)) } - def newEventType(eventType: Any): Future[Boolean] = ??? + def evaluateResponse[T](response: HttpResponse)(implicit unmarshaller: FromEntityUnmarshaller[T], m: Manifest[T], charset: String = "UTF-8"): Future[T] = { + logger.debug("received [response={}]", response) + response.status match { + case status if (status.isSuccess()) => + logger.info("Result is successfull ({}) ", status.intValue().toString()) + Unmarshal(response.entity).to[T] + case status if (status.isRedirection()) => + logger.info("Result is a redirect with http-status ({}) ", status.intValue().toString()) + //TODO: Implement logic for following redirects + Unmarshal(response.entity).to[T].map { x => + logger.info("##### >>>>> ") + x + } + + case status if (status.isFailure()) => + logger.warn("An error occurred with http-status ({}) and reason:{} ", status.reason(), status.intValue().toString()) + Unmarshal(response.entity).to[T] + } + } + def newEventType(eventType: EventType): Future[Boolean] = ??? def eventType(name: String): Future[HttpResponse] = ??? @@ -207,11 +210,12 @@ private[client] class ClientImpl(connection: Connection) extends Client { object Main extends App with DefaultMarshaller { val host = "nakadi-sandbox.aruha-test.zalan.do" - val OAuth2Token = () => "" + val OAuth2Token = () => "3ba41b94-a30a-4dee-a90e-237f4e77edde" val port = 443 val client = new ClientImpl(Connection.newConnection(host, port, OAuth2Token, true, false)) val response = client.eventTypes() + Await.result(response, 10.second) response.map(r => println("######################## " + r)) diff --git a/client/src/main/scala/org/zalando/nakadi/client/model/DefaultMarshaller.scala b/client/src/main/scala/org/zalando/nakadi/client/model/DefaultMarshaller.scala index 4f582a6..213248d 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/model/DefaultMarshaller.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/model/DefaultMarshaller.scala @@ -1,27 +1,40 @@ package org.zalando.nakadi.client.model -import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport -import spray.json._ import org.zalando.nakadi.client.utils.ParseHelper +import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport +import akka.http.scaladsl.marshalling.Marshaller +import akka.http.scaladsl.unmarshalling.Unmarshaller +import spray.json.{ DefaultJsonProtocol, JsonFormat, JsonReader, JsonWriter, pimpAny, pimpString } + trait DefaultMarshaller extends SprayJsonSupport with DefaultJsonProtocol { - import ParseHelper._ - - implicit val eventMetadata = jsonFormat7(EventMetadata) - implicit val problem = jsonFormat5(Problem) - implicit val metrics = jsonFormat1(Metrics) - implicit val partition = jsonFormat3(Partition) - implicit val cursor = jsonFormat2(Cursor) - implicit val dataChangeEventQualifier = jsonFormat2(DataChangeEventQualifier) - implicit val partitionResolutionStrategy = jsonFormat2(PartitionResolutionStrategy) - implicit val businessEvent = jsonFormat1(BusinessEvent) - implicit val eventTypeSchema = jsonFormat2(EventTypeSchema) - implicit val eventValidationStrategy = jsonFormat2(EventValidationStrategy) - implicit val eventEnrichmentStrategy = jsonFormat2(EventEnrichmentStrategy) - implicit val eventType = jsonFormat10(EventType) - implicit val event = jsonFormat3(Event) - implicit val eventStreamBatch = jsonFormat2(EventStreamBatch) - -} - - \ No newline at end of file + import org.zalando.nakadi.client.utils.ParseHelper._ + + implicit val eventMetadataFormatter = jsonFormat7(EventMetadata) + implicit val problemFormatter = jsonFormat5(Problem) + implicit val metricsFormatter = jsonFormat1(Metrics) + implicit val partitionFormatter = jsonFormat3(Partition) + implicit val cursorFormatter = jsonFormat2(Cursor) + implicit val dataChangeEventQualifierFormatter = jsonFormat2(DataChangeEventQualifier) + implicit val partitionResolutionStrategyFormatter = jsonFormat2(PartitionResolutionStrategy) + implicit val businessEventFormatter = jsonFormat1(BusinessEvent) + implicit val eventTypeSchemaFormatter = jsonFormat2(EventTypeSchema) + implicit val eventValidationStrategyFormatter = jsonFormat2(EventValidationStrategy) + implicit val eventEnrichmentStrategyFormatter = jsonFormat2(EventEnrichmentStrategy) + implicit val eventTypeFormatter = jsonFormat10(EventType) + implicit val eventFormatter = jsonFormat3(Event) + implicit val eventStreamBatchFormatter = jsonFormat2(EventStreamBatch) + + implicit def dataChangeEventFormatter[A: JsonFormat] = jsonFormat3(DataChangeEvent.apply[A]) + + //Handy marshallers + implicit def toStringMarshaller[T](implicit writer: JsonWriter[T]): Marshaller[T, String] = + Marshaller.opaque { + _.toJson(writer).toString() + } + + implicit def fromEntityUnmarshaller[T](implicit reader: JsonReader[T]): Unmarshaller[String, T] = Unmarshaller.strict { + input => input.parseJson.convertTo[T] + } + +} \ No newline at end of file diff --git a/client/src/test/scala/org/zalando/nakadi/client/DefaultMarshallerTest.scala b/client/src/test/scala/org/zalando/nakadi/client/DefaultMarshallerTest.scala new file mode 100644 index 0000000..bf3b673 --- /dev/null +++ b/client/src/test/scala/org/zalando/nakadi/client/DefaultMarshallerTest.scala @@ -0,0 +1,88 @@ +package org.zalando.nakadi.client + +import scala.concurrent.duration.DurationInt +import org.scalatest.{ Matchers, WordSpec } +import org.zalando.nakadi.client.model._ +import org.zalando.nakadi.client.model.DefaultMarshaller +import akka.actor.ActorSystem +import akka.http.scaladsl.marshalling.Marshal +import akka.http.scaladsl.marshalling.Marshaller._ +import akka.stream.Materializer +import akka.http.scaladsl.unmarshalling._ +import akka.http.scaladsl.model._ +import scala.concurrent.Await +import scala.concurrent.duration._ +import akka.stream.ActorMaterializer +import akka.http.scaladsl.marshalling.GenericMarshallers +import akka.http.scaladsl.marshalling.Marshaller +import org.zalando.nakadi.client.model.Event +import org.zalando.nakadi.client.model._ + +class DefaultMarshallerTest extends WordSpec with Matchers with DefaultMarshaller { + implicit val system = ActorSystem("rest-server") + implicit val dispatcher = system.dispatcher + implicit val materializer: Materializer = ActorMaterializer() + + import org.zalando.nakadi.client.util.SimpleModelFactory._ + + /** + * Tests + */ + "All model Objects " must { + val testName = "marshal an unmarshall without any problems" + s"$testName(eventMetadata)" in { + checkSerializationProcess("eventMetadata", eventMetadata) + } + s"$testName(problem)" in { + checkSerializationProcess("problem", problem) + } + s"$testName(metrics)" in { + checkSerializationProcess("metrics", metrics) + } + s"$testName(partition)" in { + checkSerializationProcess("partition", partition) + } + s"$testName(cursor)" in { + checkSerializationProcess("cursor", cursor) + } + s"$testName(eventTypeSchema)" in { + checkSerializationProcess("eventTypeSchema", eventTypeSchema) + } + s"$testName(eventValidationStrategy)" in { + checkSerializationProcess("eventValidationStrategy", eventValidationStrategy) + } + s"$testName(partitionResolutionStrategy)" in { + checkSerializationProcess("partitionResolutionStrategy", partitionResolutionStrategy) + } + s"$testName(eventEnrichmentStrategy)" in { + checkSerializationProcess("eventEnrichmentStrategy", partitionResolutionStrategy) + } + s"$testName(businessEvent)" in { + checkSerializationProcess("businessEvent", businessEvent) + } + s"$testName(dataChangeEventQualifier)" in { + checkSerializationProcess("dataChangeEventQualifier", dataChangeEventQualifier) + } + s"$testName(dataChangeEvent)" in { + checkSerializationProcess("dataChangeEvent", dataChangeEvent) + } + s"$testName(eventType)" in { + checkSerializationProcess("eventType", eventType) + } + s"$testName(event)" in { + checkSerializationProcess("event", event) + } + s"$testName(eventStreamBatch)" in { + checkSerializationProcess("eventStreamBatch", eventStreamBatch) + } + + } + def checkSerializationProcess[T](key: String, obj: T)(implicit m: Marshaller[T, String], um: Unmarshaller[String, T]) { + val futureEntity = Marshal(obj).to[String] + val stringResult = Await.result(futureEntity, 1.second) // don't block in non-test code! + val futureResult = Unmarshal(stringResult).to[T] + val eventResult = Await.result(futureResult, 1.second) // don't block in non-test code! + assert(eventResult == obj, s"Failed to marshall $key") + } + +} \ No newline at end of file diff --git a/client/src/test/scala/org/zalando/nakadi/client/util/SimpleModelFactory.scala b/client/src/test/scala/org/zalando/nakadi/client/util/SimpleModelFactory.scala new file mode 100644 index 0000000..32cafd9 --- /dev/null +++ b/client/src/test/scala/org/zalando/nakadi/client/util/SimpleModelFactory.scala @@ -0,0 +1,48 @@ +package org.zalando.nakadi.client.util + +import org.zalando.nakadi.client.model._ +import org.zalando.nakadi.client.model.Problem +import org.zalando.nakadi.client.model.PartitionResolutionStrategy +import org.zalando.nakadi.client.model.Partition +import org.zalando.nakadi.client.model.Metrics +import org.zalando.nakadi.client.model.EventValidationStrategy +import org.zalando.nakadi.client.model.EventTypeSchema +import org.zalando.nakadi.client.model.EventType +import org.zalando.nakadi.client.model.EventStreamBatch +import org.zalando.nakadi.client.model.EventMetadata +import org.zalando.nakadi.client.model.EventEnrichmentStrategy +import org.zalando.nakadi.client.model.Event +import org.zalando.nakadi.client.model.DataOperation +import org.zalando.nakadi.client.model.DataChangeEventQualifier +import org.zalando.nakadi.client.model.DataChangeEvent +import org.zalando.nakadi.client.model.Cursor +import org.zalando.nakadi.client.model.BusinessEvent + +object SimpleModelFactory { + + // + import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport + import spray.json.DefaultJsonProtocol._ + + //Simple bjects composed out of scalar typers only (without dependency to other Object-models) + val eventMetadata = new EventMetadata("eid", "eventType", "occurredAt", "receivedAt", List("parentEids"), "flowId", Map("key" -> "value")) + val problem = new Problem("problemType", "title", 312, "detail", "instance") + val metrics = new Metrics("metrics") + val partition = new Partition("partition", "oldestAvailableOffset", "newestAvailableOffset") + val cursor = new Cursor("partition", "offset") + val eventTypeSchema = new EventTypeSchema("schemaType", "schema") + val eventValidationStrategy = new EventValidationStrategy("name", "doc") + val partitionResolutionStrategy = new PartitionResolutionStrategy("name", "doc") + val eventEnrichmentStrategy = new EventEnrichmentStrategy("name", "doc") + + //Complex objects + val businessEvent = new BusinessEvent(eventMetadata) + val dataChangeEventQualifier = new DataChangeEventQualifier("dataType", DataOperation.CREATE) + val dataChangeEvent = new DataChangeEvent(metrics, dataChangeEventQualifier, eventMetadata) + + val eventType = new EventType("name", "owner", "category", "effectiveSchema", List("validationStrategies"), List("enrichmentStrategies"), partitionResolutionStrategy, eventTypeSchema, List("dataKeyFields"), + List("partitioningKeyFields")) + val event = new Event(eventType, true, "title") + val eventStreamBatch = new EventStreamBatch(cursor, List(event, event)) + +} \ No newline at end of file diff --git a/client/src/test/scala/org/zalando/nakadi/client/util/TestData.scala b/client/src/test/scala/org/zalando/nakadi/client/util/TestData.scala new file mode 100644 index 0000000..f88520c --- /dev/null +++ b/client/src/test/scala/org/zalando/nakadi/client/util/TestData.scala @@ -0,0 +1,26 @@ +package org.zalando.nakadi.client.util + +object TestData { + val events = """{ + "name": "article", + "owning_application": "article-producer", + "category": "data", + "partition_resolution_strategy": null, + "partitioning_key_fields": [], + "schema": { + "type": "json_schema", + "schema": "{\"Article\": { \"properties\": { \"sku\": { \"type\": \"string\" } } }}" + } + }, + { + "name": "myarticle", + "owning_application": "article-producer", + "category": "data", + "partition_resolution_strategy": null, + "partitioning_key_fields": [], + "schema": { + "type": "json_schema", + "schema": "{\"Article\": { \"properties\": { \"sku\": { \"type\": \"string\" } } }}" + } + }""" +} \ No newline at end of file diff --git a/model/src/main/scala/org/zalando/nakadi/client/model/Model.scala b/model/src/main/scala/org/zalando/nakadi/client/model/Model.scala index f8c88ad..1d042dc 100644 --- a/model/src/main/scala/org/zalando/nakadi/client/model/Model.scala +++ b/model/src/main/scala/org/zalando/nakadi/client/model/Model.scala @@ -18,8 +18,6 @@ case class Event( additionalProperties: Boolean, title: String) - - /** * Metadata for this Event. Contains commons fields for both Business and DataChange Events. Most are enriched by Nakadi upon reception, but they in general MIGHT be set by the client. * @param eid Identifier of this Event. Clients are allowed to generate this and this SHOULD be guaranteed to be unique from the perspective of the producer. Consumers MIGHT use this value to assert uniqueness of reception of the Event. @@ -82,7 +80,7 @@ case class Problem( detail: String, instance: String) -case class Metrics(metrics:String) //TODO: It is not defined yet! +case class Metrics(metrics: String) //TODO: It is not defined yet! /** * Partition information. Can be helpful when trying to start a stream using an unmanaged API. This information is not related to the state of the consumer clients. @@ -189,4 +187,4 @@ case object DataOperation extends Enumeration { } - + \ No newline at end of file diff --git a/project/Build.scala b/project/Build.scala index 46d5763..d003e7f 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -8,6 +8,8 @@ object NakadiClient extends Build { EclipseKeys.createSrc := EclipseCreateSrc.Default + EclipseCreateSrc.Resource +EclipseKeys.withSource := true + val defaultOptions= Seq( "-deprecation", // Emit warning and location for usages of deprecated APIs. "-feature", // Emit warning and location for usages of features that should be imported explicitly. diff --git a/project/Dependencies.scala b/project/Dependencies.scala index 48cca4a..2f84477 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -2,8 +2,9 @@ import sbt._ object Dependencies { + val akkaVersion = "2.4.2" + val clientDeps = { - val akkaVersion = "2.4.2" Seq( "com.typesafe.akka" %% "akka-http-spray-json-experimental" % akkaVersion withSources() withJavadoc(), "com.typesafe.akka" %% "akka-actor" % akkaVersion withSources() withJavadoc(), @@ -13,6 +14,7 @@ object Dependencies { "com.typesafe" % "config" % "1.3.0" withSources() withJavadoc(), "com.google.guava" % "guava" % "19.0" withSources() withJavadoc(), "com.typesafe.scala-logging" %% "scala-logging" % "3.1.0" withSources() withJavadoc(), + "io.spray" %% "spray-json" % "1.3.2" withSources() withJavadoc(), "com.fasterxml.jackson.core" % "jackson-core" % "2.7.0" withSources() withJavadoc(), "com.fasterxml.jackson.module" %% "jackson-module-scala" % "2.6.3" withSources() withJavadoc(), "ch.qos.logback" % "logback-classic" % "1.1.3" withSources() withJavadoc(), diff --git a/project/plugins.sbt b/project/plugins.sbt index 0cbdf2f..ab40f29 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,3 +1,9 @@ resolvers += "Typesafe repository" at "https://repo.typesafe.com/typesafe/releases/" addSbtPlugin("com.typesafe.sbt" % "sbt-git" % "0.8.5") + +addSbtPlugin("org.scoverage" % "sbt-scoverage" % "1.0.4") + +addSbtPlugin("org.scoverage" % "sbt-coveralls" % "1.0.0") + +addSbtPlugin("com.codacy" % "sbt-codacy-coverage" % "1.1.0") From 922488f012ed3f812eb6a468f6d0828f4c138703 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Wed, 30 Mar 2016 16:24:13 +0200 Subject: [PATCH 006/183] LAAS-60 Finished implementing the Default Serializers/Deserializers fixes #31, #35 --- client/src/main/scala/org/zalando/nakadi/client/Client.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/main/scala/org/zalando/nakadi/client/Client.scala b/client/src/main/scala/org/zalando/nakadi/client/Client.scala index d1f95bb..84526e1 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/Client.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/Client.scala @@ -210,7 +210,7 @@ private[client] class ClientImpl(connection: Connection) extends Client { object Main extends App with DefaultMarshaller { val host = "nakadi-sandbox.aruha-test.zalan.do" - val OAuth2Token = () => "3ba41b94-a30a-4dee-a90e-237f4e77edde" + val OAuth2Token = () => "" val port = 443 val client = new ClientImpl(Connection.newConnection(host, port, OAuth2Token, true, false)) From d2442f2d6ab6c59e37f1e0d68e3411acfed1dbce Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Wed, 30 Mar 2016 18:31:00 +0200 Subject: [PATCH 007/183] LAAS-60 Updating model to Nakadi's latest changes in Default branch (latest commit=7839be3) --- .../zalando/nakadi/client/model/Model.scala | 100 ++++++++++++++++-- 1 file changed, 91 insertions(+), 9 deletions(-) diff --git a/model/src/main/scala/org/zalando/nakadi/client/model/Model.scala b/model/src/main/scala/org/zalando/nakadi/client/model/Model.scala index 1d042dc..75ae86d 100644 --- a/model/src/main/scala/org/zalando/nakadi/client/model/Model.scala +++ b/model/src/main/scala/org/zalando/nakadi/client/model/Model.scala @@ -1,5 +1,8 @@ package org.zalando.nakadi.client.model +// Updated untill commit 7839be3 +// Compare + /** * The Event definition will be externalized in future versions of this document. * A basic payload of an Event. The actual schema is dependent on the information configured for the @@ -126,7 +129,7 @@ case class EventStreamBatch( case class EventType( name: String, owner: String, - category: String, + category: EventTypeCategory.Value, effectiveSchema: String, validationStrategies: Seq[String], enrichmentStrategies: Seq[String], @@ -136,14 +139,31 @@ case class EventType( partitioningKeyFields: Seq[String]) /** - * The schema for an EventType, expected to be a json schema in yaml format (other formats might be added in the future). + * The schema for an EventType, expected to be a json schema in yaml + * format (other formats might be added in the future). * @param type The type of schema definition (avro, json-schema, etc). - * @param schema The schema as string in the syntax defined in the field type. Failure to respect the syntax will fail any operation on an EventType. + * @param schema The schema as string in the syntax defined in the field type. + * Failure to respect the syntax will fail any operation on an EventType. */ case class EventTypeSchema( schemaType: String, schema: String) +/** + * Operational statistics for an EventType. This data is generated by Nakadi + * based on the runtime and might be used to guide changes in internal parameters. + * @param expectedWriteRate - Write rate for events of this EventType. This rate encompasses all producers of this EventType for a Nakadi cluster. Measured in kb/minutes. + * @param messageSize - Average message size for each Event of this EventType. Includes in the count the whole serialized form of the event, including metadata. Measured in bytes. + * @param readParallelism - Amount of parallel readers (consumers) to this EventType. + * @param writeParallelism - Amount of parallel writers (producers) to this EventType. + * + */ +case class EventTypeStatistics( + expectedWriteRate: Int, + messageSize: Int, + readParallelism: Int, + writeParallelism: Int) + /** * Defines a rule for validation of an incoming Event. Rules might require additional parameters; see the `doc` field of the existing rules for details. See GET /registry/validation-strategies for a list of available rules. * @param name Name of the strategy. @@ -170,13 +190,31 @@ case class PartitionResolutionStrategy( case class EventEnrichmentStrategy( name: String, doc: String) +/** + * A status corresponding to one individual Event's publishing attempt. + * @param eid Eid of the corresponding item. Will be absent if missing on the incoming Event. + * @param publishingStatus Indicator of the submission of the Event within a Batch. - SUBMITTED indicates successful submission, including commit on he underlying broker. - FAILED indicates the message submission was not possible and can be resubmitted if so desired. - ABORTED indicates that the submission of this item was not attempted any further due to a failure on another item in the batch. + * @param step Indicator of the step in the pulbishing process this Event reached. In Items that FAILED means the step of the failure. - NONE indicates that nothing was yet attempted for the publishing of this Event. Should be present only in the case of aborting the publishing during the validation of another (previous) Event. - VALIDATING, ENRICHING, PARTITIONING and PUBLISHING indicate all the corresponding steps of the publishing process. + * @param detail Human readable information about the failure on this item. Items that are not SUBMITTED should have a description. + * + */ +case class BatchItemResponse( + eid: String, + publishingStatus: BatchItemPublishingStatus.Value, + step: BatchItemStep.Value, + detail: String) + +///////////////////////////////// +// ENUMS //////////////////////// +///////////////////////////////// /** - * Identifier for the type of operation when conducting an Entity. - * @param CREATE - * @param UPDATE - * @param DELETE - * @param SNAPSHOT + * Identifier for the type of operation to executed on the entity.
+ * C: Creation
+ * U: Update
+ * D: Deletion
+ * S: Snapshot

+ * Values = CREATE("C"), UPDATE("U"), DELETE("D"), SNAPSHOT("S") */ case object DataOperation extends Enumeration { type DataOperation = Value @@ -187,4 +225,48 @@ case object DataOperation extends Enumeration { } - \ No newline at end of file +/** + * Defines the category of an EventType.
+ * Values = UNDEFINED("undefined") DATA("data") BUSINESS("business") + */ +case object EventTypeCategory extends Enumeration { + type EventTypeCategory = Value + val UNDEFINED = Value("undefined") + val DATA = Value("data") + val BUSINESS = Value("business") + +} +/** + * Indicator of the submission of the Event within a Batch.
+ * - SUBMITTED indicates successful submission, including commit on he underlying broker.
+ * - FAILED indicates the message submission was not possible and can be resubmitted if so desired.
+ * - ABORTED indicates that the submission of this item was not attempted any further due to a failure + * on another item in the batch.

+ * Values = SUBMITTED("SUBMITTED") FAILED("FAILED") ABORTED("ABORTED") + */ +case object BatchItemPublishingStatus extends Enumeration { + type BatchItemPublishingStatus = Value + val SUBMITTED = Value("SUBMITTED") + val FAILED = Value("FAILED") + val ABORTED = Value("ABORTED") +} + +/** + * Indicator of the step in the pulbishing process this Event reached. + * In Items that FAILED means the step of the failure. + * - NONE indicates that nothing was yet attempted for the publishing of this Event. Should be present + * only in the case of aborting the publishing during the validation of another (previous) Event.
+ * - VALIDATING, ENRICHING, PARTITIONING and PUBLISHING indicate all the corresponding steps of the + * publishing process.

+ * Values = NONE("NONE"), VALIDATING("VALIDATING"), ENRICHING("ENRICHING"), PARTITIONING("PARTITIONING"), PUBLISHING("PUBLISHING") + */ +case object BatchItemStep extends Enumeration { + type BatchItemStep = Value + val NONE = Value("NONE") + val VALIDATING = Value("VALIDATING") + val ENRICHING = Value("ENRICHING") + val PARTITIONING = Value("PARTITIONING") + val PUBLISHING = Value("PUBLISHING") + +} + From c60bb19cf19a5b89d2dca25e0e943035bda6b055 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Sun, 3 Apr 2016 20:53:35 +0200 Subject: [PATCH 008/183] LAAS-60 Getting events is done --- .../org/zalando/nakadi/client/package.scala | 22 --- .../org/zalando/nakadi/client/Client.scala | 146 +++++++++++------- .../zalando/nakadi/client/Connection.scala | 19 ++- .../client/model/DefaultMarshaller.scala | 40 ----- .../client/model/JacksonJsonMarshaller.scala | 103 ++++++++++++ .../client/model/SprayJsonMarshaller.scala | 56 +++++++ .../org/zalando/nakadi/client/package.scala | 2 +- .../nakadi/client/utils/ParseHelper.scala | 45 +++++- .../scala/org/zalando/nakadi/client/App.scala | 17 ++ .../nakadi/client/DefaultMarshallerTest.scala | 88 ----------- .../client/DeserializerSerializerTest.scala | 31 ++++ .../client/SerializerDeserializerTest.scala | 91 +++++++++++ .../nakadi/client/util/AkkaConfig.scala | 11 ++ .../{TestData.scala => TestJsonEntity.scala} | 13 +- ...delFactory.scala => TestScalaEntity.scala} | 22 +-- .../zalando/nakadi/client/config/App.scala | 17 ++ .../nakadi/client/KlientIntegrationTest.scala | 51 ++++++ .../zalando/nakadi/client/model/Model.scala | 48 +++--- project/Build.scala | 16 ++ project/Configs.scala | 7 + project/Testing.scala | 35 +++++ 21 files changed, 614 insertions(+), 266 deletions(-) delete mode 100644 api/src/main/scala/org/zalando/nakadi/client/package.scala delete mode 100644 client/src/main/scala/org/zalando/nakadi/client/model/DefaultMarshaller.scala create mode 100644 client/src/main/scala/org/zalando/nakadi/client/model/JacksonJsonMarshaller.scala create mode 100644 client/src/main/scala/org/zalando/nakadi/client/model/SprayJsonMarshaller.scala create mode 100644 client/src/test/scala/org/zalando/nakadi/client/App.scala delete mode 100644 client/src/test/scala/org/zalando/nakadi/client/DefaultMarshallerTest.scala create mode 100644 client/src/test/scala/org/zalando/nakadi/client/DeserializerSerializerTest.scala create mode 100644 client/src/test/scala/org/zalando/nakadi/client/SerializerDeserializerTest.scala create mode 100644 client/src/test/scala/org/zalando/nakadi/client/util/AkkaConfig.scala rename client/src/test/scala/org/zalando/nakadi/client/util/{TestData.scala => TestJsonEntity.scala} (52%) rename client/src/test/scala/org/zalando/nakadi/client/util/{SimpleModelFactory.scala => TestScalaEntity.scala} (68%) create mode 100644 it/src/main/scala/org/zalando/nakadi/client/config/App.scala create mode 100644 it/src/test/scala/org/zalando/nakadi/client/KlientIntegrationTest.scala create mode 100644 project/Configs.scala create mode 100644 project/Testing.scala diff --git a/api/src/main/scala/org/zalando/nakadi/client/package.scala b/api/src/main/scala/org/zalando/nakadi/client/package.scala deleted file mode 100644 index 69126c7..0000000 --- a/api/src/main/scala/org/zalando/nakadi/client/package.scala +++ /dev/null @@ -1,22 +0,0 @@ -package org.zalando.nakadi - -package object client { - - - val URI_METRICS = "/metrics" - - /*Events*/ - val URI_EVENT_TYPES = "/event-types" - val URI_EVENT_TYPE_BY_NAME = "/event-types/%s" - val URI_EVENTS_OF_EVENT_TYPE = "/event-types/%s/events" - - /*Partitions*/ - val URI_PARTITIONS_OF_EVENT_TYPE = "/event-types/%s/partitions" - val URI_PARTITION_BY_NAME = "/event-types/%s/partitions/%s" - - /*Strategies*/ - val URI_VALIDATION_STRATEGIES = "/registry/validation-strategies" - val URI_ENRICHMENT_STRATEGIES = "/registry/enrichment-strategies" - val URI_PARTITIONING_STRATEGIES = "/registry/partitioning-strategies" - -} \ No newline at end of file diff --git a/client/src/main/scala/org/zalando/nakadi/client/Client.scala b/client/src/main/scala/org/zalando/nakadi/client/Client.scala index 84526e1..ad244da 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/Client.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/Client.scala @@ -10,11 +10,15 @@ import com.typesafe.scalalogging.Logger import akka.http.scaladsl.unmarshalling.Unmarshaller import akka.protobuf.ByteString import org.zalando.nakadi.client.model.EventType +import org.zalando.nakadi.client.model.PartitionResolutionStrategy import akka.http.scaladsl.unmarshalling._ -import org.zalando.nakadi.client.model.DefaultMarshaller import scala.concurrent.Await +import org.zalando.nakadi.client.model.Partition +import org.zalando.nakadi.client.model.EventEnrichmentStrategy +import org.zalando.nakadi.client.model.EventValidationStrategy trait Client { + import Client._ /** * Retrieve monitoring metrics @@ -23,7 +27,7 @@ trait Client { * curl --request GET /metrics * }}} */ - def metrics(): Future[HttpResponse] + def metrics(): Future[Either[ClientError, HttpResponse]] /** * Returns a list of all registered EventTypes. @@ -33,7 +37,7 @@ trait Client { * }}} * */ - def eventTypes()(implicit marshaller: FromEntityUnmarshaller[EventType], charset: String = "UTF-8"): Future[EventType] + def eventTypes()(implicit marshaller: FromEntityUnmarshaller[List[EventType]]): Future[Either[ClientError, List[EventType]]] /** * Creates a new EventType. @@ -45,7 +49,7 @@ trait Client { * @param event - The EventType to create. * */ - def newEventType(eventType: EventType): Future[Boolean] + def newEventType(eventType: EventType)(implicit marshaller: FromEntityUnmarshaller[EventType]): Future[Option[ClientError]] /** * Returns the EventType identified by its name. @@ -54,7 +58,7 @@ trait Client { * }}} * @param name - Name of the EventType */ - def eventType(name: String): Future[HttpResponse] + def eventType(name: String)(implicit marshaller: FromEntityUnmarshaller[EventType]): Future[Either[ClientError, EventType]] /** * Updates the EventType identified by its name. * {{{ @@ -63,7 +67,7 @@ trait Client { * @param name - Name of the EventType * @param event - Event to update */ - def updateEventType(name: String, event: Any): Future[HttpResponse] + def updateEventType(name: String, eventType: EventType)(implicit marshaller: FromEntityUnmarshaller[EventType]): Future[Option[ClientError]] /** * Deletes an EventType identified by its name. * @@ -73,7 +77,7 @@ trait Client { * * @param name - Name of the EventType */ - def deleteEventType(name: String): Future[HttpResponse] + def deleteEventType(name: String): Future[Option[ClientError]] /** * Creates a new Event for the given EventType. @@ -83,7 +87,7 @@ trait Client { * @param name - Name of the EventType * @param event - Event to create */ - def newEvent(name: String, event: Any): Future[HttpResponse] + def newEvent[T](name: String, event: T)(implicit marshaller: FromEntityUnmarshaller[T]): Future[Option[ClientError]] /** * Request a stream delivery for the specified partitions of the given EventType. @@ -93,7 +97,7 @@ trait Client { * @param name - Name of the EventType * */ - def events(name: String): Future[HttpResponse] + def events[T](name: String)(implicit marshaller: FromEntityUnmarshaller[T]): Future[Either[ClientError, T]] /** * List the partitions for the given EventType. @@ -102,7 +106,7 @@ trait Client { * }}} * @param name - Name of the EventType */ - def partitions(name: String): Future[HttpResponse] + def partitions(name: String)(implicit marshaller: FromEntityUnmarshaller[Partition]): Future[Either[ClientError, Partition]] /** * Returns the Partition for the given EventType. @@ -113,7 +117,7 @@ trait Client { * @param partition - Name of the partition */ - def partitionByName(name: String, partition: String): Future[HttpResponse] + def partitionByName(name: String, partition: String)(implicit marshaller: FromEntityUnmarshaller[Partition]): Future[Either[ClientError, Partition]] /** * Returns all of the validation strategies supported by this installation of Nakadi. @@ -122,7 +126,7 @@ trait Client { * curl --request GET /registry/validation-strategies * }}} */ - def validationStrategies(): Future[HttpResponse] + def validationStrategies()(implicit marshaller: FromEntityUnmarshaller[EventValidationStrategy]): Future[Either[ClientError, EventValidationStrategy]] /** * Returns all of the enrichment strategies supported by this installation of Nakadi. @@ -131,7 +135,7 @@ trait Client { * }}} */ - def enrichmentStrategies(): Future[HttpResponse] + def enrichmentStrategies()(implicit marshaller: FromEntityUnmarshaller[EventEnrichmentStrategy]): Future[Either[ClientError, EventEnrichmentStrategy]] /** * Returns all of the partitioning strategies supported by this installation of Nakadi. @@ -139,7 +143,7 @@ trait Client { * curl --request GET /registry/partitioning-strategies * }}} */ - def partitionStrategies(): Future[HttpResponse] + def partitionStrategies()(implicit marshaller: FromEntityUnmarshaller[List[PartitionResolutionStrategy]]): Future[Either[ClientError, List[PartitionResolutionStrategy]]] /** * Shuts down the communication system of the client @@ -150,73 +154,97 @@ trait Client { } object Client { - case class ClientError(msg: String) + case class ClientError(msg: String, status: Option[Int]) } -private[client] class ClientImpl(connection: Connection) extends Client { +private[client] class ClientImpl(connection: Connection, charSet: String) extends Client { + import Client._ implicit val materializer = connection.materializer val logger = Logger(LoggerFactory.getLogger(this.getClass)) - def metrics(): Future[HttpResponse] = ??? + def metrics(): Future[Either[ClientError, HttpResponse]] = ??? - def eventTypes()(implicit marshaller: FromEntityUnmarshaller[EventType], charset: String = "UTF-8"): Future[EventType] = { - connection.get(URI_EVENT_TYPES).flatMap(evaluateResponse(_)) + def eventTypes()(implicit marshaller: FromEntityUnmarshaller[List[EventType]]): Future[Either[ClientError, List[EventType]]] = { + logGetError(connection.get(URI_EVENT_TYPES).flatMap(handleGetResponse(_))) } - def evaluateResponse[T](response: HttpResponse)(implicit unmarshaller: FromEntityUnmarshaller[T], m: Manifest[T], charset: String = "UTF-8"): Future[T] = { - logger.debug("received [response={}]", response) - response.status match { - case status if (status.isSuccess()) => - logger.info("Result is successfull ({}) ", status.intValue().toString()) - Unmarshal(response.entity).to[T] - case status if (status.isRedirection()) => - logger.info("Result is a redirect with http-status ({}) ", status.intValue().toString()) - //TODO: Implement logic for following redirects - Unmarshal(response.entity).to[T].map { x => - logger.info("##### >>>>> ") - x - } - - case status if (status.isFailure()) => - logger.warn("An error occurred with http-status ({}) and reason:{} ", status.reason(), status.intValue().toString()) - Unmarshal(response.entity).to[T] - } + def newEventType(eventType: EventType)(implicit marshaller: FromEntityUnmarshaller[EventType]): Future[Option[ClientError]] = { + logPostError(connection.post(URI_EVENT_TYPES).map(handlePostResponse(_))) } - def newEventType(eventType: EventType): Future[Boolean] = ??? - def eventType(name: String): Future[HttpResponse] = ??? + def eventType(name: String)(implicit marshaller: FromEntityUnmarshaller[EventType]): Future[Either[ClientError, EventType]] = ??? - def updateEventType(name: String, event: Any): Future[HttpResponse] = ??? + def updateEventType(name: String, eventType: EventType)(implicit marshaller: FromEntityUnmarshaller[EventType]): Future[Option[ClientError]] = ??? - def deleteEventType(name: String): Future[HttpResponse] = ??? + def deleteEventType(name: String): Future[Option[ClientError]] = ??? - def newEvent(name: String, event: Any): Future[HttpResponse] = ??? + def newEvent[T](name: String, event: T)(implicit marshaller: FromEntityUnmarshaller[T]): Future[Option[ClientError]] = ??? - def events(name: String): Future[HttpResponse] = ??? + def events[T](name: String)(implicit marshaller: FromEntityUnmarshaller[T]): Future[Either[ClientError, T]] = ??? - def partitions(name: String): Future[HttpResponse] = ??? + def partitions(name: String)(implicit marshaller: FromEntityUnmarshaller[Partition]): Future[Either[ClientError, Partition]] = ??? - def partitionByName(name: String, partition: String): Future[HttpResponse] = ??? + def partitionByName(name: String, partition: String)(implicit marshaller: FromEntityUnmarshaller[Partition]): Future[Either[ClientError, Partition]] = ??? - def validationStrategies(): Future[HttpResponse] = ??? + def validationStrategies()(implicit marshaller: FromEntityUnmarshaller[EventValidationStrategy]): Future[Either[ClientError, EventValidationStrategy]] = ??? - def enrichmentStrategies(): Future[HttpResponse] = ??? + def enrichmentStrategies()(implicit marshaller: FromEntityUnmarshaller[EventEnrichmentStrategy]): Future[Either[ClientError, EventEnrichmentStrategy]] = ??? - def partitionStrategies(): Future[HttpResponse] = ??? + def partitionStrategies()(implicit marshaller: FromEntityUnmarshaller[List[PartitionResolutionStrategy]]): Future[Either[ClientError, List[PartitionResolutionStrategy]]] = + logGetError(connection.get(URI_PARTITIONING_STRATEGIES).flatMap(handleGetResponse(_))) def stop(): Future[Terminated] = connection.stop() -} + //#################### + //# HELPER METHODS # + //#################### -object Main extends App with DefaultMarshaller { - val host = "nakadi-sandbox.aruha-test.zalan.do" - val OAuth2Token = () => "" - val port = 443 - val client = new ClientImpl(Connection.newConnection(host, port, OAuth2Token, true, false)) + def logGetError[A, T](future: Future[Either[ClientError, T]]): Future[Either[ClientError, T]] = { + future recover { + case e: Throwable => + logger.error("A unexpected error occured", e) + Left(ClientError("Error: " + e.getMessage, None)) + } + } + def logPostError(future: Future[Option[ClientError]]): Future[Option[ClientError]] = { + future recover { + case e: Throwable => + logger.error("A unexpected error occured", e) + Option(ClientError("Error: " + e.getMessage, None)) + } + } + + def handleGetResponse[T](response: HttpResponse)(implicit unmarshaller: FromEntityUnmarshaller[T], m: Manifest[T]): Future[Either[ClientError, T]] = { + logger.debug("received [response={}]", response) + response.status match { + case status if (status.isSuccess()) => + logger.info("Result is successfull ({}) ", status.intValue().toString()) + Unmarshal(response.entity).to[T].map(Right(_)) + case status if (status.isRedirection()) => + val msg = "An error occurred with http-status (" + status.intValue() + "}) and reason:" + status.reason() + logger.info(msg) + Future.successful(Left(ClientError(msg, Some(status.intValue())))) + case status if (status.isFailure()) => + val msg = "An error occurred with http-status (" + status.intValue() + "}) and reason:" + status.reason() + logger.warn(msg) + Future.successful(Left(ClientError(msg, Some(status.intValue())))) + } + } + def handlePostResponse[T](response: HttpResponse): Option[ClientError] = { + logger.debug("received [response={}]", response) + response.status match { + case status if (status.isSuccess()) => + None + case status if (status.isRedirection()) => + val msg = "An error occurred with http-status (" + status.intValue() + "}) and reason:" + status.reason() + logger.info(msg) + Option(ClientError(msg, Some(status.intValue()))) + case status if (status.isFailure()) => + val msg = "An error occurred with http-status (" + status.intValue() + "}) and reason:" + status.reason() + logger.warn(msg) + Option(ClientError(msg, Some(status.intValue()))) + } + } - val response = client.eventTypes() - Await.result(response, 10.second) - response.map(r => - println("######################## " + r)) +} -} \ No newline at end of file diff --git a/client/src/main/scala/org/zalando/nakadi/client/Connection.scala b/client/src/main/scala/org/zalando/nakadi/client/Connection.scala index 7157d82..c4a27ab 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/Connection.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/Connection.scala @@ -15,6 +15,7 @@ import akka.actor.{ ActorSystem, Terminated } import akka.http.scaladsl.{ Http, HttpsConnectionContext } import akka.http.scaladsl.model.{ HttpRequest, HttpResponse, MediaRange } import akka.http.scaladsl.model.MediaTypes.`application/json` +import akka.http.scaladsl.model.HttpMethods.POST import akka.http.scaladsl.model.Uri.apply import akka.http.scaladsl.model.headers import akka.http.scaladsl.model.headers.OAuth2BearerToken @@ -24,6 +25,7 @@ import javax.net.ssl.{ SSLContext, TrustManager, X509TrustManager } trait Connection { def get(endpoint: String): Future[HttpResponse] + def post(endpoint: String): Future[HttpResponse] def stop(): Future[Terminated] def materializer(): ActorMaterializer } @@ -81,7 +83,16 @@ private[client] class ConnectionImpl(host: String, port: Int, tokenProvider: () def get(endpoint: String): Future[HttpResponse] = { logger.info("Calling {}", endpoint) val response: Future[HttpResponse] = - Source.single(DefaultHttpRequest(endpoint)) + Source.single(GetHttpRequest(endpoint)) + .via(connectionFlow). + runWith(Sink.head) + logError(response) + response + } + def post(endpoint: String): Future[HttpResponse] = { + logger.info("Calling {}", endpoint) + val response: Future[HttpResponse] = + Source.single(PostHttpRequest(endpoint)) .via(connectionFlow). runWith(Sink.head) logError(response) @@ -94,10 +105,14 @@ private[client] class ConnectionImpl(host: String, port: Int, tokenProvider: () } } - private def DefaultHttpRequest(url: String): HttpRequest = { + private def GetHttpRequest(url: String): HttpRequest = { HttpRequest(uri = url).withHeaders(headers.Authorization(OAuth2BearerToken(tokenProvider())), headers.Accept(MediaRange(`application/json`))) } + private def PostHttpRequest(url: String): HttpRequest = { + HttpRequest(uri = url, method = POST).withHeaders(headers.Authorization(OAuth2BearerToken(tokenProvider())), + headers.Accept(MediaRange(`application/json`))) + } def stop(): Future[Terminated] = actorSystem.terminate() } diff --git a/client/src/main/scala/org/zalando/nakadi/client/model/DefaultMarshaller.scala b/client/src/main/scala/org/zalando/nakadi/client/model/DefaultMarshaller.scala deleted file mode 100644 index 213248d..0000000 --- a/client/src/main/scala/org/zalando/nakadi/client/model/DefaultMarshaller.scala +++ /dev/null @@ -1,40 +0,0 @@ -package org.zalando.nakadi.client.model - -import org.zalando.nakadi.client.utils.ParseHelper - -import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport -import akka.http.scaladsl.marshalling.Marshaller -import akka.http.scaladsl.unmarshalling.Unmarshaller -import spray.json.{ DefaultJsonProtocol, JsonFormat, JsonReader, JsonWriter, pimpAny, pimpString } - -trait DefaultMarshaller extends SprayJsonSupport with DefaultJsonProtocol { - import org.zalando.nakadi.client.utils.ParseHelper._ - - implicit val eventMetadataFormatter = jsonFormat7(EventMetadata) - implicit val problemFormatter = jsonFormat5(Problem) - implicit val metricsFormatter = jsonFormat1(Metrics) - implicit val partitionFormatter = jsonFormat3(Partition) - implicit val cursorFormatter = jsonFormat2(Cursor) - implicit val dataChangeEventQualifierFormatter = jsonFormat2(DataChangeEventQualifier) - implicit val partitionResolutionStrategyFormatter = jsonFormat2(PartitionResolutionStrategy) - implicit val businessEventFormatter = jsonFormat1(BusinessEvent) - implicit val eventTypeSchemaFormatter = jsonFormat2(EventTypeSchema) - implicit val eventValidationStrategyFormatter = jsonFormat2(EventValidationStrategy) - implicit val eventEnrichmentStrategyFormatter = jsonFormat2(EventEnrichmentStrategy) - implicit val eventTypeFormatter = jsonFormat10(EventType) - implicit val eventFormatter = jsonFormat3(Event) - implicit val eventStreamBatchFormatter = jsonFormat2(EventStreamBatch) - - implicit def dataChangeEventFormatter[A: JsonFormat] = jsonFormat3(DataChangeEvent.apply[A]) - - //Handy marshallers - implicit def toStringMarshaller[T](implicit writer: JsonWriter[T]): Marshaller[T, String] = - Marshaller.opaque { - _.toJson(writer).toString() - } - - implicit def fromEntityUnmarshaller[T](implicit reader: JsonReader[T]): Unmarshaller[String, T] = Unmarshaller.strict { - input => input.parseJson.convertTo[T] - } - -} \ No newline at end of file diff --git a/client/src/main/scala/org/zalando/nakadi/client/model/JacksonJsonMarshaller.scala b/client/src/main/scala/org/zalando/nakadi/client/model/JacksonJsonMarshaller.scala new file mode 100644 index 0000000..2683eea --- /dev/null +++ b/client/src/main/scala/org/zalando/nakadi/client/model/JacksonJsonMarshaller.scala @@ -0,0 +1,103 @@ +package org.zalando.nakadi.client.model + +import scala.annotation.implicitNotFound +import scala.concurrent.{ ExecutionContext, Future } +import scala.reflect.Manifest +import org.slf4j.LoggerFactory +import com.fasterxml.jackson.core.{ JsonFactory, JsonParser } +import com.fasterxml.jackson.core.`type`.TypeReference +import com.fasterxml.jackson.databind.{ DeserializationContext, DeserializationFeature, JsonDeserializer, ObjectMapper, PropertyNamingStrategy, SerializationFeature } +import com.fasterxml.jackson.databind.deser.DeserializationProblemHandler +import com.fasterxml.jackson.module.scala.DefaultScalaModule +import com.typesafe.scalalogging.Logger +import akka.http.scaladsl.marshalling.Marshaller +import akka.http.scaladsl.model.HttpEntity +import akka.http.scaladsl.unmarshalling.Unmarshaller +import akka.stream.ActorMaterializer +import javax.annotation.PostConstruct +import com.fasterxml.jackson.databind.JsonSerializer +import com.fasterxml.jackson.core.JsonGenerator +import com.fasterxml.jackson.databind.SerializerProvider +trait JacksonJsonMarshaller { + import JacksonJsonMarshaller._ + val factory = new JsonFactory(); + + class DataOperationType extends TypeReference[DataOperation.type] + class EventTypeCategoryType extends TypeReference[EventTypeCategory.type] + class BatchItemStepType extends TypeReference[BatchItemStep.type] + class BatchItemPublishingStatusType extends TypeReference[BatchItemPublishingStatus.type] + + implicit val DataOperationTypeTypeReference = new TypeReference[DataOperationType] {} + implicit val EventTypeCategoryTypeTypeReference = new TypeReference[EventTypeCategoryType] {} + implicit val BatchItemStepTypeTypeReference = new TypeReference[BatchItemStepType] {} + implicit val BatchItemPublishingStatusTypeTypeReference = new TypeReference[BatchItemPublishingStatusType] {} + + implicit val problemTypeReference = new TypeReference[Problem] {} + implicit val metricsTypeReference = new TypeReference[Metrics] {} + implicit val partitionTypeReference = new TypeReference[Partition] {} + implicit val cursorTypeReference = new TypeReference[Cursor] {} + implicit val eventTypeSchemaTypeReference = new TypeReference[EventTypeSchema] {} + implicit val eventValidationStrategyTypeReference = new TypeReference[EventValidationStrategy] {} + implicit val partitionResolutionStrategyTypeReference = new TypeReference[PartitionResolutionStrategy] {} + implicit val eventEnrichmentStrategyTypeReference = new TypeReference[EventEnrichmentStrategy] {} + implicit val dataChangeEventQualifierTypeReference = new TypeReference[DataChangeEventQualifier] {} + implicit val eventTypeStatisticsTypeReference = new TypeReference[EventTypeStatistics] {} + implicit val eventTypeTypeReference = new TypeReference[EventType] {} + implicit val eventTypeReference = new TypeReference[Event] {} + implicit val eventStreamBatchTypeReference = new TypeReference[EventStreamBatch] {} + implicit val eventMetadataTypeReference = new TypeReference[EventMetadata] {} + implicit val businessEventTypeReference = new TypeReference[BusinessEvent] {} + implicit val batchItemResponseTypeReference = new TypeReference[BatchItemResponse] {} + + implicit def dataChangeEventTypeReference[T](implicit expectedType: TypeReference[T]) = { new TypeReference[DataChangeEvent[T]] {} } + + implicit def unmarshaller[T: Manifest](implicit ec: ExecutionContext, am: ActorMaterializer, expectedType: TypeReference[T]): Unmarshaller[HttpEntity, Future[T]] = Unmarshaller.strict { + input => Unmarshaller.byteArrayUnmarshaller(input).map(objectMapper.readValue(_, expectedType)) + } + + implicit def objectToStringMarshaller[T]: Marshaller[T, String] = Marshaller.opaque { + input => objectMapper.writeValueAsString(input) + } + + implicit def stringToObjectUnmarshaller[T](implicit expectedType: TypeReference[T]): Unmarshaller[String, T] = Unmarshaller.strict { + input => objectMapper.readValue[T](input, expectedType) + } + +} + +private[model] object JacksonJsonMarshaller { + val logger = Logger(LoggerFactory.getLogger(JacksonJsonMarshaller.getClass)) + def objectMapper(): ObjectMapper = { + val m = new ObjectMapper { + @PostConstruct + def customConfiguration() { + // Uses Enum.toString() for serialization of an Enum + this.enable(SerializationFeature.WRITE_ENUMS_USING_TO_STRING); + // Uses Enum.toString() for deserialization of an Enum + this.enable(DeserializationFeature.READ_ENUMS_USING_TO_STRING); + } + + } + m.registerModule(new DefaultScalaModule) + m.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) + m.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + m.setPropertyNamingStrategy(PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES) + m.addHandler(new DeserializationProblemHandler() { + override def handleUnknownProperty(ctxt: DeserializationContext, + jp: JsonParser, deserializer: JsonDeserializer[_], + beanOrClass: AnyRef, + propertyName: String): Boolean = { + logger.warn(s"unknown property occurred in JSON representation: [beanOrClass=$beanOrClass, property=$propertyName]") + true + } + }) + m + } + + def enumSerizalizer[T <: Enumeration]() = new JsonSerializer[T#Value] { + override def serialize(value: T#Value, jgen: JsonGenerator, provider: SerializerProvider): Unit = { + jgen.writeString(value.toString()) + } + } + +} \ No newline at end of file diff --git a/client/src/main/scala/org/zalando/nakadi/client/model/SprayJsonMarshaller.scala b/client/src/main/scala/org/zalando/nakadi/client/model/SprayJsonMarshaller.scala new file mode 100644 index 0000000..a792cd9 --- /dev/null +++ b/client/src/main/scala/org/zalando/nakadi/client/model/SprayJsonMarshaller.scala @@ -0,0 +1,56 @@ +package org.zalando.nakadi.client.model + +import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport +import akka.http.scaladsl.marshalling.Marshaller +import akka.http.scaladsl.unmarshalling.Unmarshaller +import spray.json.{ DefaultJsonProtocol, JsValue, JsonFormat, JsonReader, JsonWriter, pimpAny, pimpString } +import spray.json._ +import org.zalando.nakadi.client.utils.ParseHelper + +trait SprayJsonMarshaller extends SprayJsonSupport with DefaultJsonProtocol { + import ParseHelper._ + case class Test(subjectDescription: String) + implicit val testFormatter = jsonFormat1(Test) + //Enums + implicit val dataOperationEnumFormatter = jsonEnumFormat(DataOperation) + implicit val eventTypeCategoryEnumFormatter = jsonEnumFormat(EventTypeCategory) + implicit val batchItemStepjsonEnumFormat = jsonEnumFormat(BatchItemStep) + implicit val BatchItemPublishingStatusEnumFormat = jsonEnumFormat(BatchItemPublishingStatus) + + implicit val problemFormatter = jsonFormat(Problem, "problem_type", "title", "status", "detail", "instance") + implicit val metricsFormatter = jsonFormat1(Metrics) + implicit val partitionFormatter = jsonFormat(Partition, "partition", "oldest_available_offset", "newest_available_offset") + implicit val cursorFormatter = jsonFormat2(Cursor) + implicit val dataChangeEventQualifierFormatter = jsonFormat(DataChangeEventQualifier, "data_type", "data_operation") + implicit val partitionResolutionStrategyFormatter = jsonFormat2(PartitionResolutionStrategy) + implicit val eventTypeSchemaFormatter = jsonFormat(EventTypeSchema, "schema_type", "schema") + implicit val eventTypeStatisticsFormatter = jsonFormat(EventTypeStatistics, "expected_write_rate", "message_size", "read_parallelism", "write_parallelism") + implicit val eventValidationStrategyFormatter = jsonFormat2(EventValidationStrategy) + implicit val eventTypeFormatter = jsonFormat(EventType, "name", "owning_application", "category", "validation_strategies", "enrichment_strategies", "partition_resolution_strategy", "schema", "data_key_fields", "partitioning_key_fields", "statistics") + implicit val eventMetadataFormatter = jsonFormat(EventMetadata, "eid", "event_type", "occurred_at", "received_at", "parent_eids", "flow_id", "partition", "metadata") + implicit val businessEventFormatter = jsonFormat1(BusinessEvent) + implicit val eventEnrichmentStrategyFormatter = jsonFormat2(EventEnrichmentStrategy) + implicit val eventFormatter = jsonFormat(Event,"event_type","additional_properties","title") + implicit val eventStreamBatchFormatter = jsonFormat2(EventStreamBatch) + implicit val batchItemResponseFormatter = jsonFormat(BatchItemResponse,"eid","publishing_status","step","detail") + + implicit def dataChangeEventFormatter[A: JsonFormat] = jsonFormat3(DataChangeEvent.apply[A]) + + //Custom Marshaller/Unmarshaller + implicit def entityToStringMarshaller[T](implicit writer: JsonWriter[T]): Marshaller[T, String] = Marshaller.opaque { + _.toJson(writer).toString() + } + + implicit def stringToEntityUnmarshaller[T](implicit reader: JsonReader[T]): Unmarshaller[String, T] = Unmarshaller.strict { + input => input.parseJson.convertTo[T] + } + + implicit def entityToJsValueMarshaller[T](implicit writer: JsonWriter[T]): Marshaller[T, JsValue] = Marshaller.opaque { + _.toJson(writer) + } + + implicit def jsValueToEntityUnmarshaller[T](implicit reader: JsonReader[T]): Unmarshaller[JsValue, T] = Unmarshaller.strict { + input => input.convertTo[T] + } + +} \ No newline at end of file diff --git a/client/src/main/scala/org/zalando/nakadi/client/package.scala b/client/src/main/scala/org/zalando/nakadi/client/package.scala index 69126c7..968814e 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/package.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/package.scala @@ -17,6 +17,6 @@ package object client { /*Strategies*/ val URI_VALIDATION_STRATEGIES = "/registry/validation-strategies" val URI_ENRICHMENT_STRATEGIES = "/registry/enrichment-strategies" - val URI_PARTITIONING_STRATEGIES = "/registry/partitioning-strategies" + val URI_PARTITIONING_STRATEGIES = "/registry/partition-strategies" } \ No newline at end of file diff --git a/client/src/main/scala/org/zalando/nakadi/client/utils/ParseHelper.scala b/client/src/main/scala/org/zalando/nakadi/client/utils/ParseHelper.scala index 7155444..419254c 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/utils/ParseHelper.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/utils/ParseHelper.scala @@ -39,20 +39,49 @@ object ParseHelper { case v => deserializationError("Unexpected value within JsObject: " + v) } } - - implicit object DataOperationFormat extends JsonFormat[DataOperation.Value] { - def write(in: DataOperation.Value) = JsString(in.toString()) - def read(json: JsValue) = json match { - case JsString(s) => DataOperation.withName(s) + + + def jsonEnumFormat[T <: Enumeration](enum: T) = new JsonFormat[T#Value] { + def write(in: T#Value) = JsString(in.toString()) + def read(json: JsValue) = json match { + case JsString(s) => enum.withName(s) case somethingElse => deserializationError("Unexpected value four our Enumerator: " + somethingElse) } } - - implicit def listFormat[T :JsonFormat] = new JsonFormat[T] { + + implicit def listFormat[T: JsonFormat] = new JsonFormat[T] { def write(in: T) = write(in) def read(value: JsValue) = value match { - case element:JsObject => element.convertTo[T] + case element: JsObject => element.convertTo[T] case _ => deserializationError("A JsObject was expected!!") } } + + + +/** + * A custom version of the Spray DefaultJsonProtocol with a modified field naming strategy + */ +trait SnakifiedSprayJsonSupport extends DefaultJsonProtocol { + import reflect._ + + /** + * This is the most important piece of code in this object! + * It overrides the default naming scheme used by spray-json and replaces it with a scheme that turns camelcased + * names into snakified names (i.e. using underscores as word separators). + */ + override protected def extractFieldNames(classTag: ClassTag[_]) = { + import java.util.Locale + + def snakify(name: String) = PASS2.replaceAllIn(PASS1.replaceAllIn(name, REPLACEMENT), REPLACEMENT).toLowerCase(Locale.US) + + super.extractFieldNames(classTag).map { snakify(_) } + } + + private val PASS1 = """([A-Z]+)([A-Z][a-z])""".r + private val PASS2 = """([a-z\d])([A-Z])""".r + private val REPLACEMENT = "$1_$2" +} + +object SnakifiedSprayJsonSupport extends SnakifiedSprayJsonSupport } \ No newline at end of file diff --git a/client/src/test/scala/org/zalando/nakadi/client/App.scala b/client/src/test/scala/org/zalando/nakadi/client/App.scala new file mode 100644 index 0000000..28286f4 --- /dev/null +++ b/client/src/test/scala/org/zalando/nakadi/client/App.scala @@ -0,0 +1,17 @@ +package org.zalando.nakadi.client + +import org.zalando.nakadi.client.model.JacksonJsonMarshaller + + +object Main extends App with JacksonJsonMarshaller { + val host = "nakadi-beta.aruha.zalan.do" + val OAuth2Token = () => "4ae2a575-ebd7-40c5-a4de-851b387e00f5" + val port = 443 + val client = new ClientImpl(Connection.newConnection(host, port, OAuth2Token, true, false),"UTF-8") + +// val response = client.eventTypes() +// Await.result(response, 10.second) +// response.map(r => +// println("######################## " + r)) + +} \ No newline at end of file diff --git a/client/src/test/scala/org/zalando/nakadi/client/DefaultMarshallerTest.scala b/client/src/test/scala/org/zalando/nakadi/client/DefaultMarshallerTest.scala deleted file mode 100644 index bf3b673..0000000 --- a/client/src/test/scala/org/zalando/nakadi/client/DefaultMarshallerTest.scala +++ /dev/null @@ -1,88 +0,0 @@ -package org.zalando.nakadi.client - -import scala.concurrent.duration.DurationInt -import org.scalatest.{ Matchers, WordSpec } -import org.zalando.nakadi.client.model._ -import org.zalando.nakadi.client.model.DefaultMarshaller -import akka.actor.ActorSystem -import akka.http.scaladsl.marshalling.Marshal -import akka.http.scaladsl.marshalling.Marshaller._ -import akka.stream.Materializer -import akka.http.scaladsl.unmarshalling._ -import akka.http.scaladsl.model._ -import scala.concurrent.Await -import scala.concurrent.duration._ -import akka.stream.ActorMaterializer -import akka.http.scaladsl.marshalling.GenericMarshallers -import akka.http.scaladsl.marshalling.Marshaller -import org.zalando.nakadi.client.model.Event -import org.zalando.nakadi.client.model._ - -class DefaultMarshallerTest extends WordSpec with Matchers with DefaultMarshaller { - implicit val system = ActorSystem("rest-server") - implicit val dispatcher = system.dispatcher - implicit val materializer: Materializer = ActorMaterializer() - - import org.zalando.nakadi.client.util.SimpleModelFactory._ - - /** - * Tests - */ - "All model Objects " must { - val testName = "marshal an unmarshall without any problems" - s"$testName(eventMetadata)" in { - checkSerializationProcess("eventMetadata", eventMetadata) - } - s"$testName(problem)" in { - checkSerializationProcess("problem", problem) - } - s"$testName(metrics)" in { - checkSerializationProcess("metrics", metrics) - } - s"$testName(partition)" in { - checkSerializationProcess("partition", partition) - } - s"$testName(cursor)" in { - checkSerializationProcess("cursor", cursor) - } - s"$testName(eventTypeSchema)" in { - checkSerializationProcess("eventTypeSchema", eventTypeSchema) - } - s"$testName(eventValidationStrategy)" in { - checkSerializationProcess("eventValidationStrategy", eventValidationStrategy) - } - s"$testName(partitionResolutionStrategy)" in { - checkSerializationProcess("partitionResolutionStrategy", partitionResolutionStrategy) - } - s"$testName(eventEnrichmentStrategy)" in { - checkSerializationProcess("eventEnrichmentStrategy", partitionResolutionStrategy) - } - s"$testName(businessEvent)" in { - checkSerializationProcess("businessEvent", businessEvent) - } - s"$testName(dataChangeEventQualifier)" in { - checkSerializationProcess("dataChangeEventQualifier", dataChangeEventQualifier) - } - s"$testName(dataChangeEvent)" in { - checkSerializationProcess("dataChangeEvent", dataChangeEvent) - } - s"$testName(eventType)" in { - checkSerializationProcess("eventType", eventType) - } - s"$testName(event)" in { - checkSerializationProcess("event", event) - } - s"$testName(eventStreamBatch)" in { - checkSerializationProcess("eventStreamBatch", eventStreamBatch) - } - - } - def checkSerializationProcess[T](key: String, obj: T)(implicit m: Marshaller[T, String], um: Unmarshaller[String, T]) { - val futureEntity = Marshal(obj).to[String] - val stringResult = Await.result(futureEntity, 1.second) // don't block in non-test code! - val futureResult = Unmarshal(stringResult).to[T] - val eventResult = Await.result(futureResult, 1.second) // don't block in non-test code! - assert(eventResult == obj, s"Failed to marshall $key") - } - -} \ No newline at end of file diff --git a/client/src/test/scala/org/zalando/nakadi/client/DeserializerSerializerTest.scala b/client/src/test/scala/org/zalando/nakadi/client/DeserializerSerializerTest.scala new file mode 100644 index 0000000..ef46f4c --- /dev/null +++ b/client/src/test/scala/org/zalando/nakadi/client/DeserializerSerializerTest.scala @@ -0,0 +1,31 @@ +package org.zalando.nakadi.client + +import scala.concurrent.Await +import scala.concurrent.duration.DurationInt +import org.scalatest.{ Matchers, WordSpec } +import org.zalando.nakadi.client.model.SprayJsonMarshaller +import org.zalando.nakadi.client.util.AkkaConfig +import akka.http.scaladsl.marshalling.{ Marshal, Marshaller } +import akka.http.scaladsl.unmarshalling.{ Unmarshal, Unmarshaller } +import akka.stream.Materializer +import org.zalando.nakadi.client.util.TestJsonEntity +import org.zalando.nakadi.client.model.EventType + +class DeserializerSerializerTest extends WordSpec with Matchers with SprayJsonMarshaller with AkkaConfig { + import TestJsonEntity._ + "When a json entity is unmarshalled and marshalled it" should { + val testName = "always result in the same entity" + s"$testName(event)" in { + checkDeserializationProcessSerialization[EventType]("event", events) + } + } + + def checkDeserializationProcessSerialization[T](key: String, value: String)(implicit m: Marshaller[T, String], um: Unmarshaller[String, T]) { + val futureScalaEntity = Unmarshal(value).to[T] + val scalaEntity = Await.result(futureScalaEntity, 1.second) + val futureJsonEntity = Marshal(value).to[String] + val jsonEntity = Await.result(futureScalaEntity, 1.second) + assert(jsonEntity == value, s"Failed to marshall $key") + } + +} \ No newline at end of file diff --git a/client/src/test/scala/org/zalando/nakadi/client/SerializerDeserializerTest.scala b/client/src/test/scala/org/zalando/nakadi/client/SerializerDeserializerTest.scala new file mode 100644 index 0000000..aa98229 --- /dev/null +++ b/client/src/test/scala/org/zalando/nakadi/client/SerializerDeserializerTest.scala @@ -0,0 +1,91 @@ +package org.zalando.nakadi.client + +import scala.concurrent.Await +import scala.concurrent.duration.DurationInt +import org.scalatest.{ Matchers, WordSpec } +import org.zalando.nakadi.client.util.AkkaConfig +import akka.http.scaladsl.marshalling.{ Marshal, Marshaller } +import akka.http.scaladsl.unmarshalling.{ Unmarshal, Unmarshaller } +import org.zalando.nakadi.client.util.TestScalaEntity +import org.zalando.nakadi.client.model.SprayJsonMarshaller + +/** + * Tests the Marshalling and Umarshalling of the same object in a single run. It tests in this sequence: 1.Marshall and 2.Unmarshall.
+ * This is just a simple test that should break when Custom + * Marshallers/Unmarshallers are used and produce different + * unexpected results. + */ +class SerializerDeserializerTest extends WordSpec with Matchers with SprayJsonMarshaller with AkkaConfig { + + import TestScalaEntity._ + + + /** + * Tests + */ + "When an entity(scala object) is marshalled and unmarshalled it" should { + val testName = "always result in the same entity" + s"$testName(eventMetadata)" in { + checkSerializationDeserializationProcess("eventMetadata", eventMetadata) + } + s"$testName(problem)" in { + checkSerializationDeserializationProcess("problem", problem) + } + s"$testName(metrics)" in { + checkSerializationDeserializationProcess("metrics", metrics) + } + s"$testName(partition)" in { + checkSerializationDeserializationProcess("partition", partition) + } + s"$testName(cursor)" in { + checkSerializationDeserializationProcess("cursor", cursor) + } + s"$testName(eventTypeSchema)" in { + checkSerializationDeserializationProcess("eventTypeSchema", eventTypeSchema) + } + s"$testName(eventValidationStrategy)" in { + checkSerializationDeserializationProcess("eventValidationStrategy", eventValidationStrategy) + } + s"$testName(partitionResolutionStrategy)" in { + checkSerializationDeserializationProcess("partitionResolutionStrategy", partitionResolutionStrategy) + } + s"$testName(eventEnrichmentStrategy)" in { + checkSerializationDeserializationProcess("eventEnrichmentStrategy", partitionResolutionStrategy) + } + s"$testName(businessEvent)" in { + checkSerializationDeserializationProcess("businessEvent", businessEvent) + } + s"$testName(dataChangeEventQualifier)" in { + checkSerializationDeserializationProcess("dataChangeEventQualifier", dataChangeEventQualifier) + } + s"$testName(dataChangeEvent)" in { + checkSerializationDeserializationProcess("dataChangeEvent", dataChangeEvent) + } + s"$testName(eventType)" in { + checkSerializationDeserializationProcess("eventType", eventType) + } + s"$testName(event)" in { + checkSerializationDeserializationProcess("event", event) + } + s"$testName(eventStreamBatch)" in { + checkSerializationDeserializationProcess("eventStreamBatch", eventStreamBatch) + } + s"$testName(eventTypeStatistics)" in { + checkSerializationDeserializationProcess("eventTypeStatistics", eventTypeStatistics) + } + s"$testName(batchItemResponse)" in { + checkSerializationDeserializationProcess("batchItemResponse", batchItemResponse) + } + + } + + def checkSerializationDeserializationProcess[T](key: String, value: T)(implicit m: Marshaller[T, String], um: Unmarshaller[String, T]) { + val futureJsonEntity = Marshal(value).to[String] // Marshal + val jsonEntity = Await.result(futureJsonEntity, 1.second) + println(jsonEntity) + val futureScalaEntity = Unmarshal(jsonEntity).to[T] //Unmarshal + val scalaEntity = Await.result(futureScalaEntity, 1.second) + assert(scalaEntity == value, s"Failed to marshall $key correctly!!!") + } + +} \ No newline at end of file diff --git a/client/src/test/scala/org/zalando/nakadi/client/util/AkkaConfig.scala b/client/src/test/scala/org/zalando/nakadi/client/util/AkkaConfig.scala new file mode 100644 index 0000000..dd0c33f --- /dev/null +++ b/client/src/test/scala/org/zalando/nakadi/client/util/AkkaConfig.scala @@ -0,0 +1,11 @@ +package org.zalando.nakadi.client.util + +import akka.actor.ActorSystem +import akka.stream.Materializer +import akka.stream.ActorMaterializer + +trait AkkaConfig { + implicit val system = ActorSystem("AkkaConfig") + implicit val dispatcher = system.dispatcher + implicit val materializer: Materializer = ActorMaterializer() +} \ No newline at end of file diff --git a/client/src/test/scala/org/zalando/nakadi/client/util/TestData.scala b/client/src/test/scala/org/zalando/nakadi/client/util/TestJsonEntity.scala similarity index 52% rename from client/src/test/scala/org/zalando/nakadi/client/util/TestData.scala rename to client/src/test/scala/org/zalando/nakadi/client/util/TestJsonEntity.scala index f88520c..e8c4cd9 100644 --- a/client/src/test/scala/org/zalando/nakadi/client/util/TestData.scala +++ b/client/src/test/scala/org/zalando/nakadi/client/util/TestJsonEntity.scala @@ -1,6 +1,6 @@ package org.zalando.nakadi.client.util -object TestData { +object TestJsonEntity { val events = """{ "name": "article", "owning_application": "article-producer", @@ -11,16 +11,5 @@ object TestData { "type": "json_schema", "schema": "{\"Article\": { \"properties\": { \"sku\": { \"type\": \"string\" } } }}" } - }, - { - "name": "myarticle", - "owning_application": "article-producer", - "category": "data", - "partition_resolution_strategy": null, - "partitioning_key_fields": [], - "schema": { - "type": "json_schema", - "schema": "{\"Article\": { \"properties\": { \"sku\": { \"type\": \"string\" } } }}" - } }""" } \ No newline at end of file diff --git a/client/src/test/scala/org/zalando/nakadi/client/util/SimpleModelFactory.scala b/client/src/test/scala/org/zalando/nakadi/client/util/TestScalaEntity.scala similarity index 68% rename from client/src/test/scala/org/zalando/nakadi/client/util/SimpleModelFactory.scala rename to client/src/test/scala/org/zalando/nakadi/client/util/TestScalaEntity.scala index 32cafd9..47c66fb 100644 --- a/client/src/test/scala/org/zalando/nakadi/client/util/SimpleModelFactory.scala +++ b/client/src/test/scala/org/zalando/nakadi/client/util/TestScalaEntity.scala @@ -18,31 +18,31 @@ import org.zalando.nakadi.client.model.DataChangeEvent import org.zalando.nakadi.client.model.Cursor import org.zalando.nakadi.client.model.BusinessEvent -object SimpleModelFactory { +object TestScalaEntity { // import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport import spray.json.DefaultJsonProtocol._ //Simple bjects composed out of scalar typers only (without dependency to other Object-models) - val eventMetadata = new EventMetadata("eid", "eventType", "occurredAt", "receivedAt", List("parentEids"), "flowId", Map("key" -> "value")) - val problem = new Problem("problemType", "title", 312, "detail", "instance") + val problem = new Problem("problemType", "title", 312, Option("detail"), Option("instance")) val metrics = new Metrics("metrics") val partition = new Partition("partition", "oldestAvailableOffset", "newestAvailableOffset") val cursor = new Cursor("partition", "offset") val eventTypeSchema = new EventTypeSchema("schemaType", "schema") - val eventValidationStrategy = new EventValidationStrategy("name", "doc") - val partitionResolutionStrategy = new PartitionResolutionStrategy("name", "doc") - val eventEnrichmentStrategy = new EventEnrichmentStrategy("name", "doc") + val eventValidationStrategy = new EventValidationStrategy("name", Option("doc")) + val partitionResolutionStrategy = new PartitionResolutionStrategy("name", Option("doc")) + val eventEnrichmentStrategy = new EventEnrichmentStrategy("name", Option("doc")) //Complex objects - val businessEvent = new BusinessEvent(eventMetadata) val dataChangeEventQualifier = new DataChangeEventQualifier("dataType", DataOperation.CREATE) - val dataChangeEvent = new DataChangeEvent(metrics, dataChangeEventQualifier, eventMetadata) - val eventType = new EventType("name", "owner", "category", "effectiveSchema", List("validationStrategies"), List("enrichmentStrategies"), partitionResolutionStrategy, eventTypeSchema, List("dataKeyFields"), - List("partitioningKeyFields")) + val eventTypeStatistics = new EventTypeStatistics(Option(9281002), Option(19283), Option(21), Option(312)) + val eventType = new EventType("name", "owner", EventTypeCategory.BUSINESS, Option(List("validationStrategies")), Option(List("enrichmentStrategies")), partitionResolutionStrategy, Option(eventTypeSchema), Option(List("dataKeyFields")), Option(List("partitioningKeyFields")), Option(eventTypeStatistics)) val event = new Event(eventType, true, "title") val eventStreamBatch = new EventStreamBatch(cursor, List(event, event)) - + val eventMetadata = new EventMetadata("eid", Option(eventType), "occurredAt", Option("receivedAt"), List("parentEids"), Option("flowId"), Option("partition"), Map("key" -> "value")) + val dataChangeEvent = new DataChangeEvent(metrics, dataChangeEventQualifier, eventMetadata) + val businessEvent = new BusinessEvent(eventMetadata) + val batchItemResponse = new BatchItemResponse(Option("eid"), BatchItemPublishingStatus.SUBMITTED, Option(BatchItemStep.PUBLISHING), Option("detail")) } \ No newline at end of file diff --git a/it/src/main/scala/org/zalando/nakadi/client/config/App.scala b/it/src/main/scala/org/zalando/nakadi/client/config/App.scala new file mode 100644 index 0000000..1ff27fe --- /dev/null +++ b/it/src/main/scala/org/zalando/nakadi/client/config/App.scala @@ -0,0 +1,17 @@ +package org.zalando.nakadi.client + +import org.zalando.nakadi.client.model.JacksonJsonMarshaller + + +object Main extends App with JacksonJsonMarshaller { + val host = "" + val OAuth2Token = () => "" + val port = 443 + val client = new ClientImpl(Connection.newConnection(host, port, OAuth2Token, true, false),"UTF-8") + +// val response = client.eventTypes() +// Await.result(response, 10.second) +// response.map(r => +// println("######################## " + r)) + +} \ No newline at end of file diff --git a/it/src/test/scala/org/zalando/nakadi/client/KlientIntegrationTest.scala b/it/src/test/scala/org/zalando/nakadi/client/KlientIntegrationTest.scala new file mode 100644 index 0000000..c017df1 --- /dev/null +++ b/it/src/test/scala/org/zalando/nakadi/client/KlientIntegrationTest.scala @@ -0,0 +1,51 @@ +package org.zalando.nakadi.client + +import org.scalatest.Matchers +import org.scalatest.WordSpec +import scala.concurrent.Await +import scala.concurrent.duration.DurationInt +import org.zalando.nakadi.client.model.SprayJsonMarshaller +import scala.concurrent.Future +import org.zalando.nakadi.client.model._ + +class KlientIntegrationTest extends WordSpec with Matchers with SprayJsonMarshaller { + + val host = "nakadi-sandbox.aruha-test.zalan.do" + val OAuth2Token = () => "42b2f4f8-4052-4fca-916e-972f573b1c52" + val port = 443 + val client = new ClientImpl(Connection.newConnection(host, port, OAuth2Token, true, false),"UTF-8") + + //Defaults + val partitionStrategy = new PartitionResolutionStrategy("hash",None) + + + "Nakadi Client" should { + "parse multiple PartitionResolutionStrategy" in { + val Right(result) = executeCall(client.partitionStrategies()) + result.size should be >0 + } + "parse multiple EventType" in { + val eventType=new EventType("nakadi-client-test", + "laas-team", + EventTypeCategory.UNDEFINED,None,None, + partitionStrategy, + None, + None, + None, + None + ) + client.newEventType(eventType) + val Right(result) = Await.result(client.eventTypes(), 10.second) + result.size should be >20 + } + + def executeCall[T](call: =>Future[T]):T={ + Await.result(call, 10.second) + } + + } + +} + + + diff --git a/model/src/main/scala/org/zalando/nakadi/client/model/Model.scala b/model/src/main/scala/org/zalando/nakadi/client/model/Model.scala index 75ae86d..218681e 100644 --- a/model/src/main/scala/org/zalando/nakadi/client/model/Model.scala +++ b/model/src/main/scala/org/zalando/nakadi/client/model/Model.scala @@ -22,6 +22,7 @@ case class Event( title: String) /** + * * Metadata for this Event. Contains commons fields for both Business and DataChange Events. Most are enriched by Nakadi upon reception, but they in general MIGHT be set by the client. * @param eid Identifier of this Event. Clients are allowed to generate this and this SHOULD be guaranteed to be unique from the perspective of the producer. Consumers MIGHT use this value to assert uniqueness of reception of the Event. * @param eventType The EventType of this Event. This is enriched by Nakadi on reception of the Event based on the endpoint where the Producer sent the Event to. If provided MUST match the endpoint. Failure to do so will cause rejection of the Event. @@ -34,11 +35,12 @@ case class Event( */ case class EventMetadata( eid: String, - eventType: String, + eventType: Option[EventType], occurredAt: String, - receivedAt: String, + receivedAt: Option[String], parentEids: Seq[String], - flowId: String, + flowId: Option[String], + partition:Option[String], metadata: Map[String, Any]) /** @@ -64,7 +66,7 @@ case class DataChangeEventQualifier( * @param eventQualifier Indicators of a `DataChangeEvent`'s referred data type and the type of operations done on them. * @param metadata Metadata for this Event. Contains commons fields for both Business and DataChange Events. Most are enriched by Nakadi upon reception, but they in general MIGHT be set by the client */ -case class DataChangeEvent[T]( +case class DataChangeEvent[T]( //TODO: Maybe DataChangeEvent must subclass DataChangeEventQualifier and EventMetadata via trait data: T, eventQualifier: DataChangeEventQualifier, metadata: EventMetadata) @@ -80,8 +82,8 @@ case class Problem( problemType: String, title: String, status: Int, - detail: String, - instance: String) + detail: Option[String], + instance: Option[String]) case class Metrics(metrics: String) //TODO: It is not defined yet! @@ -128,15 +130,15 @@ case class EventStreamBatch( */ case class EventType( name: String, - owner: String, + owningApplication: String, category: EventTypeCategory.Value, - effectiveSchema: String, - validationStrategies: Seq[String], - enrichmentStrategies: Seq[String], + validationStrategies: Option[Seq[String]], + enrichmentStrategies: Option[Seq[String]], partitionResolutionStrategy: PartitionResolutionStrategy, //Different naming - schema: EventTypeSchema, - dataKeyFields: Seq[String], - partitioningKeyFields: Seq[String]) + schema: Option[EventTypeSchema], + dataKeyFields: Option[Seq[String]], + partitioningKeyFields: Option[Seq[String]], + statistics:Option[EventTypeStatistics]) /** * The schema for an EventType, expected to be a json schema in yaml @@ -159,10 +161,10 @@ case class EventTypeSchema( * */ case class EventTypeStatistics( - expectedWriteRate: Int, - messageSize: Int, - readParallelism: Int, - writeParallelism: Int) + expectedWriteRate: Option[Int], + messageSize: Option[Int], + readParallelism: Option[Int], + writeParallelism: Option[Int]) /** * Defines a rule for validation of an incoming Event. Rules might require additional parameters; see the `doc` field of the existing rules for details. See GET /registry/validation-strategies for a list of available rules. @@ -171,7 +173,7 @@ case class EventTypeStatistics( */ case class EventValidationStrategy( name: String, - doc: String) + doc: Option[String]) /** * Defines a rule for the resolution of incoming Events into partitions. Rules might require additional parameters; see the `doc` field of the existing rules for details. See `GET /registry/partition-strategies` for a list of available rules. @@ -180,7 +182,7 @@ case class EventValidationStrategy( */ case class PartitionResolutionStrategy( name: String, - doc: String) + doc: Option[String]) /** * Defines a rule for transformation of an incoming Event. No existing fields might be modified. In practice this is used to set automatically values in the Event upon reception (i.e. set a reception timestamp on the Event). Rules might require additional parameters; see the `doc` field of the existing rules for details. See GET /registry/enrichment-strategies for a list of available rules. @@ -189,7 +191,7 @@ case class PartitionResolutionStrategy( */ case class EventEnrichmentStrategy( name: String, - doc: String) + doc: Option[String]) /** * A status corresponding to one individual Event's publishing attempt. * @param eid Eid of the corresponding item. Will be absent if missing on the incoming Event. @@ -199,10 +201,10 @@ case class EventEnrichmentStrategy( * */ case class BatchItemResponse( - eid: String, + eid: Option[String], publishingStatus: BatchItemPublishingStatus.Value, - step: BatchItemStep.Value, - detail: String) + step: Option[BatchItemStep.Value], + detail: Option[String]) ///////////////////////////////// // ENUMS //////////////////////// diff --git a/project/Build.scala b/project/Build.scala index d003e7f..534ac16 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -43,6 +43,19 @@ lazy val client = withDefaults( project.in(file("client")).dependsOn(api, model) ).settings(libraryDependencies ++= clientDeps) + + lazy val it = withDefaults( + "nakadi-integration-test", + project.in(file("it")).dependsOn(model, api, client) + ).settings(libraryDependencies ++= clientDeps) + + + lazy val e2e = withDefaults( + "nakadi-end-2-end-test", + project.in(file("e2e")).dependsOn(model, api, client, it) + ).settings(libraryDependencies ++= clientDeps) + + def withDefaults(projectName:String, project:sbt.Project)={ project.settings( name := projectName, @@ -51,6 +64,9 @@ def withDefaults(projectName:String, project:sbt.Project)={ resolvers += Resolver.mavenLocal, resolvers += "Maven Central Server" at "http://repo1.maven.org/maven2", scalacOptions ++= defaultOptions) + .configs(Configs.all: _*) + .settings(Testing.settings: _*) + } diff --git a/project/Configs.scala b/project/Configs.scala new file mode 100644 index 0000000..62284f9 --- /dev/null +++ b/project/Configs.scala @@ -0,0 +1,7 @@ +import sbt._ + +object Configs { + val IntegrationTest = config("it") extend(Runtime) + val EndToEndTest = config("e2e") extend(Runtime) + val all = Seq(IntegrationTest, EndToEndTest) +} diff --git a/project/Testing.scala b/project/Testing.scala new file mode 100644 index 0000000..ea14154 --- /dev/null +++ b/project/Testing.scala @@ -0,0 +1,35 @@ +import sbt._ +import Keys._ +import sbt.Keys._ + + + +object Testing { + import Configs._ + lazy val testAll = TaskKey[Unit]("test-all") + + private lazy val itSettings = + inConfig(IntegrationTest)(Defaults.testSettings) ++ Seq( + fork in IntegrationTest := false, + parallelExecution in IntegrationTest := false + ,unmanagedResourceDirectories in Compile += baseDirectory.value / "src/it/scala" + //scalaSource in IntegrationTest := baseDirectory.value / "src/it/scala", + //resourceDirectory in IntegrationTest := baseDirectory.value / "src/it/resources" + //,sourceDirectory in Compile := baseDirectory.value / "src/it/scala" + //,sourceDirectory in IntegrationTestTest <<= baseDirectory(_ / "src/test/scala") + //,resourceDirectory in Compile <<= baseDirectory(_ / "resources") + ) + + private lazy val e2eSettings = + inConfig(EndToEndTest)(Defaults.testSettings) ++ + Seq( + fork in EndToEndTest := false, + parallelExecution in EndToEndTest := false, + scalaSource in EndToEndTest := baseDirectory.value / "src/e2e/scala", + resourceDirectory in IntegrationTest := baseDirectory.value / "src/it/resources" + ) + + lazy val settings = itSettings ++ e2eSettings ++ Seq( + testAll <<= (test in EndToEndTest).dependsOn((test in IntegrationTest).dependsOn(test in Test)) + ) +} From 9871431a8bd7eb57c71f8ab8b2dea8298b0f0445 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Mon, 4 Apr 2016 09:38:10 +0200 Subject: [PATCH 009/183] Should not be committed --- .../org/zalando/nakadi/client/KlientIntegrationTest.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/it/src/test/scala/org/zalando/nakadi/client/KlientIntegrationTest.scala b/it/src/test/scala/org/zalando/nakadi/client/KlientIntegrationTest.scala index c017df1..a00cdbb 100644 --- a/it/src/test/scala/org/zalando/nakadi/client/KlientIntegrationTest.scala +++ b/it/src/test/scala/org/zalando/nakadi/client/KlientIntegrationTest.scala @@ -10,8 +10,8 @@ import org.zalando.nakadi.client.model._ class KlientIntegrationTest extends WordSpec with Matchers with SprayJsonMarshaller { - val host = "nakadi-sandbox.aruha-test.zalan.do" - val OAuth2Token = () => "42b2f4f8-4052-4fca-916e-972f573b1c52" + val host = "" + val OAuth2Token = () => "" val port = 443 val client = new ClientImpl(Connection.newConnection(host, port, OAuth2Token, true, false),"UTF-8") From d967330900693c6d84a818e070e3aaed6b731f46 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Mon, 4 Apr 2016 13:04:16 +0200 Subject: [PATCH 010/183] Creation of EventTypes. Done! --- .../org/zalando/nakadi/client/Client.scala | 55 +++++++++++-------- .../zalando/nakadi/client/Connection.scala | 30 +++++----- .../client/model/SprayJsonMarshaller.scala | 5 +- .../nakadi/client/util/TestScalaEntity.scala | 2 +- .../nakadi/client/KlientIntegrationTest.scala | 51 +++++++++++------ .../zalando/nakadi/client/model/Model.scala | 8 ++- 6 files changed, 91 insertions(+), 60 deletions(-) diff --git a/client/src/main/scala/org/zalando/nakadi/client/Client.scala b/client/src/main/scala/org/zalando/nakadi/client/Client.scala index ad244da..7efc703 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/Client.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/Client.scala @@ -12,6 +12,7 @@ import akka.protobuf.ByteString import org.zalando.nakadi.client.model.EventType import org.zalando.nakadi.client.model.PartitionResolutionStrategy import akka.http.scaladsl.unmarshalling._ +import akka.http.scaladsl.marshalling._ import scala.concurrent.Await import org.zalando.nakadi.client.model.Partition import org.zalando.nakadi.client.model.EventEnrichmentStrategy @@ -37,7 +38,7 @@ trait Client { * }}} * */ - def eventTypes()(implicit marshaller: FromEntityUnmarshaller[List[EventType]]): Future[Either[ClientError, List[EventType]]] + def eventTypes()(implicit marshaller: FromEntityUnmarshaller[List[EventType]]): Future[Either[ClientError, Option[List[EventType]]]] /** * Creates a new EventType. @@ -49,7 +50,7 @@ trait Client { * @param event - The EventType to create. * */ - def newEventType(eventType: EventType)(implicit marshaller: FromEntityUnmarshaller[EventType]): Future[Option[ClientError]] + def newEventType(eventType: EventType)(implicit marshaller: ToEntityMarshaller[EventType]): Future[Option[ClientError]] /** * Returns the EventType identified by its name. @@ -58,7 +59,7 @@ trait Client { * }}} * @param name - Name of the EventType */ - def eventType(name: String)(implicit marshaller: FromEntityUnmarshaller[EventType]): Future[Either[ClientError, EventType]] + def eventType(name: String)(implicit marshaller: FromEntityUnmarshaller[EventType]): Future[Either[ClientError, Option[EventType]]] /** * Updates the EventType identified by its name. * {{{ @@ -97,7 +98,7 @@ trait Client { * @param name - Name of the EventType * */ - def events[T](name: String)(implicit marshaller: FromEntityUnmarshaller[T]): Future[Either[ClientError, T]] + def events[T](name: String)(implicit marshaller: FromEntityUnmarshaller[T]): Future[Either[ClientError, Option[T]]] /** * List the partitions for the given EventType. @@ -106,7 +107,7 @@ trait Client { * }}} * @param name - Name of the EventType */ - def partitions(name: String)(implicit marshaller: FromEntityUnmarshaller[Partition]): Future[Either[ClientError, Partition]] + def partitions(name: String)(implicit marshaller: FromEntityUnmarshaller[Partition]): Future[Either[ClientError, Option[Partition]]] /** * Returns the Partition for the given EventType. @@ -117,7 +118,7 @@ trait Client { * @param partition - Name of the partition */ - def partitionByName(name: String, partition: String)(implicit marshaller: FromEntityUnmarshaller[Partition]): Future[Either[ClientError, Partition]] + def partitionByName(name: String, partition: String)(implicit marshaller: FromEntityUnmarshaller[Partition]): Future[Either[ClientError, Option[Partition]]] /** * Returns all of the validation strategies supported by this installation of Nakadi. @@ -126,7 +127,7 @@ trait Client { * curl --request GET /registry/validation-strategies * }}} */ - def validationStrategies()(implicit marshaller: FromEntityUnmarshaller[EventValidationStrategy]): Future[Either[ClientError, EventValidationStrategy]] + def validationStrategies()(implicit marshaller: FromEntityUnmarshaller[EventValidationStrategy]): Future[Either[ClientError, Option[EventValidationStrategy]]] /** * Returns all of the enrichment strategies supported by this installation of Nakadi. @@ -135,7 +136,7 @@ trait Client { * }}} */ - def enrichmentStrategies()(implicit marshaller: FromEntityUnmarshaller[EventEnrichmentStrategy]): Future[Either[ClientError, EventEnrichmentStrategy]] + def enrichmentStrategies()(implicit marshaller: FromEntityUnmarshaller[EventEnrichmentStrategy]): Future[Either[ClientError, Option[EventEnrichmentStrategy]]] /** * Returns all of the partitioning strategies supported by this installation of Nakadi. @@ -143,7 +144,7 @@ trait Client { * curl --request GET /registry/partitioning-strategies * }}} */ - def partitionStrategies()(implicit marshaller: FromEntityUnmarshaller[List[PartitionResolutionStrategy]]): Future[Either[ClientError, List[PartitionResolutionStrategy]]] + def partitionStrategies()(implicit marshaller: FromEntityUnmarshaller[List[PartitionResolutionStrategy]]): Future[Either[ClientError, Option[List[PartitionResolutionStrategy]]]] /** * Shuts down the communication system of the client @@ -164,15 +165,15 @@ private[client] class ClientImpl(connection: Connection, charSet: String) extend val logger = Logger(LoggerFactory.getLogger(this.getClass)) def metrics(): Future[Either[ClientError, HttpResponse]] = ??? - def eventTypes()(implicit marshaller: FromEntityUnmarshaller[List[EventType]]): Future[Either[ClientError, List[EventType]]] = { + def eventTypes()(implicit unmarshaller: FromEntityUnmarshaller[List[EventType]]): Future[Either[ClientError, Option[List[EventType]]]] = { logGetError(connection.get(URI_EVENT_TYPES).flatMap(handleGetResponse(_))) } - def newEventType(eventType: EventType)(implicit marshaller: FromEntityUnmarshaller[EventType]): Future[Option[ClientError]] = { - logPostError(connection.post(URI_EVENT_TYPES).map(handlePostResponse(_))) + def newEventType(eventType: EventType)(implicit marshaller: ToEntityMarshaller[EventType]): Future[Option[ClientError]] = { + logPostError(connection.post(URI_EVENT_TYPES, eventType).map(handlePostResponse(_))) } - def eventType(name: String)(implicit marshaller: FromEntityUnmarshaller[EventType]): Future[Either[ClientError, EventType]] = ??? + def eventType(name: String)(implicit marshaller: FromEntityUnmarshaller[EventType]): Future[Either[ClientError, Option[EventType]]] = ??? def updateEventType(name: String, eventType: EventType)(implicit marshaller: FromEntityUnmarshaller[EventType]): Future[Option[ClientError]] = ??? @@ -180,17 +181,17 @@ private[client] class ClientImpl(connection: Connection, charSet: String) extend def newEvent[T](name: String, event: T)(implicit marshaller: FromEntityUnmarshaller[T]): Future[Option[ClientError]] = ??? - def events[T](name: String)(implicit marshaller: FromEntityUnmarshaller[T]): Future[Either[ClientError, T]] = ??? + def events[T](name: String)(implicit marshaller: FromEntityUnmarshaller[T]): Future[Either[ClientError, Option[T]]] = ??? - def partitions(name: String)(implicit marshaller: FromEntityUnmarshaller[Partition]): Future[Either[ClientError, Partition]] = ??? + def partitions(name: String)(implicit marshaller: FromEntityUnmarshaller[Partition]): Future[Either[ClientError, Option[Partition]]] = ??? - def partitionByName(name: String, partition: String)(implicit marshaller: FromEntityUnmarshaller[Partition]): Future[Either[ClientError, Partition]] = ??? + def partitionByName(name: String, partition: String)(implicit marshaller: FromEntityUnmarshaller[Partition]): Future[Either[ClientError, Option[Partition]]] = ??? - def validationStrategies()(implicit marshaller: FromEntityUnmarshaller[EventValidationStrategy]): Future[Either[ClientError, EventValidationStrategy]] = ??? + def validationStrategies()(implicit marshaller: FromEntityUnmarshaller[EventValidationStrategy]): Future[Either[ClientError, Option[EventValidationStrategy]]] = ??? - def enrichmentStrategies()(implicit marshaller: FromEntityUnmarshaller[EventEnrichmentStrategy]): Future[Either[ClientError, EventEnrichmentStrategy]] = ??? + def enrichmentStrategies()(implicit marshaller: FromEntityUnmarshaller[EventEnrichmentStrategy]): Future[Either[ClientError, Option[EventEnrichmentStrategy]]] = ??? - def partitionStrategies()(implicit marshaller: FromEntityUnmarshaller[List[PartitionResolutionStrategy]]): Future[Either[ClientError, List[PartitionResolutionStrategy]]] = + def partitionStrategies()(implicit marshaller: FromEntityUnmarshaller[List[PartitionResolutionStrategy]]): Future[Either[ClientError, Option[List[PartitionResolutionStrategy]]]] = logGetError(connection.get(URI_PARTITIONING_STRATEGIES).flatMap(handleGetResponse(_))) def stop(): Future[Terminated] = connection.stop() @@ -214,12 +215,12 @@ private[client] class ClientImpl(connection: Connection, charSet: String) extend } } - def handleGetResponse[T](response: HttpResponse)(implicit unmarshaller: FromEntityUnmarshaller[T], m: Manifest[T]): Future[Either[ClientError, T]] = { + def handleGetResponse[T](response: HttpResponse)(implicit unmarshaller: FromEntityUnmarshaller[T], m: Manifest[T]): Future[Either[ClientError, Option[T]]] = { logger.debug("received [response={}]", response) response.status match { case status if (status.isSuccess()) => logger.info("Result is successfull ({}) ", status.intValue().toString()) - Unmarshal(response.entity).to[T].map(Right(_)) + Unmarshal(response.entity).to[T].map(in => Right(Option(in))) case status if (status.isRedirection()) => val msg = "An error occurred with http-status (" + status.intValue() + "}) and reason:" + status.reason() logger.info(msg) @@ -231,16 +232,22 @@ private[client] class ClientImpl(connection: Connection, charSet: String) extend } } def handlePostResponse[T](response: HttpResponse): Option[ClientError] = { - logger.debug("received [response={}]", response) response.status match { case status if (status.isSuccess()) => + logger.debug("Success. http-status: %s", status.intValue().toString()) None case status if (status.isRedirection()) => - val msg = "An error occurred with http-status (" + status.intValue() + "}) and reason:" + status.reason() + val msg = "Redirection - http-status: %s, reason[%s]".format(status.intValue().toString(), status.reason()) logger.info(msg) + response.entity.toStrict(10.second).map{ body => + logger.debug("Redirection - http-status: %s, reason[%s], body:[%s]".format(status.intValue().toString(), status.reason(), body.data.decodeString(charSet))) + } Option(ClientError(msg, Some(status.intValue()))) case status if (status.isFailure()) => - val msg = "An error occurred with http-status (" + status.intValue() + "}) and reason:" + status.reason() + response.entity.toStrict(10.second).map{ body => + logger.debug("Failure - http-status: %s, reason[%s], body:[%s]".format(status.intValue().toString(), status.reason(), body.data.decodeString(charSet))) + } + val msg = "Failure - http-status: %s, reason[%s], body:[%s]".format(status.intValue().toString(), status.reason(), response.entity.dataBytes) logger.warn(msg) Option(ClientError(msg, Some(status.intValue()))) } diff --git a/client/src/main/scala/org/zalando/nakadi/client/Connection.scala b/client/src/main/scala/org/zalando/nakadi/client/Connection.scala index c4a27ab..35aba00 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/Connection.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/Connection.scala @@ -10,10 +10,10 @@ import scala.concurrent.duration.DurationInt import org.slf4j.LoggerFactory import com.typesafe.scalalogging.Logger - +import akka.http.scaladsl.marshalling._ import akka.actor.{ ActorSystem, Terminated } import akka.http.scaladsl.{ Http, HttpsConnectionContext } -import akka.http.scaladsl.model.{ HttpRequest, HttpResponse, MediaRange } +import akka.http.scaladsl.model._ import akka.http.scaladsl.model.MediaTypes.`application/json` import akka.http.scaladsl.model.HttpMethods.POST import akka.http.scaladsl.model.Uri.apply @@ -25,7 +25,7 @@ import javax.net.ssl.{ SSLContext, TrustManager, X509TrustManager } trait Connection { def get(endpoint: String): Future[HttpResponse] - def post(endpoint: String): Future[HttpResponse] + def post[T](endpoint: String, event: T)(implicit marshaller: ToEntityMarshaller[T]): Future[HttpResponse] def stop(): Future[Terminated] def materializer(): ActorMaterializer } @@ -60,7 +60,7 @@ object Connection { } /** - * Class for handling the configuration and most basic calls. + * Class for handling the configuration and basic http calls. */ private[client] class ConnectionImpl(host: String, port: Int, tokenProvider: () => String, securedConnection: Boolean, verifySSlCertificate: Boolean) extends Connection { @@ -89,14 +89,16 @@ private[client] class ConnectionImpl(host: String, port: Int, tokenProvider: () logError(response) response } - def post(endpoint: String): Future[HttpResponse] = { - logger.info("Calling {}", endpoint) - val response: Future[HttpResponse] = - Source.single(PostHttpRequest(endpoint)) + def post[T](endpoint: String, event: T)(implicit marshaller: ToEntityMarshaller[T]): Future[HttpResponse] = { + val result = Marshal(event).to[MessageEntity].flatMap { entity => + logger.info("Posting to endpoint {}", endpoint) + logger.debug("Data to post {}", entity.toString()) + Source.single(PostHttpRequest(endpoint, entity)) .via(connectionFlow). runWith(Sink.head) - logError(response) - response + } + logError(result) + result } private def logError(future: Future[Any]) { @@ -109,9 +111,11 @@ private[client] class ConnectionImpl(host: String, port: Int, tokenProvider: () HttpRequest(uri = url).withHeaders(headers.Authorization(OAuth2BearerToken(tokenProvider())), headers.Accept(MediaRange(`application/json`))) } - private def PostHttpRequest(url: String): HttpRequest = { - HttpRequest(uri = url, method = POST).withHeaders(headers.Authorization(OAuth2BearerToken(tokenProvider())), - headers.Accept(MediaRange(`application/json`))) + private def PostHttpRequest(url: String, entity: MessageEntity): HttpRequest = { + HttpRequest(uri = url, method = POST) // + .withHeaders(headers.Authorization(OAuth2BearerToken(tokenProvider())), + headers.Accept(MediaRange(`application/json`))) + .withEntity(entity) } def stop(): Future[Terminated] = actorSystem.terminate() diff --git a/client/src/main/scala/org/zalando/nakadi/client/model/SprayJsonMarshaller.scala b/client/src/main/scala/org/zalando/nakadi/client/model/SprayJsonMarshaller.scala index a792cd9..359a957 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/model/SprayJsonMarshaller.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/model/SprayJsonMarshaller.scala @@ -16,6 +16,7 @@ trait SprayJsonMarshaller extends SprayJsonSupport with DefaultJsonProtocol { implicit val eventTypeCategoryEnumFormatter = jsonEnumFormat(EventTypeCategory) implicit val batchItemStepjsonEnumFormat = jsonEnumFormat(BatchItemStep) implicit val BatchItemPublishingStatusEnumFormat = jsonEnumFormat(BatchItemPublishingStatus) + implicit val SchemaTypeEnumFormat = jsonEnumFormat(SchemaType) implicit val problemFormatter = jsonFormat(Problem, "problem_type", "title", "status", "detail", "instance") implicit val metricsFormatter = jsonFormat1(Metrics) @@ -23,10 +24,10 @@ trait SprayJsonMarshaller extends SprayJsonSupport with DefaultJsonProtocol { implicit val cursorFormatter = jsonFormat2(Cursor) implicit val dataChangeEventQualifierFormatter = jsonFormat(DataChangeEventQualifier, "data_type", "data_operation") implicit val partitionResolutionStrategyFormatter = jsonFormat2(PartitionResolutionStrategy) - implicit val eventTypeSchemaFormatter = jsonFormat(EventTypeSchema, "schema_type", "schema") + implicit val eventTypeSchemaFormatter = jsonFormat(EventTypeSchema, "type", "schema") implicit val eventTypeStatisticsFormatter = jsonFormat(EventTypeStatistics, "expected_write_rate", "message_size", "read_parallelism", "write_parallelism") implicit val eventValidationStrategyFormatter = jsonFormat2(EventValidationStrategy) - implicit val eventTypeFormatter = jsonFormat(EventType, "name", "owning_application", "category", "validation_strategies", "enrichment_strategies", "partition_resolution_strategy", "schema", "data_key_fields", "partitioning_key_fields", "statistics") + implicit val eventTypeFormatter = jsonFormat(EventType, "name", "owning_application", "category", "validation_strategies", "enrichment_strategies", "partition_strategy", "schema", "partition_key_fields", "partitioning_key_fields", "statistics") implicit val eventMetadataFormatter = jsonFormat(EventMetadata, "eid", "event_type", "occurred_at", "received_at", "parent_eids", "flow_id", "partition", "metadata") implicit val businessEventFormatter = jsonFormat1(BusinessEvent) implicit val eventEnrichmentStrategyFormatter = jsonFormat2(EventEnrichmentStrategy) diff --git a/client/src/test/scala/org/zalando/nakadi/client/util/TestScalaEntity.scala b/client/src/test/scala/org/zalando/nakadi/client/util/TestScalaEntity.scala index 47c66fb..76320bb 100644 --- a/client/src/test/scala/org/zalando/nakadi/client/util/TestScalaEntity.scala +++ b/client/src/test/scala/org/zalando/nakadi/client/util/TestScalaEntity.scala @@ -29,7 +29,7 @@ object TestScalaEntity { val metrics = new Metrics("metrics") val partition = new Partition("partition", "oldestAvailableOffset", "newestAvailableOffset") val cursor = new Cursor("partition", "offset") - val eventTypeSchema = new EventTypeSchema("schemaType", "schema") + val eventTypeSchema = new EventTypeSchema(SchemaType.JSON, "schema") val eventValidationStrategy = new EventValidationStrategy("name", Option("doc")) val partitionResolutionStrategy = new PartitionResolutionStrategy("name", Option("doc")) val eventEnrichmentStrategy = new EventEnrichmentStrategy("name", Option("doc")) diff --git a/it/src/test/scala/org/zalando/nakadi/client/KlientIntegrationTest.scala b/it/src/test/scala/org/zalando/nakadi/client/KlientIntegrationTest.scala index a00cdbb..3293d72 100644 --- a/it/src/test/scala/org/zalando/nakadi/client/KlientIntegrationTest.scala +++ b/it/src/test/scala/org/zalando/nakadi/client/KlientIntegrationTest.scala @@ -7,39 +7,54 @@ import scala.concurrent.duration.DurationInt import org.zalando.nakadi.client.model.SprayJsonMarshaller import scala.concurrent.Future import org.zalando.nakadi.client.model._ +import scala.util.Random class KlientIntegrationTest extends WordSpec with Matchers with SprayJsonMarshaller { val host = "" val OAuth2Token = () => "" val port = 443 - val client = new ClientImpl(Connection.newConnection(host, port, OAuth2Token, true, false),"UTF-8") - + val client = new ClientImpl(Connection.newConnection(host, port, OAuth2Token, true, false), "UTF-8") + //Defaults - val partitionStrategy = new PartitionResolutionStrategy("hash",None) - - + val partitionStrategy = new PartitionResolutionStrategy("hash", None) + val paritionKeyFields = List("order_number") + val schemaDefinition = """{ "properties": { "order_number": { "type": "string" } } }""" + val eventTypeSchema = new EventTypeSchema(SchemaType.JSON, schemaDefinition) + "Nakadi Client" should { "parse multiple PartitionResolutionStrategy" in { val Right(result) = executeCall(client.partitionStrategies()) - result.size should be >0 + result.size should be > 0 + } + "Event POST/GET/DELETE" in { + val random = Random.nextInt() + val name = "test-client-integration"+random + val team = "laas-team" + val eventType = new EventType(name, team, EventTypeCategory.BUSINESS, + None, None, partitionStrategy, Option(eventTypeSchema), Option(paritionKeyFields), None, None) + val creationResult = executeCall(client.newEventType(eventType)) + creationResult.isDefined shouldBe false + //Now get it back + val Right(Some(result)) = executeCall(client.eventType(name)) + result == eventType + } "parse multiple EventType" in { - val eventType=new EventType("nakadi-client-test", - "laas-team", - EventTypeCategory.UNDEFINED,None,None, - partitionStrategy, - None, - None, - None, - None - ) + val eventType = new EventType("nakadi-client-test", + "laas-team", + EventTypeCategory.UNDEFINED, None, None, + partitionStrategy, + None, + None, + None, + None) client.newEventType(eventType) val Right(result) = Await.result(client.eventTypes(), 10.second) - result.size should be >20 + result.size should be > 20 } - - def executeCall[T](call: =>Future[T]):T={ + + def executeCall[T](call: => Future[T]): T = { Await.result(call, 10.second) } diff --git a/model/src/main/scala/org/zalando/nakadi/client/model/Model.scala b/model/src/main/scala/org/zalando/nakadi/client/model/Model.scala index 218681e..da32493 100644 --- a/model/src/main/scala/org/zalando/nakadi/client/model/Model.scala +++ b/model/src/main/scala/org/zalando/nakadi/client/model/Model.scala @@ -134,7 +134,7 @@ case class EventType( category: EventTypeCategory.Value, validationStrategies: Option[Seq[String]], enrichmentStrategies: Option[Seq[String]], - partitionResolutionStrategy: PartitionResolutionStrategy, //Different naming + partitionStrategy: PartitionResolutionStrategy, //Different naming schema: Option[EventTypeSchema], dataKeyFields: Option[Seq[String]], partitioningKeyFields: Option[Seq[String]], @@ -148,7 +148,7 @@ case class EventType( * Failure to respect the syntax will fail any operation on an EventType. */ case class EventTypeSchema( - schemaType: String, + schemaType: SchemaType.Value,//Name is type (keyword in scala) schema: String) /** @@ -272,3 +272,7 @@ case object BatchItemStep extends Enumeration { } +case object SchemaType extends Enumeration{ + type SchemaType = Value + val JSON = Value("json_schema") +} From 0085e0497b73aba072909a3756ea3e8b709b59fe Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Mon, 4 Apr 2016 16:05:21 +0200 Subject: [PATCH 011/183] Added Integration tests for EventTypes --- .../org/zalando/nakadi/client/Client.scala | 118 +--------------- .../zalando/nakadi/client/ClientImpl.scala | 122 +++++++++++++++++ .../zalando/nakadi/client/Connection.scala | 52 ++++--- .../client/EventTypesIntegrationTest.scala | 129 ++++++++++++++++++ .../nakadi/client/KlientIntegrationTest.scala | 45 ++---- 5 files changed, 299 insertions(+), 167 deletions(-) create mode 100644 client/src/main/scala/org/zalando/nakadi/client/ClientImpl.scala create mode 100644 it/src/test/scala/org/zalando/nakadi/client/EventTypesIntegrationTest.scala diff --git a/client/src/main/scala/org/zalando/nakadi/client/Client.scala b/client/src/main/scala/org/zalando/nakadi/client/Client.scala index 7efc703..b5f4657 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/Client.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/Client.scala @@ -1,22 +1,13 @@ package org.zalando.nakadi.client import scala.concurrent.Future + +import org.zalando.nakadi.client.model.{ EventEnrichmentStrategy, EventType, EventValidationStrategy, Partition, PartitionResolutionStrategy } + import akka.actor.Terminated +import akka.http.scaladsl.marshalling.ToEntityMarshaller import akka.http.scaladsl.model.HttpResponse -import scala.concurrent.duration.DurationInt -import scala.concurrent.ExecutionContext.Implicits.global -import org.slf4j.LoggerFactory -import com.typesafe.scalalogging.Logger -import akka.http.scaladsl.unmarshalling.Unmarshaller -import akka.protobuf.ByteString -import org.zalando.nakadi.client.model.EventType -import org.zalando.nakadi.client.model.PartitionResolutionStrategy -import akka.http.scaladsl.unmarshalling._ -import akka.http.scaladsl.marshalling._ -import scala.concurrent.Await -import org.zalando.nakadi.client.model.Partition -import org.zalando.nakadi.client.model.EventEnrichmentStrategy -import org.zalando.nakadi.client.model.EventValidationStrategy +import akka.http.scaladsl.unmarshalling.FromEntityUnmarshaller trait Client { import Client._ @@ -68,7 +59,7 @@ trait Client { * @param name - Name of the EventType * @param event - Event to update */ - def updateEventType(name: String, eventType: EventType)(implicit marshaller: FromEntityUnmarshaller[EventType]): Future[Option[ClientError]] + def updateEventType(name: String, eventType: EventType)(implicit marshaller: ToEntityMarshaller[EventType]): Future[Option[ClientError]] /** * Deletes an EventType identified by its name. * @@ -158,100 +149,3 @@ object Client { case class ClientError(msg: String, status: Option[Int]) } -private[client] class ClientImpl(connection: Connection, charSet: String) extends Client { - import Client._ - implicit val materializer = connection.materializer - - val logger = Logger(LoggerFactory.getLogger(this.getClass)) - def metrics(): Future[Either[ClientError, HttpResponse]] = ??? - - def eventTypes()(implicit unmarshaller: FromEntityUnmarshaller[List[EventType]]): Future[Either[ClientError, Option[List[EventType]]]] = { - logGetError(connection.get(URI_EVENT_TYPES).flatMap(handleGetResponse(_))) - } - - def newEventType(eventType: EventType)(implicit marshaller: ToEntityMarshaller[EventType]): Future[Option[ClientError]] = { - logPostError(connection.post(URI_EVENT_TYPES, eventType).map(handlePostResponse(_))) - } - - def eventType(name: String)(implicit marshaller: FromEntityUnmarshaller[EventType]): Future[Either[ClientError, Option[EventType]]] = ??? - - def updateEventType(name: String, eventType: EventType)(implicit marshaller: FromEntityUnmarshaller[EventType]): Future[Option[ClientError]] = ??? - - def deleteEventType(name: String): Future[Option[ClientError]] = ??? - - def newEvent[T](name: String, event: T)(implicit marshaller: FromEntityUnmarshaller[T]): Future[Option[ClientError]] = ??? - - def events[T](name: String)(implicit marshaller: FromEntityUnmarshaller[T]): Future[Either[ClientError, Option[T]]] = ??? - - def partitions(name: String)(implicit marshaller: FromEntityUnmarshaller[Partition]): Future[Either[ClientError, Option[Partition]]] = ??? - - def partitionByName(name: String, partition: String)(implicit marshaller: FromEntityUnmarshaller[Partition]): Future[Either[ClientError, Option[Partition]]] = ??? - - def validationStrategies()(implicit marshaller: FromEntityUnmarshaller[EventValidationStrategy]): Future[Either[ClientError, Option[EventValidationStrategy]]] = ??? - - def enrichmentStrategies()(implicit marshaller: FromEntityUnmarshaller[EventEnrichmentStrategy]): Future[Either[ClientError, Option[EventEnrichmentStrategy]]] = ??? - - def partitionStrategies()(implicit marshaller: FromEntityUnmarshaller[List[PartitionResolutionStrategy]]): Future[Either[ClientError, Option[List[PartitionResolutionStrategy]]]] = - logGetError(connection.get(URI_PARTITIONING_STRATEGIES).flatMap(handleGetResponse(_))) - - def stop(): Future[Terminated] = connection.stop() - - //#################### - //# HELPER METHODS # - //#################### - - def logGetError[A, T](future: Future[Either[ClientError, T]]): Future[Either[ClientError, T]] = { - future recover { - case e: Throwable => - logger.error("A unexpected error occured", e) - Left(ClientError("Error: " + e.getMessage, None)) - } - } - def logPostError(future: Future[Option[ClientError]]): Future[Option[ClientError]] = { - future recover { - case e: Throwable => - logger.error("A unexpected error occured", e) - Option(ClientError("Error: " + e.getMessage, None)) - } - } - - def handleGetResponse[T](response: HttpResponse)(implicit unmarshaller: FromEntityUnmarshaller[T], m: Manifest[T]): Future[Either[ClientError, Option[T]]] = { - logger.debug("received [response={}]", response) - response.status match { - case status if (status.isSuccess()) => - logger.info("Result is successfull ({}) ", status.intValue().toString()) - Unmarshal(response.entity).to[T].map(in => Right(Option(in))) - case status if (status.isRedirection()) => - val msg = "An error occurred with http-status (" + status.intValue() + "}) and reason:" + status.reason() - logger.info(msg) - Future.successful(Left(ClientError(msg, Some(status.intValue())))) - case status if (status.isFailure()) => - val msg = "An error occurred with http-status (" + status.intValue() + "}) and reason:" + status.reason() - logger.warn(msg) - Future.successful(Left(ClientError(msg, Some(status.intValue())))) - } - } - def handlePostResponse[T](response: HttpResponse): Option[ClientError] = { - response.status match { - case status if (status.isSuccess()) => - logger.debug("Success. http-status: %s", status.intValue().toString()) - None - case status if (status.isRedirection()) => - val msg = "Redirection - http-status: %s, reason[%s]".format(status.intValue().toString(), status.reason()) - logger.info(msg) - response.entity.toStrict(10.second).map{ body => - logger.debug("Redirection - http-status: %s, reason[%s], body:[%s]".format(status.intValue().toString(), status.reason(), body.data.decodeString(charSet))) - } - Option(ClientError(msg, Some(status.intValue()))) - case status if (status.isFailure()) => - response.entity.toStrict(10.second).map{ body => - logger.debug("Failure - http-status: %s, reason[%s], body:[%s]".format(status.intValue().toString(), status.reason(), body.data.decodeString(charSet))) - } - val msg = "Failure - http-status: %s, reason[%s], body:[%s]".format(status.intValue().toString(), status.reason(), response.entity.dataBytes) - logger.warn(msg) - Option(ClientError(msg, Some(status.intValue()))) - } - } - -} - diff --git a/client/src/main/scala/org/zalando/nakadi/client/ClientImpl.scala b/client/src/main/scala/org/zalando/nakadi/client/ClientImpl.scala new file mode 100644 index 0000000..6e5063d --- /dev/null +++ b/client/src/main/scala/org/zalando/nakadi/client/ClientImpl.scala @@ -0,0 +1,122 @@ +package org.zalando.nakadi.client + +import scala.{ Left, Right } +import scala.concurrent.ExecutionContext.Implicits.global +import scala.concurrent.Future +import scala.concurrent.duration.DurationInt + +import org.slf4j.LoggerFactory +import org.zalando.nakadi.client.model.{ EventEnrichmentStrategy, EventType, EventValidationStrategy, Partition, PartitionResolutionStrategy } + +import com.typesafe.scalalogging.Logger + +import Client.ClientError +import akka.actor.Terminated +import akka.http.scaladsl.marshalling.ToEntityMarshaller +import akka.http.scaladsl.model.HttpResponse +import akka.http.scaladsl.unmarshalling.{ FromEntityUnmarshaller, Unmarshal } + +private[client] class ClientImpl(connection: Connection, charSet: String) extends Client { + import Client._ + implicit val materializer = connection.materializer + + val logger = Logger(LoggerFactory.getLogger(this.getClass)) + def metrics(): Future[Either[ClientError, HttpResponse]] = ??? + + def eventTypes()(implicit unmarshaller: FromEntityUnmarshaller[List[EventType]]): Future[Either[ClientError, Option[List[EventType]]]] = { + logFutureEither(connection.get(URI_EVENT_TYPES).flatMap(mapToEither(_))) + } + + def newEventType(eventType: EventType)(implicit marshaller: ToEntityMarshaller[EventType]): Future[Option[ClientError]] = { + logFutureOption(connection.post(URI_EVENT_TYPES, eventType).map(mapToOption(_))) + } + + def eventType(name: String)(implicit marshaller: FromEntityUnmarshaller[EventType]): Future[Either[ClientError, Option[EventType]]] = { + logFutureEither(connection.get(URI_EVENT_TYPE_BY_NAME.format(name)).flatMap(in => mapToEither(in))) + } + + def updateEventType(name: String, eventType: EventType)(implicit marshaller: ToEntityMarshaller[EventType]): Future[Option[ClientError]] = { + val result =connection.put(URI_EVENT_TYPE_BY_NAME.format(name),eventType) + logFutureOption(result.map(in => mapToOption(in))) + } + + def deleteEventType(name: String): Future[Option[ClientError]] = { + logFutureOption(connection.delete(URI_EVENT_TYPE_BY_NAME.format(name)).map(in => mapToOption(in))) + } + + def newEvent[T](name: String, event: T)(implicit marshaller: FromEntityUnmarshaller[T]): Future[Option[ClientError]] = ??? + + def events[T](name: String)(implicit marshaller: FromEntityUnmarshaller[T]): Future[Either[ClientError, Option[T]]] = ??? + + def partitions(name: String)(implicit marshaller: FromEntityUnmarshaller[Partition]): Future[Either[ClientError, Option[Partition]]] = ??? + + def partitionByName(name: String, partition: String)(implicit marshaller: FromEntityUnmarshaller[Partition]): Future[Either[ClientError, Option[Partition]]] = ??? + + def validationStrategies()(implicit marshaller: FromEntityUnmarshaller[EventValidationStrategy]): Future[Either[ClientError, Option[EventValidationStrategy]]] = ??? + + def enrichmentStrategies()(implicit marshaller: FromEntityUnmarshaller[EventEnrichmentStrategy]): Future[Either[ClientError, Option[EventEnrichmentStrategy]]] = ??? + + def partitionStrategies()(implicit marshaller: FromEntityUnmarshaller[List[PartitionResolutionStrategy]]): Future[Either[ClientError, Option[List[PartitionResolutionStrategy]]]] = + logFutureEither(connection.get(URI_PARTITIONING_STRATEGIES).flatMap(mapToEither(_))) + + def stop(): Future[Terminated] = connection.stop() + + //#################### + //# HELPER METHODS # + //#################### + + def logFutureEither[A, T](future: Future[Either[ClientError, T]]): Future[Either[ClientError, T]] = { + future recover { + case e: Throwable => + logger.error("A unexpected error occured", e) + Left(ClientError("Error: " + e.getMessage, None)) + } + } + def logFutureOption(future: Future[Option[ClientError]]): Future[Option[ClientError]] = { + future recover { + case e: Throwable => + logger.error("A unexpected error occured", e) + Option(ClientError("Error: " + e.getMessage, None)) + } + } + + def mapToEither[T](response: HttpResponse)(implicit unmarshaller: FromEntityUnmarshaller[T], m: Manifest[T]): Future[Either[ClientError, Option[T]]] = { + logger.debug("received [response={}]", response) + response.status match { + case status if (status.isSuccess()) => + logger.info("Result is successfull ({}) ", status.intValue().toString()) + Unmarshal(response.entity).to[T].map(in => Right(Option(in))) + case status if (status.isRedirection()) => + val msg = "An error occurred with http-status (" + status.intValue() + "}) and reason:" + status.reason() + logger.info(msg) + Future.successful(Left(ClientError(msg, Some(status.intValue())))) + case status if (status.isFailure()) => + val msg = "An error occurred with http-status (" + status.intValue() + "}) and reason:" + status.reason() + logger.warn(msg) + Future.successful(Left(ClientError(msg, Some(status.intValue())))) + } + } + def mapToOption[T](response: HttpResponse): Option[ClientError] = { + response.status match { + case status if (status.isSuccess()) => + logger.debug("Success. http-status: %s", status.intValue().toString()) + None + case status if (status.isRedirection()) => + val msg = "Redirection - http-status: %s, reason[%s]".format(status.intValue().toString(), status.reason()) + logger.info(msg) + response.entity.toStrict(10.second).map { body => + logger.debug("Redirection - http-status: %s, reason[%s], body:[%s]".format(status.intValue().toString(), status.reason(), body.data.decodeString(charSet))) + } + Option(ClientError(msg, Some(status.intValue()))) + case status if (status.isFailure()) => + response.entity.toStrict(10.second).map { body => + logger.debug("Failure - http-status: %s, reason[%s], body:[%s]".format(status.intValue().toString(), status.reason(), body.data.decodeString(charSet))) + } + val msg = "Failure - http-status: %s, reason[%s], body:[%s]".format(status.intValue().toString(), status.reason(), response.entity.dataBytes) + logger.warn(msg) + Option(ClientError(msg, Some(status.intValue()))) + } + } + +} + diff --git a/client/src/main/scala/org/zalando/nakadi/client/Connection.scala b/client/src/main/scala/org/zalando/nakadi/client/Connection.scala index 35aba00..6f826f9 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/Connection.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/Connection.scala @@ -25,7 +25,10 @@ import javax.net.ssl.{ SSLContext, TrustManager, X509TrustManager } trait Connection { def get(endpoint: String): Future[HttpResponse] - def post[T](endpoint: String, event: T)(implicit marshaller: ToEntityMarshaller[T]): Future[HttpResponse] + def delete(endpoint: String): Future[HttpResponse] + def post[T](endpoint: String, model: T)(implicit marshaller: ToEntityMarshaller[T]): Future[HttpResponse] + def put[T](endpoint: String, model: T)(implicit marshaller: ToEntityMarshaller[T]): Future[HttpResponse] + def stop(): Future[Terminated] def materializer(): ActorMaterializer } @@ -66,7 +69,7 @@ object Connection { private[client] class ConnectionImpl(host: String, port: Int, tokenProvider: () => String, securedConnection: Boolean, verifySSlCertificate: Boolean) extends Connection { import Connection._ - private implicit val actorSystem = ActorSystem("Nakadi-Connections") + private implicit val actorSystem = ActorSystem("Nakadi-Client-Connections") private implicit val http = Http(actorSystem) implicit val materializer = ActorMaterializer() private val timeout = 5.seconds @@ -81,38 +84,47 @@ private[client] class ConnectionImpl(host: String, port: Int, tokenProvider: () } def get(endpoint: String): Future[HttpResponse] = { - logger.info("Calling {}", endpoint) - val response: Future[HttpResponse] = - Source.single(GetHttpRequest(endpoint)) - .via(connectionFlow). - runWith(Sink.head) - logError(response) - response + logger.info("Get: {}", endpoint) + executeCall(httpRequest(endpoint, HttpMethods.GET)) + } + + def put[T](endpoint: String, model: T)(implicit marshaller: ToEntityMarshaller[T]): Future[HttpResponse] = { + logger.info("Get: {}", endpoint) + executeCall(httpRequest(endpoint, HttpMethods.GET)) } - def post[T](endpoint: String, event: T)(implicit marshaller: ToEntityMarshaller[T]): Future[HttpResponse] = { - val result = Marshal(event).to[MessageEntity].flatMap { entity => + + def post[T](endpoint: String, model: T)(implicit marshaller: ToEntityMarshaller[T]): Future[HttpResponse] = { + Marshal(model).to[MessageEntity].flatMap { entity => logger.info("Posting to endpoint {}", endpoint) logger.debug("Data to post {}", entity.toString()) - Source.single(PostHttpRequest(endpoint, entity)) - .via(connectionFlow). - runWith(Sink.head) + executeCall(httpRequestWithPayload(endpoint, entity, HttpMethods.POST)) } - logError(result) - result + } + def delete(endpoint: String): Future[HttpResponse] = { + logger.info("Delete: {}", endpoint) + executeCall(httpRequest(endpoint, HttpMethods.DELETE)) } + private def executeCall(request: HttpRequest): Future[HttpResponse] = { + val response: Future[HttpResponse] = + Source.single(request) + .via(connectionFlow). + runWith(Sink.head) + logError(response) + response + } private def logError(future: Future[Any]) { future recover { case e: Throwable => logger.error("Failed to call endpoint with: ", e.getMessage) } } - private def GetHttpRequest(url: String): HttpRequest = { - HttpRequest(uri = url).withHeaders(headers.Authorization(OAuth2BearerToken(tokenProvider())), + private def httpRequest(url: String, httpMethod: HttpMethod): HttpRequest = { + HttpRequest(uri = url, method = httpMethod).withHeaders(headers.Authorization(OAuth2BearerToken(tokenProvider())), headers.Accept(MediaRange(`application/json`))) } - private def PostHttpRequest(url: String, entity: MessageEntity): HttpRequest = { - HttpRequest(uri = url, method = POST) // + private def httpRequestWithPayload(url: String, entity: MessageEntity, httpMethod: HttpMethod): HttpRequest = { + HttpRequest(uri = url, method = httpMethod) // .withHeaders(headers.Authorization(OAuth2BearerToken(tokenProvider())), headers.Accept(MediaRange(`application/json`))) .withEntity(entity) diff --git a/it/src/test/scala/org/zalando/nakadi/client/EventTypesIntegrationTest.scala b/it/src/test/scala/org/zalando/nakadi/client/EventTypesIntegrationTest.scala new file mode 100644 index 0000000..39eb3a2 --- /dev/null +++ b/it/src/test/scala/org/zalando/nakadi/client/EventTypesIntegrationTest.scala @@ -0,0 +1,129 @@ +package org.zalando.nakadi.client + +import scala.concurrent.{ Await, Future } +import scala.concurrent.duration.DurationInt +import scala.util.Random + +import org.scalatest.{ Matchers, WordSpec } +import org.zalando.nakadi.client.model.{ EventType, EventTypeCategory, EventTypeSchema, PartitionResolutionStrategy, SchemaType, SprayJsonMarshaller } + +class EventTypeTest extends WordSpec with Matchers with SprayJsonMarshaller { + + //Client configuration + val host = "nakadi-sandbox.aruha-test.zalan.do" + val OAuth2Token = () => "e97311d9-bb5e-48fa-9a60-0b153726c71c" + val port = 443 + val client = new ClientImpl(Connection.newConnection(host, port, OAuth2Token, true, false), "UTF-8") + val events = new EventTypesActions(client) + + //EventType fields + val partitionStrategy = new PartitionResolutionStrategy("hash", None) + val paritionKeyFields = List("order_number") + val schemaDefinition = """{ "properties": { "order_number": { "type": "string" } } }""" + val eventTypeSchema = new EventTypeSchema(SchemaType.JSON, schemaDefinition) + + "POST/PUT/GET/DELETE single EventType " in { + + //Create event + val eventType = createUniqueEventType() + val creationResult = events.create(eventType) + creationResult.isDefined shouldBe false + + //Check the created EventType + checkEventTypeExists(eventType) + + + //TODO: Enable this when PUT is supported again. + //Update the event +// val updatedEvent = eventType.copy(owningApplication = "laas-team-2") +// events.update(updatedEvent) + + //Check the EventType has bee updated +// checkEventTypeExists(updatedEvent) +// checkEventTypeDoesNotExist(eventType) + + //Delete the created Event + val deletedEvent = events.delete(eventType.name) + deletedEvent.isEmpty shouldBe true + + //Is it really deleted? + checkEventTypeDoesNotExist(eventType) + } + + "POST/GET/DELETE multiple EventTypes " in { + + //Create 2 EventTypes + val eventType1 = createUniqueEventType() + val eventType2 = createUniqueEventType() + + events.create(eventType1) + checkEventTypeExists(eventType1) + + events.create(eventType2) + checkEventTypeExists(eventType2) + + //Get all EventTypes again + val Right(Some(allEvents)) = events.getAll() + allEvents should contain(eventType1) + allEvents should contain(eventType2) + + //Delete the 2 EventTypes + events.delete(eventType1.name) + events.delete(eventType2.name) + + //Check if the're really deleted + checkEventTypeDoesNotExist(eventType1) + checkEventTypeDoesNotExist(eventType2) + + //Get all should not contain the deleted events + val Right(Some(updatedEvents)) = events.getAll() + + updatedEvents shouldNot contain(eventType1) + updatedEvents shouldNot contain(eventType2) + + } + + def checkEventTypeDoesNotExist(eventType: EventType) = { + val requestedEvent = events.get(eventType.name) + requestedEvent.isLeft shouldBe true + val Left(result) = requestedEvent + result.status shouldBe Some(404) + } + + def checkEventTypeExists(eventType: EventType) = { + val Right(Some(createdEvent)) = events.get(eventType.name) + createdEvent shouldBe eventType + } + + private def createUniqueEventType(): EventType = { + new EventType("test-client-integration-event-" + Random.nextInt() + "-" + Random.nextInt() + Random.nextInt(), // + "laas-team", // + EventTypeCategory.BUSINESS, None, None, // + partitionStrategy, Option(eventTypeSchema), // + Option(paritionKeyFields), None, None) + } + +} + +class EventTypesActions(client: Client) extends SprayJsonMarshaller { + + def create(event: EventType) = { + executeCall(client.newEventType(event)) + } + def update(event: EventType) = { + executeCall(client.updateEventType(event.name, event)) + } + def get(name: String) = { + executeCall(client.eventType(name)) + } + def getAll() = { + executeCall(client.eventTypes()) + } + def delete(name: String) = { + executeCall(client.deleteEventType(name)) + } + + private def executeCall[T](call: => Future[T]): T = { + Await.result(call, 10.second) + } +} \ No newline at end of file diff --git a/it/src/test/scala/org/zalando/nakadi/client/KlientIntegrationTest.scala b/it/src/test/scala/org/zalando/nakadi/client/KlientIntegrationTest.scala index 3293d72..387fec0 100644 --- a/it/src/test/scala/org/zalando/nakadi/client/KlientIntegrationTest.scala +++ b/it/src/test/scala/org/zalando/nakadi/client/KlientIntegrationTest.scala @@ -10,56 +10,31 @@ import org.zalando.nakadi.client.model._ import scala.util.Random class KlientIntegrationTest extends WordSpec with Matchers with SprayJsonMarshaller { - - val host = "" - val OAuth2Token = () => "" + val host = "nakadi-sandbox.aruha-test.zalan.do" + val OAuth2Token = () => "41c7e243-fe44-4aa9-9df1-e63a5de8aed6" val port = 443 val client = new ClientImpl(Connection.newConnection(host, port, OAuth2Token, true, false), "UTF-8") - + //Defaults val partitionStrategy = new PartitionResolutionStrategy("hash", None) val paritionKeyFields = List("order_number") val schemaDefinition = """{ "properties": { "order_number": { "type": "string" } } }""" val eventTypeSchema = new EventTypeSchema(SchemaType.JSON, schemaDefinition) + val actions = new EventTypesActions(client) + "Nakadi Client" should { "parse multiple PartitionResolutionStrategy" in { val Right(result) = executeCall(client.partitionStrategies()) result.size should be > 0 } - "Event POST/GET/DELETE" in { - val random = Random.nextInt() - val name = "test-client-integration"+random - val team = "laas-team" - val eventType = new EventType(name, team, EventTypeCategory.BUSINESS, - None, None, partitionStrategy, Option(eventTypeSchema), Option(paritionKeyFields), None, None) - val creationResult = executeCall(client.newEventType(eventType)) - creationResult.isDefined shouldBe false - //Now get it back - val Right(Some(result)) = executeCall(client.eventType(name)) - result == eventType - - } - "parse multiple EventType" in { - val eventType = new EventType("nakadi-client-test", - "laas-team", - EventTypeCategory.UNDEFINED, None, None, - partitionStrategy, - None, - None, - None, - None) - client.newEventType(eventType) - val Right(result) = Await.result(client.eventTypes(), 10.second) - result.size should be > 20 - } - - def executeCall[T](call: => Future[T]): T = { - Await.result(call, 10.second) - } + + } - + private def executeCall[T](call: => Future[T]): T = { + Await.result(call, 10.second) + } } From f8d799638db9db71d2f309788b9f0a89a3131187 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Mon, 4 Apr 2016 16:05:50 +0200 Subject: [PATCH 012/183] Added Integration tests for EventTypes --- .../zalando/nakadi/client/EventTypesIntegrationTest.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/it/src/test/scala/org/zalando/nakadi/client/EventTypesIntegrationTest.scala b/it/src/test/scala/org/zalando/nakadi/client/EventTypesIntegrationTest.scala index 39eb3a2..4acb17d 100644 --- a/it/src/test/scala/org/zalando/nakadi/client/EventTypesIntegrationTest.scala +++ b/it/src/test/scala/org/zalando/nakadi/client/EventTypesIntegrationTest.scala @@ -10,8 +10,8 @@ import org.zalando.nakadi.client.model.{ EventType, EventTypeCategory, EventType class EventTypeTest extends WordSpec with Matchers with SprayJsonMarshaller { //Client configuration - val host = "nakadi-sandbox.aruha-test.zalan.do" - val OAuth2Token = () => "e97311d9-bb5e-48fa-9a60-0b153726c71c" + val host = "" + val OAuth2Token = () => "" val port = 443 val client = new ClientImpl(Connection.newConnection(host, port, OAuth2Token, true, false), "UTF-8") val events = new EventTypesActions(client) @@ -33,7 +33,7 @@ class EventTypeTest extends WordSpec with Matchers with SprayJsonMarshaller { checkEventTypeExists(eventType) - //TODO: Enable this when PUT is supported again. + //TODO: Enable this when PUT is supported. //Update the event // val updatedEvent = eventType.copy(owningApplication = "laas-team-2") // events.update(updatedEvent) From 73329c97de734fd252154ae967fb5b8b9da91952 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Tue, 5 Apr 2016 09:16:08 +0200 Subject: [PATCH 013/183] LAAS-60 Getting events is done --- client/src/test/scala/org/zalando/nakadi/client/App.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/src/test/scala/org/zalando/nakadi/client/App.scala b/client/src/test/scala/org/zalando/nakadi/client/App.scala index 28286f4..1ff27fe 100644 --- a/client/src/test/scala/org/zalando/nakadi/client/App.scala +++ b/client/src/test/scala/org/zalando/nakadi/client/App.scala @@ -4,8 +4,8 @@ import org.zalando.nakadi.client.model.JacksonJsonMarshaller object Main extends App with JacksonJsonMarshaller { - val host = "nakadi-beta.aruha.zalan.do" - val OAuth2Token = () => "4ae2a575-ebd7-40c5-a4de-851b387e00f5" + val host = "" + val OAuth2Token = () => "" val port = 443 val client = new ClientImpl(Connection.newConnection(host, port, OAuth2Token, true, false),"UTF-8") From 0ce9ac6b393fcc0e4adf511672acb64f69f8bd69 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Tue, 12 Apr 2016 11:53:24 +0200 Subject: [PATCH 014/183] Adding sbt-eclipse --- project/plugins.sbt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/project/plugins.sbt b/project/plugins.sbt index ab40f29..ca80229 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -7,3 +7,5 @@ addSbtPlugin("org.scoverage" % "sbt-scoverage" % "1.0.4") addSbtPlugin("org.scoverage" % "sbt-coveralls" % "1.0.0") addSbtPlugin("com.codacy" % "sbt-codacy-coverage" % "1.1.0") + +addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "4.0.0") From 2a81c81f405a5f13f387b6e660a7fc2add1b2a10 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Wed, 13 Apr 2016 11:20:29 +0200 Subject: [PATCH 015/183] LAAS-60 Finished implementing the client's low-level API --- client/src/main/resources/reference.conf | 2 +- .../org/zalando/nakadi/client/Client.scala | 33 ++- .../zalando/nakadi/client/ClientImpl.scala | 91 +++++--- .../zalando/nakadi/client/Connection.scala | 45 ++-- .../zalando/nakadi/client/Serialization.scala | 17 ++ .../client/model/JacksonJsonMarshaller.scala | 146 ++++++------ .../client/model/SprayJsonMarshaller.scala | 131 +++++++---- .../org/zalando/nakadi/client/package.scala | 4 +- client/src/test/resources/Events.txt | 214 ++++++++++++++++++ client/src/test/resources/application.conf | 4 +- .../scala/org/zalando/nakadi/client/App.scala | 17 -- .../zalando/nakadi/client/ClientTest.scala | 59 +++++ .../client/DeserializerSerializerTest.scala | 37 +-- .../zalando/nakadi/client/EnumModule.scala | 67 ++++++ .../client/SerializerDeserializerTest.scala | 53 ++--- .../zalando/nakadi/client/TestFactory.scala | 73 ++++++ .../nakadi/client/util/TestJsonEntity.scala | 55 ++++- .../nakadi/client/util/TestScalaEntity.scala | 24 +- .../zalando/nakadi/client/config/App.scala | 17 -- .../client/EventTypesIntegrationTest.scala | 89 +++----- .../nakadi/client/KlientIntegrationTest.scala | 83 +++++-- .../zalando/nakadi/client/model/Model.scala | 67 +++--- project/Build.scala | 2 +- project/Dependencies.scala | 8 +- 24 files changed, 935 insertions(+), 403 deletions(-) create mode 100644 client/src/main/scala/org/zalando/nakadi/client/Serialization.scala create mode 100644 client/src/test/resources/Events.txt delete mode 100644 client/src/test/scala/org/zalando/nakadi/client/App.scala create mode 100644 client/src/test/scala/org/zalando/nakadi/client/ClientTest.scala create mode 100644 client/src/test/scala/org/zalando/nakadi/client/EnumModule.scala create mode 100644 client/src/test/scala/org/zalando/nakadi/client/TestFactory.scala delete mode 100644 it/src/main/scala/org/zalando/nakadi/client/config/App.scala diff --git a/client/src/main/resources/reference.conf b/client/src/main/resources/reference.conf index 9c1f394..a750c17 100644 --- a/client/src/main/resources/reference.conf +++ b/client/src/main/resources/reference.conf @@ -27,4 +27,4 @@ nakadi.client { } -akka.cluster.metrics.enabled=off +akka.cluster.metrics.enabled=false diff --git a/client/src/main/scala/org/zalando/nakadi/client/Client.scala b/client/src/main/scala/org/zalando/nakadi/client/Client.scala index b5f4657..089a8fd 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/Client.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/Client.scala @@ -1,13 +1,10 @@ package org.zalando.nakadi.client import scala.concurrent.Future - import org.zalando.nakadi.client.model.{ EventEnrichmentStrategy, EventType, EventValidationStrategy, Partition, PartitionResolutionStrategy } - import akka.actor.Terminated -import akka.http.scaladsl.marshalling.ToEntityMarshaller import akka.http.scaladsl.model.HttpResponse -import akka.http.scaladsl.unmarshalling.FromEntityUnmarshaller +import org.zalando.nakadi.client.model.Metrics trait Client { import Client._ @@ -19,7 +16,7 @@ trait Client { * curl --request GET /metrics * }}} */ - def metrics(): Future[Either[ClientError, HttpResponse]] + def metrics()(implicit ser: Deserializer[Metrics]): Future[Either[ClientError, Option[Metrics]]] /** * Returns a list of all registered EventTypes. @@ -29,7 +26,7 @@ trait Client { * }}} * */ - def eventTypes()(implicit marshaller: FromEntityUnmarshaller[List[EventType]]): Future[Either[ClientError, Option[List[EventType]]]] + def eventTypes()(implicit ser: Deserializer[Seq[EventType]]): Future[Either[ClientError, Option[Seq[EventType]]]] /** * Creates a new EventType. @@ -41,7 +38,7 @@ trait Client { * @param event - The EventType to create. * */ - def newEventType(eventType: EventType)(implicit marshaller: ToEntityMarshaller[EventType]): Future[Option[ClientError]] + def newEventType(eventType: EventType)(implicit ser: Serializer[EventType]): Future[Option[ClientError]] /** * Returns the EventType identified by its name. @@ -50,7 +47,7 @@ trait Client { * }}} * @param name - Name of the EventType */ - def eventType(name: String)(implicit marshaller: FromEntityUnmarshaller[EventType]): Future[Either[ClientError, Option[EventType]]] + def eventType(name: String)(implicit ser: Deserializer[EventType]): Future[Either[ClientError, Option[EventType]]] /** * Updates the EventType identified by its name. * {{{ @@ -59,7 +56,7 @@ trait Client { * @param name - Name of the EventType * @param event - Event to update */ - def updateEventType(name: String, eventType: EventType)(implicit marshaller: ToEntityMarshaller[EventType]): Future[Option[ClientError]] + def updateEventType(name: String, eventType: EventType)(implicit ser: Serializer[EventType]): Future[Option[ClientError]] /** * Deletes an EventType identified by its name. * @@ -72,14 +69,14 @@ trait Client { def deleteEventType(name: String): Future[Option[ClientError]] /** - * Creates a new Event for the given EventType. + * Creates a new batch of Events for the given EventType. * {{{ * curl --request POST -d @fileWithEvent /event-types/{name}/events * }}} * @param name - Name of the EventType * @param event - Event to create */ - def newEvent[T](name: String, event: T)(implicit marshaller: FromEntityUnmarshaller[T]): Future[Option[ClientError]] + def newEvents[T](name: String, events: Seq[T])(implicit ser: Serializer[Seq[T]]): Future[Option[ClientError]] /** * Request a stream delivery for the specified partitions of the given EventType. @@ -89,7 +86,7 @@ trait Client { * @param name - Name of the EventType * */ - def events[T](name: String)(implicit marshaller: FromEntityUnmarshaller[T]): Future[Either[ClientError, Option[T]]] + def events[T](name: String)(implicit ser: Deserializer[T]): Future[Either[ClientError, Option[T]]] /** * List the partitions for the given EventType. @@ -98,7 +95,7 @@ trait Client { * }}} * @param name - Name of the EventType */ - def partitions(name: String)(implicit marshaller: FromEntityUnmarshaller[Partition]): Future[Either[ClientError, Option[Partition]]] + def partitions(name: String)(implicit ser: Deserializer[Partition]): Future[Either[ClientError, Option[Partition]]] /** * Returns the Partition for the given EventType. @@ -106,10 +103,10 @@ trait Client { * curl --request GET /event-types/{name}/partitions/{partition} * }}} * @param name - Name of the EventType - * @param partition - Name of the partition + * @param partition - Partition id for the given EventType */ - def partitionByName(name: String, partition: String)(implicit marshaller: FromEntityUnmarshaller[Partition]): Future[Either[ClientError, Option[Partition]]] + def partitionById(name: String, id: String)(implicit ser: Deserializer[Partition]): Future[Either[ClientError, Option[Partition]]] /** * Returns all of the validation strategies supported by this installation of Nakadi. @@ -118,7 +115,7 @@ trait Client { * curl --request GET /registry/validation-strategies * }}} */ - def validationStrategies()(implicit marshaller: FromEntityUnmarshaller[EventValidationStrategy]): Future[Either[ClientError, Option[EventValidationStrategy]]] + def validationStrategies()(implicit des: Deserializer[Seq[EventValidationStrategy]]): Future[Either[ClientError, Option[Seq[EventValidationStrategy]]]] /** * Returns all of the enrichment strategies supported by this installation of Nakadi. @@ -127,7 +124,7 @@ trait Client { * }}} */ - def enrichmentStrategies()(implicit marshaller: FromEntityUnmarshaller[EventEnrichmentStrategy]): Future[Either[ClientError, Option[EventEnrichmentStrategy]]] + def enrichmentStrategies()(implicit des: Deserializer[Seq[EventEnrichmentStrategy]]): Future[Either[ClientError, Option[Seq[EventEnrichmentStrategy]]]] /** * Returns all of the partitioning strategies supported by this installation of Nakadi. @@ -135,7 +132,7 @@ trait Client { * curl --request GET /registry/partitioning-strategies * }}} */ - def partitionStrategies()(implicit marshaller: FromEntityUnmarshaller[List[PartitionResolutionStrategy]]): Future[Either[ClientError, Option[List[PartitionResolutionStrategy]]]] + def partitionStrategies()(implicit des: Deserializer[Seq[PartitionResolutionStrategy]]): Future[Either[ClientError, Option[Seq[PartitionResolutionStrategy]]]] /** * Shuts down the communication system of the client diff --git a/client/src/main/scala/org/zalando/nakadi/client/ClientImpl.scala b/client/src/main/scala/org/zalando/nakadi/client/ClientImpl.scala index 6e5063d..dac211c 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/ClientImpl.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/ClientImpl.scala @@ -4,39 +4,44 @@ import scala.{ Left, Right } import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.Future import scala.concurrent.duration.DurationInt - +import akka.http.scaladsl.model.HttpResponse import org.slf4j.LoggerFactory import org.zalando.nakadi.client.model.{ EventEnrichmentStrategy, EventType, EventValidationStrategy, Partition, PartitionResolutionStrategy } - import com.typesafe.scalalogging.Logger - import Client.ClientError import akka.actor.Terminated -import akka.http.scaladsl.marshalling.ToEntityMarshaller import akka.http.scaladsl.model.HttpResponse -import akka.http.scaladsl.unmarshalling.{ FromEntityUnmarshaller, Unmarshal } - -private[client] class ClientImpl(connection: Connection, charSet: String) extends Client { +import scala.util.Try +import scala.util.Failure +import scala.util.Success +import spray.json.DeserializationException +import akka.http.scaladsl.model.StatusCodes +import akka.http.scaladsl.unmarshalling.Unmarshal +import org.zalando.nakadi.client.model.Metrics + +private[client] class ClientImpl(connection: Connection, charSet: String = "UTF-8") extends Client { import Client._ implicit val materializer = connection.materializer val logger = Logger(LoggerFactory.getLogger(this.getClass)) - def metrics(): Future[Either[ClientError, HttpResponse]] = ??? + def metrics()(implicit ser: Deserializer[Metrics]): Future[Either[ClientError, Option[Metrics]]] ={ + logFutureEither(connection.get(URI_METRICS).flatMap(mapToEither(_))) + } - def eventTypes()(implicit unmarshaller: FromEntityUnmarshaller[List[EventType]]): Future[Either[ClientError, Option[List[EventType]]]] = { + def eventTypes()(implicit ser: Deserializer[Seq[EventType]]): Future[Either[ClientError, Option[Seq[EventType]]]] = { logFutureEither(connection.get(URI_EVENT_TYPES).flatMap(mapToEither(_))) } - def newEventType(eventType: EventType)(implicit marshaller: ToEntityMarshaller[EventType]): Future[Option[ClientError]] = { + def newEventType(eventType: EventType)(implicit ser: Serializer[EventType]): Future[Option[ClientError]] = { logFutureOption(connection.post(URI_EVENT_TYPES, eventType).map(mapToOption(_))) } - def eventType(name: String)(implicit marshaller: FromEntityUnmarshaller[EventType]): Future[Either[ClientError, Option[EventType]]] = { + def eventType(name: String)(implicit ser: Deserializer[EventType]): Future[Either[ClientError, Option[EventType]]] = { logFutureEither(connection.get(URI_EVENT_TYPE_BY_NAME.format(name)).flatMap(in => mapToEither(in))) } - def updateEventType(name: String, eventType: EventType)(implicit marshaller: ToEntityMarshaller[EventType]): Future[Option[ClientError]] = { - val result =connection.put(URI_EVENT_TYPE_BY_NAME.format(name),eventType) + def updateEventType(name: String, eventType: EventType)(implicit ser: Serializer[EventType]): Future[Option[ClientError]] = { + val result = connection.put(URI_EVENT_TYPE_BY_NAME.format(name), eventType) logFutureOption(result.map(in => mapToOption(in))) } @@ -44,19 +49,32 @@ private[client] class ClientImpl(connection: Connection, charSet: String) extend logFutureOption(connection.delete(URI_EVENT_TYPE_BY_NAME.format(name)).map(in => mapToOption(in))) } - def newEvent[T](name: String, event: T)(implicit marshaller: FromEntityUnmarshaller[T]): Future[Option[ClientError]] = ??? + def newEvents[T](name: String, events: Seq[T])(implicit ser: Serializer[Seq[T]]): Future[Option[ClientError]] = { + logFutureOption(connection.post(URI_EVENTS_OF_EVENT_TYPE.format(name), events).map(in => mapToOption(in))) + } - def events[T](name: String)(implicit marshaller: FromEntityUnmarshaller[T]): Future[Either[ClientError, Option[T]]] = ??? + def events[T](name: String)(implicit ser: Deserializer[T]): Future[Either[ClientError, Option[T]]] = { + logFutureEither(connection.get(URI_EVENTS_OF_EVENT_TYPE.format(name)).flatMap(in => mapToEither(in))) + } - def partitions(name: String)(implicit marshaller: FromEntityUnmarshaller[Partition]): Future[Either[ClientError, Option[Partition]]] = ??? + + def partitions(name: String)(implicit ser: Deserializer[Partition]): Future[Either[ClientError, Option[Partition]]] = { + logFutureEither(connection.get(URI_PARTITIONS_BY_EVENT_TYPE.format(name)).flatMap(in => mapToEither(in))) + } - def partitionByName(name: String, partition: String)(implicit marshaller: FromEntityUnmarshaller[Partition]): Future[Either[ClientError, Option[Partition]]] = ??? + def partitionById(name: String, id: String)(implicit ser: Deserializer[Partition]): Future[Either[ClientError, Option[Partition]]] = { + logFutureEither(connection.get(URI_PARTITION_BY_EVENT_TYPE_AND_ID.format(name)).flatMap(in => mapToEither(in))) + } - def validationStrategies()(implicit marshaller: FromEntityUnmarshaller[EventValidationStrategy]): Future[Either[ClientError, Option[EventValidationStrategy]]] = ??? + def validationStrategies()(implicit des: Deserializer[Seq[EventValidationStrategy]]): Future[Either[ClientError, Option[Seq[EventValidationStrategy]]]] = { + logFutureEither(connection.get(URI_VALIDATION_STRATEGIES).flatMap(mapToEither(_))) + } - def enrichmentStrategies()(implicit marshaller: FromEntityUnmarshaller[EventEnrichmentStrategy]): Future[Either[ClientError, Option[EventEnrichmentStrategy]]] = ??? + def enrichmentStrategies()(implicit des: Deserializer[Seq[EventEnrichmentStrategy]]): Future[Either[ClientError, Option[Seq[EventEnrichmentStrategy]]]] = { + logFutureEither(connection.get(URI_ENRICHMENT_STRATEGIES).flatMap(mapToEither(_))) + } - def partitionStrategies()(implicit marshaller: FromEntityUnmarshaller[List[PartitionResolutionStrategy]]): Future[Either[ClientError, Option[List[PartitionResolutionStrategy]]]] = + def partitionStrategies()(implicit des: Deserializer[Seq[PartitionResolutionStrategy]]): Future[Either[ClientError, Option[Seq[PartitionResolutionStrategy]]]] = logFutureEither(connection.get(URI_PARTITIONING_STRATEGIES).flatMap(mapToEither(_))) def stop(): Future[Terminated] = connection.stop() @@ -80,22 +98,33 @@ private[client] class ClientImpl(connection: Connection, charSet: String) extend } } - def mapToEither[T](response: HttpResponse)(implicit unmarshaller: FromEntityUnmarshaller[T], m: Manifest[T]): Future[Either[ClientError, Option[T]]] = { + def mapToEither[T](response: HttpResponse)(implicit deserializer: Deserializer[T]): Future[Either[ClientError, Option[T]]] = { logger.debug("received [response={}]", response) - response.status match { - case status if (status.isSuccess()) => - logger.info("Result is successfull ({}) ", status.intValue().toString()) - Unmarshal(response.entity).to[T].map(in => Right(Option(in))) - case status if (status.isRedirection()) => - val msg = "An error occurred with http-status (" + status.intValue() + "}) and reason:" + status.reason() + response match { + case HttpResponse(status, headers, entity, protocol) if (status.isSuccess()) => + try { + Unmarshal(entity).to[String].map(body => Right(Some(deserializer.fromJson(body)))) + } catch { + case e: Throwable => + val msg = "Failed to deserialise the content with error: %s".format(e.getMessage) + logger.error(msg) + Future.successful(Left(ClientError(msg, Some(status.intValue())))) + } + case HttpResponse(StatusCodes.NotFound, headers, entity, protocol) => + Future.successful(Right(None)) + case HttpResponse(status, headers, entity, protocol) if (status.isRedirection()) => + val msg = "Not implemented: http-status (" + status.intValue() + "}) and reason:" + status.reason() logger.info(msg) Future.successful(Left(ClientError(msg, Some(status.intValue())))) - case status if (status.isFailure()) => - val msg = "An error occurred with http-status (" + status.intValue() + "}) and reason:" + status.reason() - logger.warn(msg) - Future.successful(Left(ClientError(msg, Some(status.intValue())))) + case HttpResponse(status, headers, entity, protocol) if (status.isFailure()) => + Unmarshal(entity).to[String].map { body => + val msg = "An error occurred, http-status: %s (%s) Message: %s".format(status.intValue(), status.reason(), body) + logger.warn(msg) + Left(ClientError(msg, Some(status.intValue()))) + } } } + def mapToOption[T](response: HttpResponse): Option[ClientError] = { response.status match { case status if (status.isSuccess()) => diff --git a/client/src/main/scala/org/zalando/nakadi/client/Connection.scala b/client/src/main/scala/org/zalando/nakadi/client/Connection.scala index 6f826f9..e5a62df 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/Connection.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/Connection.scala @@ -10,24 +10,35 @@ import scala.concurrent.duration.DurationInt import org.slf4j.LoggerFactory import com.typesafe.scalalogging.Logger -import akka.http.scaladsl.marshalling._ -import akka.actor.{ ActorSystem, Terminated } -import akka.http.scaladsl.{ Http, HttpsConnectionContext } -import akka.http.scaladsl.model._ -import akka.http.scaladsl.model.MediaTypes.`application/json` + +import akka.actor.ActorSystem +import akka.actor.Terminated +import akka.http.scaladsl.Http +import akka.http.scaladsl.HttpsConnectionContext +import akka.http.scaladsl.model.ContentType +import akka.http.scaladsl.model.HttpMethod +import akka.http.scaladsl.model.HttpMethods import akka.http.scaladsl.model.HttpMethods.POST +import akka.http.scaladsl.model.HttpRequest +import akka.http.scaladsl.model.HttpResponse +import akka.http.scaladsl.model.MediaRange +import akka.http.scaladsl.model.MediaTypes.`application/json` import akka.http.scaladsl.model.Uri.apply import akka.http.scaladsl.model.headers import akka.http.scaladsl.model.headers.OAuth2BearerToken import akka.stream.ActorMaterializer -import akka.stream.scaladsl.{ Flow, Sink, Source } -import javax.net.ssl.{ SSLContext, TrustManager, X509TrustManager } +import akka.stream.scaladsl.Flow +import akka.stream.scaladsl.Sink +import akka.stream.scaladsl.Source +import javax.net.ssl.SSLContext +import javax.net.ssl.TrustManager +import javax.net.ssl.X509TrustManager trait Connection { def get(endpoint: String): Future[HttpResponse] def delete(endpoint: String): Future[HttpResponse] - def post[T](endpoint: String, model: T)(implicit marshaller: ToEntityMarshaller[T]): Future[HttpResponse] - def put[T](endpoint: String, model: T)(implicit marshaller: ToEntityMarshaller[T]): Future[HttpResponse] + def post[T](endpoint: String, model: T)(implicit serializer: Serializer[T]): Future[HttpResponse] + def put[T](endpoint: String, model: T)(implicit serializer: Serializer[T]): Future[HttpResponse] def stop(): Future[Terminated] def materializer(): ActorMaterializer @@ -88,18 +99,18 @@ private[client] class ConnectionImpl(host: String, port: Int, tokenProvider: () executeCall(httpRequest(endpoint, HttpMethods.GET)) } - def put[T](endpoint: String, model: T)(implicit marshaller: ToEntityMarshaller[T]): Future[HttpResponse] = { + def put[T](endpoint: String, model: T)(implicit serializer: Serializer[T]): Future[HttpResponse] = { logger.info("Get: {}", endpoint) executeCall(httpRequest(endpoint, HttpMethods.GET)) } - def post[T](endpoint: String, model: T)(implicit marshaller: ToEntityMarshaller[T]): Future[HttpResponse] = { - Marshal(model).to[MessageEntity].flatMap { entity => + def post[T](endpoint: String, model: T)(implicit serializer: Serializer[T]): Future[HttpResponse] = { + val entity = serializer.toJson(model) logger.info("Posting to endpoint {}", endpoint) - logger.debug("Data to post {}", entity.toString()) + logger.debug("Data to post {}", entity) executeCall(httpRequestWithPayload(endpoint, entity, HttpMethods.POST)) - } } + def delete(endpoint: String): Future[HttpResponse] = { logger.info("Delete: {}", endpoint) executeCall(httpRequest(endpoint, HttpMethods.DELETE)) @@ -113,6 +124,7 @@ private[client] class ConnectionImpl(host: String, port: Int, tokenProvider: () logError(response) response } + private def logError(future: Future[Any]) { future recover { case e: Throwable => logger.error("Failed to call endpoint with: ", e.getMessage) @@ -123,11 +135,12 @@ private[client] class ConnectionImpl(host: String, port: Int, tokenProvider: () HttpRequest(uri = url, method = httpMethod).withHeaders(headers.Authorization(OAuth2BearerToken(tokenProvider())), headers.Accept(MediaRange(`application/json`))) } - private def httpRequestWithPayload(url: String, entity: MessageEntity, httpMethod: HttpMethod): HttpRequest = { + + private def httpRequestWithPayload(url: String, entity: String, httpMethod: HttpMethod): HttpRequest = { HttpRequest(uri = url, method = httpMethod) // .withHeaders(headers.Authorization(OAuth2BearerToken(tokenProvider())), headers.Accept(MediaRange(`application/json`))) - .withEntity(entity) + .withEntity(ContentType(`application/json`),entity) } def stop(): Future[Terminated] = actorSystem.terminate() diff --git a/client/src/main/scala/org/zalando/nakadi/client/Serialization.scala b/client/src/main/scala/org/zalando/nakadi/client/Serialization.scala new file mode 100644 index 0000000..06251a9 --- /dev/null +++ b/client/src/main/scala/org/zalando/nakadi/client/Serialization.scala @@ -0,0 +1,17 @@ +package org.zalando.nakadi.client + + +/** + * + */ +trait Deserializer[T] { + def fromJson(from: String): T +} + +/** + * + */ +trait Serializer[T] { + def toJson(from: T): String +} + diff --git a/client/src/main/scala/org/zalando/nakadi/client/model/JacksonJsonMarshaller.scala b/client/src/main/scala/org/zalando/nakadi/client/model/JacksonJsonMarshaller.scala index 2683eea..5600067 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/model/JacksonJsonMarshaller.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/model/JacksonJsonMarshaller.scala @@ -1,88 +1,90 @@ package org.zalando.nakadi.client.model -import scala.annotation.implicitNotFound -import scala.concurrent.{ ExecutionContext, Future } -import scala.reflect.Manifest +import scala.reflect.runtime.universe import org.slf4j.LoggerFactory -import com.fasterxml.jackson.core.{ JsonFactory, JsonParser } +import org.zalando.nakadi.client.Deserializer +import org.zalando.nakadi.client.Serializer +import org.zalando.nakadi.client.model.BatchItemPublishingStatus.BatchItemPublishingStatus +import org.zalando.nakadi.client.model.BatchItemStep.BatchItemStep +import org.zalando.nakadi.client.model.DataOperation.DataOperation +import org.zalando.nakadi.client.model.EventTypeCategory.EventTypeCategory +import org.zalando.nakadi.client.model.SchemaType.SchemaType +import com.fasterxml.jackson.core.JsonFactory +import com.fasterxml.jackson.core.JsonGenerator +import com.fasterxml.jackson.core.JsonParser +import com.fasterxml.jackson.core.Version import com.fasterxml.jackson.core.`type`.TypeReference -import com.fasterxml.jackson.databind.{ DeserializationContext, DeserializationFeature, JsonDeserializer, ObjectMapper, PropertyNamingStrategy, SerializationFeature } +import com.fasterxml.jackson.databind.DeserializationContext +import com.fasterxml.jackson.databind.DeserializationFeature +import com.fasterxml.jackson.databind.JsonDeserializer +import com.fasterxml.jackson.databind.JsonSerializer +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.databind.PropertyNamingStrategy +import com.fasterxml.jackson.databind.SerializationFeature +import com.fasterxml.jackson.databind.SerializerProvider import com.fasterxml.jackson.databind.deser.DeserializationProblemHandler +import com.fasterxml.jackson.databind.module.SimpleModule import com.fasterxml.jackson.module.scala.DefaultScalaModule import com.typesafe.scalalogging.Logger -import akka.http.scaladsl.marshalling.Marshaller -import akka.http.scaladsl.model.HttpEntity -import akka.http.scaladsl.unmarshalling.Unmarshaller -import akka.stream.ActorMaterializer -import javax.annotation.PostConstruct -import com.fasterxml.jackson.databind.JsonSerializer -import com.fasterxml.jackson.core.JsonGenerator -import com.fasterxml.jackson.databind.SerializerProvider +import scala.reflect._ +import scala.reflect.runtime.universe +import scala.reflect.runtime.universe._ +import com.fasterxml.jackson.databind.ser.std.StdScalarSerializer +import com.fasterxml.jackson.annotation.JsonInclude + + + trait JacksonJsonMarshaller { - import JacksonJsonMarshaller._ + val logger = Logger(LoggerFactory.getLogger(this.getClass)) val factory = new JsonFactory(); - class DataOperationType extends TypeReference[DataOperation.type] - class EventTypeCategoryType extends TypeReference[EventTypeCategory.type] - class BatchItemStepType extends TypeReference[BatchItemStep.type] - class BatchItemPublishingStatusType extends TypeReference[BatchItemPublishingStatus.type] - implicit val DataOperationTypeTypeReference = new TypeReference[DataOperationType] {} - implicit val EventTypeCategoryTypeTypeReference = new TypeReference[EventTypeCategoryType] {} - implicit val BatchItemStepTypeTypeReference = new TypeReference[BatchItemStepType] {} - implicit val BatchItemPublishingStatusTypeTypeReference = new TypeReference[BatchItemPublishingStatusType] {} + // All TypeReferences + implicit def problemTR: TypeReference[Problem] = new TypeReference[Problem] {} + implicit def metricsTR: TypeReference[Metrics] = new TypeReference[Metrics] {} + implicit def partitionTR: TypeReference[Partition] = new TypeReference[Partition] {} + implicit def cursorTR: TypeReference[Cursor] = new TypeReference[Cursor] {} + implicit def eventTypeSchemaTR: TypeReference[EventTypeSchema] = new TypeReference[EventTypeSchema] {} + implicit def eventValidationStrategyTR: TypeReference[EventValidationStrategy] = new TypeReference[EventValidationStrategy] {} + implicit def partitionResolutionStrategyTR: TypeReference[PartitionResolutionStrategy] = new TypeReference[PartitionResolutionStrategy] {} + implicit def eventEnrichmentStrategyTR: TypeReference[EventEnrichmentStrategy] = new TypeReference[EventEnrichmentStrategy] {} + implicit def dataChangeEventQualifierTR: TypeReference[DataChangeEventQualifier] = new TypeReference[DataChangeEventQualifier] {} + implicit def eventTypeStatisticsTR: TypeReference[EventTypeStatistics] = new TypeReference[EventTypeStatistics] {} + implicit def eventTypeTR: TypeReference[EventType] = new TypeReference[EventType] {} + implicit def eventTR: TypeReference[Event] = new TypeReference[Event] {} - implicit val problemTypeReference = new TypeReference[Problem] {} - implicit val metricsTypeReference = new TypeReference[Metrics] {} - implicit val partitionTypeReference = new TypeReference[Partition] {} - implicit val cursorTypeReference = new TypeReference[Cursor] {} - implicit val eventTypeSchemaTypeReference = new TypeReference[EventTypeSchema] {} - implicit val eventValidationStrategyTypeReference = new TypeReference[EventValidationStrategy] {} - implicit val partitionResolutionStrategyTypeReference = new TypeReference[PartitionResolutionStrategy] {} - implicit val eventEnrichmentStrategyTypeReference = new TypeReference[EventEnrichmentStrategy] {} - implicit val dataChangeEventQualifierTypeReference = new TypeReference[DataChangeEventQualifier] {} - implicit val eventTypeStatisticsTypeReference = new TypeReference[EventTypeStatistics] {} - implicit val eventTypeTypeReference = new TypeReference[EventType] {} - implicit val eventTypeReference = new TypeReference[Event] {} - implicit val eventStreamBatchTypeReference = new TypeReference[EventStreamBatch] {} - implicit val eventMetadataTypeReference = new TypeReference[EventMetadata] {} - implicit val businessEventTypeReference = new TypeReference[BusinessEvent] {} - implicit val batchItemResponseTypeReference = new TypeReference[BatchItemResponse] {} + implicit def eventMetadataTR: TypeReference[EventMetadata] = new TypeReference[EventMetadata] {} + implicit def businessEventTR: TypeReference[BusinessEvent] = new TypeReference[BusinessEvent] {} + implicit def batchItemResponseTR: TypeReference[BatchItemResponse] = new TypeReference[BatchItemResponse] {} + implicit def dataChangeEventTR: TypeReference[DataChangeEvent[Any]] = new TypeReference[DataChangeEvent[Any]] {} - implicit def dataChangeEventTypeReference[T](implicit expectedType: TypeReference[T]) = { new TypeReference[DataChangeEvent[T]] {} } + //Lists + implicit def listOfPartitionResolutionStrategyTR: TypeReference[Seq[PartitionResolutionStrategy]] = new TypeReference[Seq[PartitionResolutionStrategy]] {} + implicit def listOfEventValidationStrategyTR: TypeReference[Seq[EventValidationStrategy]] = new TypeReference[Seq[EventValidationStrategy]] {} + implicit def listOfEventEnrichmentStrategyTR: TypeReference[Seq[EventEnrichmentStrategy]] = new TypeReference[Seq[EventEnrichmentStrategy]] {} + implicit def listOfEventTypeTR: TypeReference[Seq[EventType]] = new TypeReference[Seq[EventType]] {} - implicit def unmarshaller[T: Manifest](implicit ec: ExecutionContext, am: ActorMaterializer, expectedType: TypeReference[T]): Unmarshaller[HttpEntity, Future[T]] = Unmarshaller.strict { - input => Unmarshaller.byteArrayUnmarshaller(input).map(objectMapper.readValue(_, expectedType)) + implicit def seqDeserializer[T](implicit expectedType: TypeReference[Seq[T]]) = new Deserializer[Seq[T]] { + def fromJson(from: String): Seq[T] = defaultObjectMapper.readValue[Seq[T]](from, expectedType) } - - implicit def objectToStringMarshaller[T]: Marshaller[T, String] = Marshaller.opaque { - input => objectMapper.writeValueAsString(input) + implicit def seqSerializer[T]() = new Serializer[Seq[T]] { + def toJson(from: Seq[T]): String = defaultObjectMapper.writeValueAsString(from) } - implicit def stringToObjectUnmarshaller[T](implicit expectedType: TypeReference[T]): Unmarshaller[String, T] = Unmarshaller.strict { - input => objectMapper.readValue[T](input, expectedType) + implicit def serializer[T](implicit expectedType: TypeReference[T]): Serializer[T] = new Serializer[T] { + def toJson(from: T): String = defaultObjectMapper.writeValueAsString(from) } -} - -private[model] object JacksonJsonMarshaller { - val logger = Logger(LoggerFactory.getLogger(JacksonJsonMarshaller.getClass)) - def objectMapper(): ObjectMapper = { - val m = new ObjectMapper { - @PostConstruct - def customConfiguration() { - // Uses Enum.toString() for serialization of an Enum - this.enable(SerializationFeature.WRITE_ENUMS_USING_TO_STRING); - // Uses Enum.toString() for deserialization of an Enum - this.enable(DeserializationFeature.READ_ENUMS_USING_TO_STRING); - } + implicit def deserializer[T](implicit expectedType: TypeReference[T]): Deserializer[T] = new Deserializer[T] { + def fromJson(from: String): T = defaultObjectMapper.readValue[T](from, expectedType) + } - } - m.registerModule(new DefaultScalaModule) - m.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) - m.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) - m.setPropertyNamingStrategy(PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES) - m.addHandler(new DeserializationProblemHandler() { + lazy val defaultObjectMapper: ObjectMapper = new ObjectMapper().registerModule(new DefaultScalaModule) + .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) + .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .setPropertyNamingStrategy(PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES) + .setSerializationInclusion(JsonInclude.Include.NON_EMPTY) + .addHandler(new DeserializationProblemHandler() { override def handleUnknownProperty(ctxt: DeserializationContext, jp: JsonParser, deserializer: JsonDeserializer[_], beanOrClass: AnyRef, @@ -90,14 +92,8 @@ private[model] object JacksonJsonMarshaller { logger.warn(s"unknown property occurred in JSON representation: [beanOrClass=$beanOrClass, property=$propertyName]") true } - }) - m - } - - def enumSerizalizer[T <: Enumeration]() = new JsonSerializer[T#Value] { - override def serialize(value: T#Value, jgen: JsonGenerator, provider: SerializerProvider): Unit = { - jgen.writeString(value.toString()) - } - } + }) -} \ No newline at end of file + +} + diff --git a/client/src/main/scala/org/zalando/nakadi/client/model/SprayJsonMarshaller.scala b/client/src/main/scala/org/zalando/nakadi/client/model/SprayJsonMarshaller.scala index 359a957..a76ef48 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/model/SprayJsonMarshaller.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/model/SprayJsonMarshaller.scala @@ -1,57 +1,88 @@ package org.zalando.nakadi.client.model +import org.zalando.nakadi.client.utils.ParseHelper import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport import akka.http.scaladsl.marshalling.Marshaller import akka.http.scaladsl.unmarshalling.Unmarshaller -import spray.json.{ DefaultJsonProtocol, JsValue, JsonFormat, JsonReader, JsonWriter, pimpAny, pimpString } import spray.json._ -import org.zalando.nakadi.client.utils.ParseHelper - -trait SprayJsonMarshaller extends SprayJsonSupport with DefaultJsonProtocol { - import ParseHelper._ - case class Test(subjectDescription: String) - implicit val testFormatter = jsonFormat1(Test) - //Enums - implicit val dataOperationEnumFormatter = jsonEnumFormat(DataOperation) - implicit val eventTypeCategoryEnumFormatter = jsonEnumFormat(EventTypeCategory) - implicit val batchItemStepjsonEnumFormat = jsonEnumFormat(BatchItemStep) - implicit val BatchItemPublishingStatusEnumFormat = jsonEnumFormat(BatchItemPublishingStatus) - implicit val SchemaTypeEnumFormat = jsonEnumFormat(SchemaType) - - implicit val problemFormatter = jsonFormat(Problem, "problem_type", "title", "status", "detail", "instance") - implicit val metricsFormatter = jsonFormat1(Metrics) - implicit val partitionFormatter = jsonFormat(Partition, "partition", "oldest_available_offset", "newest_available_offset") - implicit val cursorFormatter = jsonFormat2(Cursor) - implicit val dataChangeEventQualifierFormatter = jsonFormat(DataChangeEventQualifier, "data_type", "data_operation") - implicit val partitionResolutionStrategyFormatter = jsonFormat2(PartitionResolutionStrategy) - implicit val eventTypeSchemaFormatter = jsonFormat(EventTypeSchema, "type", "schema") - implicit val eventTypeStatisticsFormatter = jsonFormat(EventTypeStatistics, "expected_write_rate", "message_size", "read_parallelism", "write_parallelism") - implicit val eventValidationStrategyFormatter = jsonFormat2(EventValidationStrategy) - implicit val eventTypeFormatter = jsonFormat(EventType, "name", "owning_application", "category", "validation_strategies", "enrichment_strategies", "partition_strategy", "schema", "partition_key_fields", "partitioning_key_fields", "statistics") - implicit val eventMetadataFormatter = jsonFormat(EventMetadata, "eid", "event_type", "occurred_at", "received_at", "parent_eids", "flow_id", "partition", "metadata") - implicit val businessEventFormatter = jsonFormat1(BusinessEvent) - implicit val eventEnrichmentStrategyFormatter = jsonFormat2(EventEnrichmentStrategy) - implicit val eventFormatter = jsonFormat(Event,"event_type","additional_properties","title") - implicit val eventStreamBatchFormatter = jsonFormat2(EventStreamBatch) - implicit val batchItemResponseFormatter = jsonFormat(BatchItemResponse,"eid","publishing_status","step","detail") - - implicit def dataChangeEventFormatter[A: JsonFormat] = jsonFormat3(DataChangeEvent.apply[A]) - - //Custom Marshaller/Unmarshaller - implicit def entityToStringMarshaller[T](implicit writer: JsonWriter[T]): Marshaller[T, String] = Marshaller.opaque { - _.toJson(writer).toString() - } - - implicit def stringToEntityUnmarshaller[T](implicit reader: JsonReader[T]): Unmarshaller[String, T] = Unmarshaller.strict { - input => input.parseJson.convertTo[T] - } - - implicit def entityToJsValueMarshaller[T](implicit writer: JsonWriter[T]): Marshaller[T, JsValue] = Marshaller.opaque { - _.toJson(writer) - } - - implicit def jsValueToEntityUnmarshaller[T](implicit reader: JsonReader[T]): Unmarshaller[JsValue, T] = Unmarshaller.strict { - input => input.convertTo[T] - } +import spray.json.DefaultJsonProtocol +import spray.json.JsValue +import spray.json.JsonFormat +import spray.json.JsonReader +import spray.json.JsonWriter +import spray.json.pimpAny +import spray.json.pimpString +import org.zalando.nakadi.client.Serializer +import org.zalando.nakadi.client.Deserializer -} \ No newline at end of file +//trait SprayJsonMarshaller extends SprayJsonSupport with DefaultJsonProtocol { +// import ParseHelper._ +// case class Test(subjectDescription: String) +// implicit val testFormatter = jsonFormat1(Test) +// //Enums +// implicit val dataOperationEnumFormatter = jsonEnumFormat(DataOperation) +// implicit val eventTypeCategoryEnumFormatter = jsonEnumFormat(EventTypeCategory) +// implicit val batchItemStepjsonEnumFormat = jsonEnumFormat(BatchItemStep) +// implicit val BatchItemPublishingStatusEnumFormat = jsonEnumFormat(BatchItemPublishingStatus) +// implicit val SchemaTypeEnumFormat = jsonEnumFormat(SchemaType) +// +// implicit val problemFormatter = jsonFormat(Problem, "problem_type", "title", "status", "detail", "instance") +// implicit val metricsFormatter = jsonFormat1(Metrics) +// implicit val partitionFormatter = jsonFormat(Partition, "partition", "oldest_available_offset", "newest_available_offset") +// implicit val cursorFormatter = jsonFormat2(Cursor) +// implicit val partitionResolutionStrategyFormatter = jsonFormat2(PartitionResolutionStrategy) +// implicit val eventTypeSchemaFormatter = jsonFormat(EventTypeSchema, "type", "schema") +// implicit val eventTypeStatisticsFormatter = jsonFormat(EventTypeStatistics, "expected_write_rate", "message_size", "read_parallelism", "write_parallelism") +// implicit val eventValidationStrategyFormatter = jsonFormat2(EventValidationStrategy) +// implicit val eventTypeFormatter = jsonFormat(EventType, "name", "owning_application", "category", "validation_strategies", "enrichment_strategies", "partition_strategy", "schema", "partition_key_fields", "partitioning_key_fields", "statistics") +// implicit val eventMetadataFormatter = jsonFormat(EventMetadata, "eid", "event_type", "occurred_at", "received_at", "parent_eids", "flow_id", "partition") +// implicit val eventEnrichmentStrategyFormatter = jsonFormat2(EventEnrichmentStrategy) +// implicit def dataChangeEventFormatter[A: JsonFormat] = jsonFormat(DataChangeEvent.apply[A],"data","data_type","data_operation","metadata") +// +// implicit object BusinessJsonFormat extends RootJsonFormat[BusinessEvent] { +// def write(a: BusinessEvent) = a match { +// case p: BusinessEvent => p.toJson +// } +// def read(json: JsValue) = json.asJsObject.fields("label") match { +// case JsString("business") => json.convertTo[BusinessEvent] +// case JsString("change-event") => json.convertTo[DataChangeEvent] +// case _ => +// throw new IllegalStateException("Couldn't parse what seems to be an Event: %s".format(json)) +// } +// } +// +// +// implicit object EventJsonFormat extends RootJsonFormat[Event] { +// def write(a: Event) = a match { +// case p: BusinessEvent => p.toJson +// } +// def read(json: JsValue) = json.asJsObject.fields("label") match { +// case JsString("business") => json.convertTo[BusinessEvent] +// case JsString("change-event") => json.convertTo[DataChangeEvent] +// case _ => +// throw new IllegalStateException("Couldn't parse what seems to be an Event: %s".format(json)) +// } +// } +// +// implicit val dataChangeEventFormatter = jsonFormat4(DataChangeEvent.apply) +// +// +// implicit def eventFormatter[T <: Event](implicit writer: JsonWriter[T])= writer.write(obj) +// +// implicit val eventStreamBatchFormatter = jsonFormat2(EventStreamBatch) +// implicit val batchItemResponseFormatter = jsonFormat(BatchItemResponse, "eid", "publishing_status", "step", "detail") +// +// +// +// implicit def serializer[T](implicit writer: JsonWriter[T]): Serializer[T] = new Serializer[T] { +// def to(from: T): String = { +// from.toJson(writer).toString() +// } +// } +// implicit def deserializer[T](implicit reader: JsonReader[T]): Derializer[T] = new Derializer[T] { +// def to(from: String): T = { +// from.parseJson.convertTo[T] +// } +// } +// +//} \ No newline at end of file diff --git a/client/src/main/scala/org/zalando/nakadi/client/package.scala b/client/src/main/scala/org/zalando/nakadi/client/package.scala index 968814e..4b40e20 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/package.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/package.scala @@ -11,8 +11,8 @@ package object client { val URI_EVENTS_OF_EVENT_TYPE = "/event-types/%s/events" /*Partitions*/ - val URI_PARTITIONS_OF_EVENT_TYPE = "/event-types/%s/partitions" - val URI_PARTITION_BY_NAME = "/event-types/%s/partitions/%s" + val URI_PARTITIONS_BY_EVENT_TYPE = "/event-types/%s/partitions" + val URI_PARTITION_BY_EVENT_TYPE_AND_ID = "/event-types/%s/partitions/%s" /*Strategies*/ val URI_VALIDATION_STRATEGIES = "/registry/validation-strategies" diff --git a/client/src/test/resources/Events.txt b/client/src/test/resources/Events.txt new file mode 100644 index 0000000..b81c0b9 --- /dev/null +++ b/client/src/test/resources/Events.txt @@ -0,0 +1,214 @@ +[ + { + "name": "article", + "owning_application": "article-producer", + "category": "data", + "partition_strategy": { + "name": "random", + "doc": "This strategy will put the event to a random partition. Use it only if your `EventType` has one partition or if you don't care about ordering of events." + }, + "partition_key_fields": [], + "schema": { + "type": "json_schema", + "schema": "{\"Article\": { \"properties\": { \"sku\": { \"type\": \"string\" } } }}" + } + }, + { + "name": "myarticle", + "owning_application": "article-producer", + "category": "data", + "partition_strategy": { + "name": "random", + "doc": "This strategy will put the event to a random partition. Use it only if your `EventType` has one partition or if you don't care about ordering of events." + }, + "partition_key_fields": [], + "schema": { + "type": "json_schema", + "schema": "{\"Article\": { \"properties\": { \"sku\": { \"type\": \"string\" } } }}" + } + }, + { + "name": "yourarticle", + "owning_application": "article-producer", + "category": "data", + "partition_strategy": { + "name": "random", + "doc": "This strategy will put the event to a random partition. Use it only if your `EventType` has one partition or if you don't care about ordering of events." + }, + "partition_key_fields": [], + "schema": { + "type": "json_schema", + "schema": "{\"Article\": { \"properties\": { \"sku\": { \"type\": \"string\" } } }}" + } + }, + { + "name": "blah", + "owning_application": "article-producer", + "category": "data", + "partition_strategy": { + "name": "random", + "doc": "This strategy will put the event to a random partition. Use it only if your `EventType` has one partition or if you don't care about ordering of events." + }, + "partition_key_fields": [], + "schema": { + "type": "json_schema", + "schema": "{\"Article\": { \"properties\": { \"sku\": { \"type\": \"string\" } } }}" + } + }, + { + "name": "slavaTest", + "owning_application": "article-producer", + "category": "data", + "partition_strategy": { + "name": "random", + "doc": "This strategy will put the event to a random partition. Use it only if your `EventType` has one partition or if you don't care about ordering of events." + }, + "partition_key_fields": [], + "schema": { + "type": "json_schema", + "schema": "{\"Article\": { \"properties\": { \"sku\": { \"type\": \"string\" } } }}" + } + }, + { + "name": "slavaTest2", + "owning_application": "article-producer", + "category": "data", + "partition_strategy": { + "name": "random", + "doc": "This strategy will put the event to a random partition. Use it only if your `EventType` has one partition or if you don't care about ordering of events." + }, + "partition_key_fields": [], + "schema": { + "type": "json_schema", + "schema": "{\"Article\": { \"properties\": { \"sku\": { \"type\": \"string\" } } }}" + } + }, + { + "name": "asf_overarch_service_sql_v1_testing", + "owning_application": "aws-asf-service-api", + "category": "data", + "partition_strategy": { + "name": "random", + "doc": "This strategy will put the event to a random partition. Use it only if your `EventType` has one partition or if you don't care about ordering of events." + }, + "partition_key_fields": [], + "schema": { + "type": "json_schema", + "schema": "{\"Article\":{\"properties\": {\"sql\": { \"type\": \"array\", \"minItems\": 0, \"items\": { \"type\": \"string\" }}, \"csku\": { \"type\": \"string\" }, \"request_id\": { \"type\": \"string\" }, \"csku_index\": { \"type\": \"integer\" }, \"scenario\": { \"type\": \"string\" }, \"max_index\": { \"type\": \"integer\" }}}}" + } + }, + { + "name": "buffalo-test-topic", + "owning_application": "buffalo-reactive-nakadi", + "category": "data", + "partition_strategy": { + "name": "random", + "doc": "This strategy will put the event to a random partition. Use it only if your `EventType` has one partition or if you don't care about ordering of events." + }, + "partition_key_fields": [], + "schema": { + "type": "json_schema", + "schema": "{\"Article\": { \"properties\": { \"sku\": { \"type\": \"string\" } } }}" + } + }, + { + "name": "eventlog.e1006_default_invoice_address_changed", + "owning_application": "eventlog", + "category": "business", + "partition_strategy": { + "name": "random", + "doc": "This strategy will put the event to a random partition. Use it only if your `EventType` has one partition or if you don't care about ordering of events." + }, + "partition_key_fields": [ + "metadata.eid" + ], + "schema": { + "type": "json_schema", + "schema": "{\"additionalProperties\": true}" + } + }, + { + "name": "reactive-nakadi-testing-uppercase", + "owning_application": "buffalo", + "category": "data", + "partition_strategy": { + "name": "random", + "doc": "This strategy will put the event to a random partition. Use it only if your `EventType` has one partition or if you don't care about ordering of events" + }, + "partition_key_fields": [], + "schema": { + "type": "json_schema", + "schema": "{\"type\": \"object\", \"properties\": {\"FOO\": {\"type\": \"string\"}}, \"required\": [\"foo\"]}" + } + }, + { + "name": "test.ORDER_RECEIVED", + "owning_application": "laas-team", + "category": "business", + "partition_strategy": { + "name": "random", + "doc": "This strategy will put the event to a random partition. Use it only if your `EventType` has one partition or if you don't care about ordering of events" + }, + "partition_key_fields": [], + "schema": { + "type": "json_schema", + "schema": "{ \"properties\": { \"order_number\": { \"type\": \"string\" } } }" + } + }, + { + "name": "di-sfc", + "owning_application": "di-sfc-nakadi", + "category": "data", + "partition_strategy": { + "name": "random", + "doc": "This strategy will put the event to a random partition. Use it only if your `EventType` has one partition or if you don't care about ordering of events." + }, + "partition_key_fields": [], + "schema": { + "type": "json_schema", + "schema": "{\"Article\": { \"properties\": { \"sql\": { \"type\": \"string\" } } }}" + } + }, + { + "name": "ORDER_CREATED", + "owning_application": "sales-order-service", + "category": "data", + "partition_strategy": { + "name": "random", + "doc": "This strategy will put the event to a random partition. Use it only if your `EventType` has one partition or if you don't care about ordering of events." + }, + "partition_key_fields": [], + "schema": { + "type": "json_schema", + "schema": "{ \"order\": { \"properties\": { \"order_number\": { \"type\": \"string\" }}}}" + } + }, + { + "name": "buffalo-test-topic-uppercase", + "owning_application": "buffalo-reactive-nakadi", + "category": "data", + "partition_strategy": { + "name": "random", + "doc": "This strategy will put the event to a random partition. Use it only if your `EventType` has one partition or if you don't care about ordering of events." + }, + "partition_key_fields": [], + "schema": { + "type": "json_schema", + "schema": "{\"Article\": { \"properties\": { \"sku\": { \"type\": \"string\" } } }}" + } + }, + { + "name": "5fade51a-9db0-452f-8e5f-203659f2a2e3", + "owning_application": "di-sfc-nakadi", + "category": "data", + "partition_strategy": { + "name": "random", + "doc": "This strategy will put the event to a random partition. Use it only if your `EventType` has one partition or if you don't care about ordering of events." + }, + "partition_key_fields": [], + "schema": { + "type": "json_schema", + "schema": "{\"Article\":{\"properties\": {\"sql\": { \"type\": \"array\", \"minItems\": 0, \"items\": { \"type\": \"string\" }}, \"csku\": { \"type\": \"string\" }}}}" + } + } +] \ No newline at end of file diff --git a/client/src/test/resources/application.conf b/client/src/test/resources/application.conf index d9376e3..e7b40a1 100644 --- a/client/src/test/resources/application.conf +++ b/client/src/test/resources/application.conf @@ -22,8 +22,8 @@ akka { akka.cluster.metrics.enabled=off # Enable metrics extension in akka-cluster-metrics. -akka.extensions=["akka.cluster.metrics.ClusterMetricsExtension"] +#akka.extensions=["akka.cluster.metrics.ClusterMetricsExtension"] # Sigar native library extract location during tests. # Note: use per-jvm-instance folder when running multiple jvm on one host. -akka.cluster.metrics.native-library-extract-folder=${user.dir}/target/native +#akka.cluster.metrics.native-library-extract-folder=${user.dir}/target/native diff --git a/client/src/test/scala/org/zalando/nakadi/client/App.scala b/client/src/test/scala/org/zalando/nakadi/client/App.scala deleted file mode 100644 index 1ff27fe..0000000 --- a/client/src/test/scala/org/zalando/nakadi/client/App.scala +++ /dev/null @@ -1,17 +0,0 @@ -package org.zalando.nakadi.client - -import org.zalando.nakadi.client.model.JacksonJsonMarshaller - - -object Main extends App with JacksonJsonMarshaller { - val host = "" - val OAuth2Token = () => "" - val port = 443 - val client = new ClientImpl(Connection.newConnection(host, port, OAuth2Token, true, false),"UTF-8") - -// val response = client.eventTypes() -// Await.result(response, 10.second) -// response.map(r => -// println("######################## " + r)) - -} \ No newline at end of file diff --git a/client/src/test/scala/org/zalando/nakadi/client/ClientTest.scala b/client/src/test/scala/org/zalando/nakadi/client/ClientTest.scala new file mode 100644 index 0000000..b8fcf5a --- /dev/null +++ b/client/src/test/scala/org/zalando/nakadi/client/ClientTest.scala @@ -0,0 +1,59 @@ +package org.zalando.nakadi.client + +import scala.concurrent.Future +import scala.concurrent.Await +import scala.concurrent.duration.DurationInt +import org.mockito.Matchers.any +import org.mockito.Matchers.anyString +import org.mockito.Mockito.reset +import org.mockito.Mockito.times +import org.mockito.Mockito.verify +import org.mockito.Mockito.when +import org.scalatest.BeforeAndAfter +import org.scalatest.FlatSpec +import org.scalatest.Matchers +import org.scalatest.WordSpec +import org.scalatest.mock.MockitoSugar +import akka.http.scaladsl.model.HttpResponse +import akka.stream.Materializer +import spray.json.JsonFormat +import akka.http.scaladsl.model.StatusCodes +import akka.http.scaladsl.model.HttpProtocol +import akka.http.scaladsl.model.HttpProtocols +import akka.http.scaladsl.model.HttpEntity +import akka.http.scaladsl.model.ContentTypes +import org.zalando.nakadi.client.model.JacksonJsonMarshaller + +class ClientTest extends WordSpec with Matchers with JacksonJsonMarshaller with MockitoSugar with BeforeAndAfter { + private var connection: Connection = mock[Connection] + private val client: Client = new ClientImpl(connection) + before { + reset(connection) + } + "ClientImpl " should { + + "return a None when receiving a 404" in { + val headers = null + val entity = HttpEntity(ContentTypes.`application/json`, "abc") + val response = new HttpResponse(StatusCodes.NotFound, headers, entity, HttpProtocols.`HTTP/1.1`) + val futureResponse = Future.successful(response) + when(connection.get(URI_PARTITIONING_STRATEGIES)).thenReturn(futureResponse) + // val result=Await.result(client.partitionStrategies(),500.seconds) + // result.isLeft shouldBe true + // val Left(clientError) = result + // clientError.status shouldBe None + } + + "marshall an object when receiving http a 200 with valid payload" in { + //TODO : implement + } + + "catch the marshalling exception when receiving a 200 with invalid payload" in { + //TODO : implement + } + "return a ClientError receiving a redirection (300-399)" in { + //TODO : implement + } + } + +} \ No newline at end of file diff --git a/client/src/test/scala/org/zalando/nakadi/client/DeserializerSerializerTest.scala b/client/src/test/scala/org/zalando/nakadi/client/DeserializerSerializerTest.scala index ef46f4c..b2b925e 100644 --- a/client/src/test/scala/org/zalando/nakadi/client/DeserializerSerializerTest.scala +++ b/client/src/test/scala/org/zalando/nakadi/client/DeserializerSerializerTest.scala @@ -2,30 +2,39 @@ package org.zalando.nakadi.client import scala.concurrent.Await import scala.concurrent.duration.DurationInt -import org.scalatest.{ Matchers, WordSpec } -import org.zalando.nakadi.client.model.SprayJsonMarshaller +import org.scalatest.Matchers +import org.scalatest.WordSpec +import org.zalando.nakadi.client.model.EventType import org.zalando.nakadi.client.util.AkkaConfig -import akka.http.scaladsl.marshalling.{ Marshal, Marshaller } -import akka.http.scaladsl.unmarshalling.{ Unmarshal, Unmarshaller } -import akka.stream.Materializer import org.zalando.nakadi.client.util.TestJsonEntity -import org.zalando.nakadi.client.model.EventType +import akka.http.scaladsl.unmarshalling.Unmarshaller +import akka.stream.Materializer +import spray.json.JsonFormat +import org.zalando.nakadi.client.model.JacksonJsonMarshaller -class DeserializerSerializerTest extends WordSpec with Matchers with SprayJsonMarshaller with AkkaConfig { +class DeserializerSerializerTest extends WordSpec with Matchers with JacksonJsonMarshaller with AkkaConfig { import TestJsonEntity._ + val file = getClass().getClassLoader().getResource("Events.txt").getFile + val input = scala.io.Source.fromFile(file).mkString + "When a json entity is unmarshalled and marshalled it" should { val testName = "always result in the same entity" s"$testName(event)" in { - checkDeserializationProcessSerialization[EventType]("event", events) +// checkDeserializationProcessSerialization[EventType]("event", singleEvent) +// checkDeserializationProcessSerialization[List[EventType]]("event", input) } } - def checkDeserializationProcessSerialization[T](key: String, value: String)(implicit m: Marshaller[T, String], um: Unmarshaller[String, T]) { - val futureScalaEntity = Unmarshal(value).to[T] - val scalaEntity = Await.result(futureScalaEntity, 1.second) - val futureJsonEntity = Marshal(value).to[String] - val jsonEntity = Await.result(futureScalaEntity, 1.second) - assert(jsonEntity == value, s"Failed to marshall $key") + def checkDeserializationProcessSerialization[T](key: String, input: String)(implicit m: JsonFormat[T] ) { + import spray.json._ + val json = input.parseJson + println(">>>>>>> IN " + json.compactPrint) + val scalaModel = json.convertTo[T] + val jsonResult = scalaModel.toJson + + println(">>>>>>> OUT 1" + jsonResult) + + assert(jsonResult == json, s"Failed to marshall $key") } } \ No newline at end of file diff --git a/client/src/test/scala/org/zalando/nakadi/client/EnumModule.scala b/client/src/test/scala/org/zalando/nakadi/client/EnumModule.scala new file mode 100644 index 0000000..3ed2bad --- /dev/null +++ b/client/src/test/scala/org/zalando/nakadi/client/EnumModule.scala @@ -0,0 +1,67 @@ +package org.zalando.nakadi.client + +import scala.reflect._ +import scala.reflect.runtime.universe._ +import org.zalando.nakadi.client.model.JacksonJsonMarshaller +import com.fasterxml.jackson.core.JsonGenerator +import com.fasterxml.jackson.core.JsonParser +import com.fasterxml.jackson.core.Version +import com.fasterxml.jackson.core.`type`.TypeReference +import com.fasterxml.jackson.core.`type`.TypeReference +import com.fasterxml.jackson.databind.DeserializationContext +import com.fasterxml.jackson.databind.JsonDeserializer +import com.fasterxml.jackson.databind.JsonDeserializer +import com.fasterxml.jackson.databind.JsonSerializer +import com.fasterxml.jackson.databind.JsonSerializer +import com.fasterxml.jackson.databind.SerializerProvider +import com.fasterxml.jackson.databind.module.SimpleModule +import org.zalando.nakadi.client.model.DataOperation +import org.zalando.nakadi.client.model.BatchItemStep +import org.zalando.nakadi.client.model.BatchItemPublishingStatus +import org.zalando.nakadi.client.model.EventTypeCategory +import org.zalando.nakadi.client.model.SchemaType +import com.fasterxml.jackson.module.scala.JsonScalaEnumeration + +case object EnumModule extends SimpleModule() { + // addSerializer(ser) +} + +object FunninessLevel extends Enumeration { + type FunninessLevel = Value + val LOL = Value("LOL") + val ROFL = Value("ROFL") + val LMAO = Value("LMAO") +} + +class EnumSerializer extends JsonSerializer[FunninessLevel.FunninessLevel] { + def serialize(id: FunninessLevel.FunninessLevel, json: JsonGenerator, provider: SerializerProvider) = { + json.writeString(id.toString()) + } +} +class EnumDeserializer extends JsonDeserializer[FunninessLevel.FunninessLevel] { + def deserialize(jp: JsonParser, ctxt: DeserializationContext): FunninessLevel.FunninessLevel = { + FunninessLevel.withName(jp.getText()) + } +} + +//object Test extends App with JacksonJsonMarshaller { +// +// val marshaller = new JacksonJsonMarshaller {} +// +// val mapper = marshaller.defaultObjectMapper +// +// implicit val personType = new TypeReference[Person] {} +// +// case class Person(name: String, @JsonScalaEnumeration(classOf[UserStatusType]) humor: DataOperation.Value) +// val person = Person("Fernando", DataOperation.CREATE) +// val json = marshaller.serializer[Person].toJson(person) +// +// println(" #### -- PERSON >> " + person) +// println(" #### -- JSON-PERSON >> " + json) +// val personResult = marshaller.deserializer[Person].fromJson(json) +// println(" #### -- PERSON >> " + personResult) +// +//} + + + diff --git a/client/src/test/scala/org/zalando/nakadi/client/SerializerDeserializerTest.scala b/client/src/test/scala/org/zalando/nakadi/client/SerializerDeserializerTest.scala index aa98229..7c0d784 100644 --- a/client/src/test/scala/org/zalando/nakadi/client/SerializerDeserializerTest.scala +++ b/client/src/test/scala/org/zalando/nakadi/client/SerializerDeserializerTest.scala @@ -1,13 +1,16 @@ package org.zalando.nakadi.client -import scala.concurrent.Await import scala.concurrent.duration.DurationInt -import org.scalatest.{ Matchers, WordSpec } +import org.scalatest.Matchers +import org.scalatest.WordSpec +import org.zalando.nakadi.client.model._ +import org.zalando.nakadi.client.model.JacksonJsonMarshaller import org.zalando.nakadi.client.util.AkkaConfig -import akka.http.scaladsl.marshalling.{ Marshal, Marshaller } -import akka.http.scaladsl.unmarshalling.{ Unmarshal, Unmarshaller } +import com.fasterxml.jackson.core.`type`.TypeReference +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.module.scala.JsonScalaEnumeration import org.zalando.nakadi.client.util.TestScalaEntity -import org.zalando.nakadi.client.model.SprayJsonMarshaller +import org.zalando.nakadi.client.model._ /** * Tests the Marshalling and Umarshalling of the same object in a single run. It tests in this sequence: 1.Marshall and 2.Unmarshall.
@@ -15,19 +18,19 @@ import org.zalando.nakadi.client.model.SprayJsonMarshaller * Marshallers/Unmarshallers are used and produce different * unexpected results. */ -class SerializerDeserializerTest extends WordSpec with Matchers with SprayJsonMarshaller with AkkaConfig { +class SerializerDeserializerTest extends WordSpec with Matchers with JacksonJsonMarshaller with AkkaConfig { import TestScalaEntity._ - - /** - * Tests - */ "When an entity(scala object) is marshalled and unmarshalled it" should { val testName = "always result in the same entity" s"$testName(eventMetadata)" in { checkSerializationDeserializationProcess("eventMetadata", eventMetadata) } + "EventType" in { + println(" ####### " + EventTypeCategory.withName("business")) + } + s"$testName(problem)" in { checkSerializationDeserializationProcess("problem", problem) } @@ -52,22 +55,17 @@ class SerializerDeserializerTest extends WordSpec with Matchers with SprayJsonMa s"$testName(eventEnrichmentStrategy)" in { checkSerializationDeserializationProcess("eventEnrichmentStrategy", partitionResolutionStrategy) } - s"$testName(businessEvent)" in { - checkSerializationDeserializationProcess("businessEvent", businessEvent) - } - s"$testName(dataChangeEventQualifier)" in { - checkSerializationDeserializationProcess("dataChangeEventQualifier", dataChangeEventQualifier) - } - s"$testName(dataChangeEvent)" in { - checkSerializationDeserializationProcess("dataChangeEvent", dataChangeEvent) - } + // s"$testName(dataChangeEvent)" in { + // checkSerializationDeserializationProcess("dataChangeEvent", dataChangeEvent) + // } s"$testName(eventType)" in { checkSerializationDeserializationProcess("eventType", eventType) } - s"$testName(event)" in { - checkSerializationDeserializationProcess("event", event) - } + // s"$testName(event)" in { + // checkSerializationDeserializationProcess("event", event) + // } s"$testName(eventStreamBatch)" in { + implicit val myEventStreamBatchTR: TypeReference[EventStreamBatch[MyEvent]] = new TypeReference[EventStreamBatch[MyEvent]] {} checkSerializationDeserializationProcess("eventStreamBatch", eventStreamBatch) } s"$testName(eventTypeStatistics)" in { @@ -79,12 +77,11 @@ class SerializerDeserializerTest extends WordSpec with Matchers with SprayJsonMa } - def checkSerializationDeserializationProcess[T](key: String, value: T)(implicit m: Marshaller[T, String], um: Unmarshaller[String, T]) { - val futureJsonEntity = Marshal(value).to[String] // Marshal - val jsonEntity = Await.result(futureJsonEntity, 1.second) - println(jsonEntity) - val futureScalaEntity = Unmarshal(jsonEntity).to[T] //Unmarshal - val scalaEntity = Await.result(futureScalaEntity, 1.second) + def checkSerializationDeserializationProcess[T](key: String, value: T)(implicit ser: Serializer[T], des: Deserializer[T]) { + val jsonEntity = ser.toJson(value) // Marshal + println("#### Json-Entity:" + jsonEntity) + val scalaEntity = des.fromJson(jsonEntity) //Unmarshal + println("#### Scala-Entity:" + scalaEntity) assert(scalaEntity == value, s"Failed to marshall $key correctly!!!") } diff --git a/client/src/test/scala/org/zalando/nakadi/client/TestFactory.scala b/client/src/test/scala/org/zalando/nakadi/client/TestFactory.scala new file mode 100644 index 0000000..e9bc112 --- /dev/null +++ b/client/src/test/scala/org/zalando/nakadi/client/TestFactory.scala @@ -0,0 +1,73 @@ +package org.zalando.nakadi.client + +import scala.concurrent.Await +import scala.concurrent.Future +import scala.concurrent.duration.DurationInt +import scala.util.Random +import org.joda.time.format.ISODateTimeFormat +import org.zalando.nakadi.client.model.Event +import org.zalando.nakadi.client.model.EventMetadata +import org.zalando.nakadi.client.model.EventType +import org.zalando.nakadi.client.model.EventTypeCategory +import org.zalando.nakadi.client.model.EventTypeSchema +import org.zalando.nakadi.client.model.PartitionResolutionStrategy +import org.zalando.nakadi.client.model.SchemaType +//import org.zalando.nakadi.client.model.SprayJsonMarshaller +import org.joda.time.DateTime +import org.zalando.nakadi.client.model.EventMetadata +import org.zalando.nakadi.client.model.JacksonJsonMarshaller + +trait ClientFactory { + val host = "" + val OAuth2Token = () => "" + val port = 443 + val client = new ClientImpl(Connection.newConnection(host, port, OAuth2Token, true, false), "UTF-8") + + +} + +case class EventTypesActions(client: Client) extends JacksonJsonMarshaller{ + + def create(event: EventType)(implicit ser: Serializer[EventType]) = { + executeCall(client.newEventType(event)) + } + def update(event: EventType)(implicit ser: Serializer[EventType]) = { + executeCall(client.updateEventType(event.name, event)) + } + def get(name: String)(implicit ser: Deserializer[EventType]) = { + executeCall(client.eventType(name)) + } + def getAll()(implicit ser: Deserializer[Seq[EventType]]) = { + executeCall(client.eventTypes()) + } + def delete(name: String) = { + executeCall(client.deleteEventType(name)) + } + + private def executeCall[T](call: => Future[T]): T = { + Await.result(call, 10.second) + } +} + +trait ModelFactory { + val x = Random.alphanumeric + def partitionStrategy() = new PartitionResolutionStrategy("hash", None) + def paritionKeyFields() = List("order_number") + def schemaDefinition() = """{ "properties": { "order_number": { "type": "string" } } }""" + def eventTypeSchema() = new EventTypeSchema(SchemaType.JSON, schemaDefinition) + + def createUniqueEventType(): EventType = { + //Unique events mean unique name + new EventType("test-client-integration-event-" + Random.nextInt() + "-" + Random.nextInt() + Random.nextInt(), // + "laas-team", // + EventTypeCategory.BUSINESS, None, None, // + partitionStrategy, Option(eventTypeSchema), // + None, Option(paritionKeyFields), None) + } + def createEventMetadata(): EventMetadata = { + val length=5 + val eid= java.util.UUID.randomUUID.toString + val occurredAt = new DateTime().toString() + new EventMetadata(eid,None,occurredAt,None,Nil,None,None) + } +} \ No newline at end of file diff --git a/client/src/test/scala/org/zalando/nakadi/client/util/TestJsonEntity.scala b/client/src/test/scala/org/zalando/nakadi/client/util/TestJsonEntity.scala index e8c4cd9..6f353ca 100644 --- a/client/src/test/scala/org/zalando/nakadi/client/util/TestJsonEntity.scala +++ b/client/src/test/scala/org/zalando/nakadi/client/util/TestJsonEntity.scala @@ -1,15 +1,50 @@ package org.zalando.nakadi.client.util object TestJsonEntity { - val events = """{ - "name": "article", - "owning_application": "article-producer", - "category": "data", - "partition_resolution_strategy": null, - "partitioning_key_fields": [], - "schema": { - "type": "json_schema", - "schema": "{\"Article\": { \"properties\": { \"sku\": { \"type\": \"string\" } } }}" + val singleEvent="""{ + "name": "article", + "owning_application": "article-producer", + "category": "data", + "partition_strategy": { + "name": "random", + "doc": "This strategy will put the event to a random partition. Use it only if your `EventType` has one partition or if you don't care about ordering of events." + }, + "partition_key_fields": [], + "schema": { + "type": "json_schema", + "schema": "{\"Article\": { \"properties\": { \"sku\": { \"type\": \"string\" } } }}" + } } - }""" + """ + val events = """ + [ + { + "name": "article", + "owning_application": "article-producer", + "category": "data", + "partition_strategy": { + "name": "random", + "doc": "This strategy will put the event to a random partition. Use it only if your `EventType` has one partition or if you don't care about ordering of events." + }, + "partition_key_fields": [], + "schema": { + "type": "json_schema", + "schema": "{\"Article\": { \"properties\": { \"sku\": { \"type\": \"string\" } } }}" + } + }, + { + "name": "myarticle", + "owning_application": "article-producer", + "category": "data", + "partition_strategy": { + "name": "random", + "doc": "This strategy will put the event to a random partition. Use it only if your `EventType` has one partition or if you don't care about ordering of events." + }, + "partition_key_fields": [], + "schema": { + "type": "json_schema", + "schema": "{\"Article\": { \"properties\": { \"sku\": { \"type\": \"string\" } } }}" + } + }] + """ } \ No newline at end of file diff --git a/client/src/test/scala/org/zalando/nakadi/client/util/TestScalaEntity.scala b/client/src/test/scala/org/zalando/nakadi/client/util/TestScalaEntity.scala index 76320bb..f4717b5 100644 --- a/client/src/test/scala/org/zalando/nakadi/client/util/TestScalaEntity.scala +++ b/client/src/test/scala/org/zalando/nakadi/client/util/TestScalaEntity.scala @@ -19,14 +19,10 @@ import org.zalando.nakadi.client.model.Cursor import org.zalando.nakadi.client.model.BusinessEvent object TestScalaEntity { - // - import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport - import spray.json.DefaultJsonProtocol._ - - //Simple bjects composed out of scalar typers only (without dependency to other Object-models) +// Simple bjects composed out of scalar typers only (without dependency to other Object-models) val problem = new Problem("problemType", "title", 312, Option("detail"), Option("instance")) - val metrics = new Metrics("metrics") + val metrics = new Metrics(Map("metrics"->"test")) val partition = new Partition("partition", "oldestAvailableOffset", "newestAvailableOffset") val cursor = new Cursor("partition", "offset") val eventTypeSchema = new EventTypeSchema(SchemaType.JSON, "schema") @@ -35,14 +31,16 @@ object TestScalaEntity { val eventEnrichmentStrategy = new EventEnrichmentStrategy("name", Option("doc")) //Complex objects - val dataChangeEventQualifier = new DataChangeEventQualifier("dataType", DataOperation.CREATE) - val eventTypeStatistics = new EventTypeStatistics(Option(9281002), Option(19283), Option(21), Option(312)) val eventType = new EventType("name", "owner", EventTypeCategory.BUSINESS, Option(List("validationStrategies")), Option(List("enrichmentStrategies")), partitionResolutionStrategy, Option(eventTypeSchema), Option(List("dataKeyFields")), Option(List("partitioningKeyFields")), Option(eventTypeStatistics)) - val event = new Event(eventType, true, "title") - val eventStreamBatch = new EventStreamBatch(cursor, List(event, event)) - val eventMetadata = new EventMetadata("eid", Option(eventType), "occurredAt", Option("receivedAt"), List("parentEids"), Option("flowId"), Option("partition"), Map("key" -> "value")) - val dataChangeEvent = new DataChangeEvent(metrics, dataChangeEventQualifier, eventMetadata) - val businessEvent = new BusinessEvent(eventMetadata) + val eventMetadata = new EventMetadata("eid", Option(eventType), "occurredAt", Option("receivedAt"), List("parentEids"), Option("flowId"), Option("partition")) + case class MyEvent(name:String,metadata:EventMetadata) extends Event + val myEvent = new MyEvent("test",eventMetadata) + val eventStreamBatch = new EventStreamBatch(cursor, List(myEvent)) val batchItemResponse = new BatchItemResponse(Option("eid"), BatchItemPublishingStatus.SUBMITTED, Option(BatchItemStep.PUBLISHING), Option("detail")) + + // own event + case class CommissionEntity(id:String,ql:List[String]) + val commissionEntity=new CommissionEntity("id2",List("ql1","ql2")) + val dataChangeEvent = new DataChangeEvent[CommissionEntity](commissionEntity,"Critical", DataOperation.DELETE, eventMetadata) } \ No newline at end of file diff --git a/it/src/main/scala/org/zalando/nakadi/client/config/App.scala b/it/src/main/scala/org/zalando/nakadi/client/config/App.scala deleted file mode 100644 index 1ff27fe..0000000 --- a/it/src/main/scala/org/zalando/nakadi/client/config/App.scala +++ /dev/null @@ -1,17 +0,0 @@ -package org.zalando.nakadi.client - -import org.zalando.nakadi.client.model.JacksonJsonMarshaller - - -object Main extends App with JacksonJsonMarshaller { - val host = "" - val OAuth2Token = () => "" - val port = 443 - val client = new ClientImpl(Connection.newConnection(host, port, OAuth2Token, true, false),"UTF-8") - -// val response = client.eventTypes() -// Await.result(response, 10.second) -// response.map(r => -// println("######################## " + r)) - -} \ No newline at end of file diff --git a/it/src/test/scala/org/zalando/nakadi/client/EventTypesIntegrationTest.scala b/it/src/test/scala/org/zalando/nakadi/client/EventTypesIntegrationTest.scala index 4acb17d..bf99568 100644 --- a/it/src/test/scala/org/zalando/nakadi/client/EventTypesIntegrationTest.scala +++ b/it/src/test/scala/org/zalando/nakadi/client/EventTypesIntegrationTest.scala @@ -1,27 +1,11 @@ package org.zalando.nakadi.client -import scala.concurrent.{ Await, Future } -import scala.concurrent.duration.DurationInt -import scala.util.Random - import org.scalatest.{ Matchers, WordSpec } -import org.zalando.nakadi.client.model.{ EventType, EventTypeCategory, EventTypeSchema, PartitionResolutionStrategy, SchemaType, SprayJsonMarshaller } +import org.zalando.nakadi.client.model._ -class EventTypeTest extends WordSpec with Matchers with SprayJsonMarshaller { +class EventTypeTest extends WordSpec with Matchers with JacksonJsonMarshaller with ModelFactory with ClientFactory { - //Client configuration - val host = "" - val OAuth2Token = () => "" - val port = 443 - val client = new ClientImpl(Connection.newConnection(host, port, OAuth2Token, true, false), "UTF-8") val events = new EventTypesActions(client) - - //EventType fields - val partitionStrategy = new PartitionResolutionStrategy("hash", None) - val paritionKeyFields = List("order_number") - val schemaDefinition = """{ "properties": { "order_number": { "type": "string" } } }""" - val eventTypeSchema = new EventTypeSchema(SchemaType.JSON, schemaDefinition) - "POST/PUT/GET/DELETE single EventType " in { //Create event @@ -32,11 +16,10 @@ class EventTypeTest extends WordSpec with Matchers with SprayJsonMarshaller { //Check the created EventType checkEventTypeExists(eventType) - //TODO: Enable this when PUT is supported. - //Update the event -// val updatedEvent = eventType.copy(owningApplication = "laas-team-2") -// events.update(updatedEvent) +// Update the event +// val updatedEvent = eventType.copy(owningApplication = "laas-team-2") +// events.update(updatedEvent) //Check the EventType has bee updated // checkEventTypeExists(updatedEvent) @@ -63,9 +46,10 @@ class EventTypeTest extends WordSpec with Matchers with SprayJsonMarshaller { checkEventTypeExists(eventType2) //Get all EventTypes again - val Right(Some(allEvents)) = events.getAll() - allEvents should contain(eventType1) - allEvents should contain(eventType2) + //TODO: Enable when Nakadi has no erranous eventType + // val Right(Some(allEvents)) = events.getAll() + // allEvents should contain(eventType1) + // allEvents should contain(eventType2) //Delete the 2 EventTypes events.delete(eventType1.name) @@ -83,11 +67,30 @@ class EventTypeTest extends WordSpec with Matchers with SprayJsonMarshaller { } + //TODO: Enable when implemented + "UpdateEventTypes" in { + //Create 2 EventTypes + val eventType = createUniqueEventType() + + events.create(eventType) + checkEventTypeExists(eventType) + + //Update the event + val updatedEvent = eventType.copy(owningApplication = "laas-team-2") + events.update(updatedEvent) + + //Check the EventType has bee updated + // checkEventTypeExists(updatedEvent) + // checkEventTypeDoesNotExist(eventType) + + } + def checkEventTypeDoesNotExist(eventType: EventType) = { val requestedEvent = events.get(eventType.name) - requestedEvent.isLeft shouldBe true - val Left(result) = requestedEvent - result.status shouldBe Some(404) + println(requestedEvent) + requestedEvent.isRight shouldBe true + val Right(result) = requestedEvent + result shouldBe None } def checkEventTypeExists(eventType: EventType) = { @@ -95,35 +98,5 @@ class EventTypeTest extends WordSpec with Matchers with SprayJsonMarshaller { createdEvent shouldBe eventType } - private def createUniqueEventType(): EventType = { - new EventType("test-client-integration-event-" + Random.nextInt() + "-" + Random.nextInt() + Random.nextInt(), // - "laas-team", // - EventTypeCategory.BUSINESS, None, None, // - partitionStrategy, Option(eventTypeSchema), // - Option(paritionKeyFields), None, None) - } - } -class EventTypesActions(client: Client) extends SprayJsonMarshaller { - - def create(event: EventType) = { - executeCall(client.newEventType(event)) - } - def update(event: EventType) = { - executeCall(client.updateEventType(event.name, event)) - } - def get(name: String) = { - executeCall(client.eventType(name)) - } - def getAll() = { - executeCall(client.eventTypes()) - } - def delete(name: String) = { - executeCall(client.deleteEventType(name)) - } - - private def executeCall[T](call: => Future[T]): T = { - Await.result(call, 10.second) - } -} \ No newline at end of file diff --git a/it/src/test/scala/org/zalando/nakadi/client/KlientIntegrationTest.scala b/it/src/test/scala/org/zalando/nakadi/client/KlientIntegrationTest.scala index 387fec0..c1add84 100644 --- a/it/src/test/scala/org/zalando/nakadi/client/KlientIntegrationTest.scala +++ b/it/src/test/scala/org/zalando/nakadi/client/KlientIntegrationTest.scala @@ -1,36 +1,75 @@ -package org.zalando.nakadi.client +package org.zalando.nakadi.client.config -import org.scalatest.Matchers -import org.scalatest.WordSpec import scala.concurrent.Await -import scala.concurrent.duration.DurationInt -import org.zalando.nakadi.client.model.SprayJsonMarshaller import scala.concurrent.Future +import scala.concurrent.duration.DurationInt +import org.scalatest.Matchers +import org.scalatest.WordSpec +import org.zalando.nakadi.client.Client._ import org.zalando.nakadi.client.model._ -import scala.util.Random - -class KlientIntegrationTest extends WordSpec with Matchers with SprayJsonMarshaller { - val host = "nakadi-sandbox.aruha-test.zalan.do" - val OAuth2Token = () => "41c7e243-fe44-4aa9-9df1-e63a5de8aed6" - val port = 443 - val client = new ClientImpl(Connection.newConnection(host, port, OAuth2Token, true, false), "UTF-8") - - //Defaults - val partitionStrategy = new PartitionResolutionStrategy("hash", None) - val paritionKeyFields = List("order_number") - val schemaDefinition = """{ "properties": { "order_number": { "type": "string" } } }""" - val eventTypeSchema = new EventTypeSchema(SchemaType.JSON, schemaDefinition) - - val actions = new EventTypesActions(client) +import spray.json._ +import com.fasterxml.jackson.core.`type`.TypeReference +import org.zalando.nakadi.client.ClientFactory +import org.zalando.nakadi.client.ModelFactory +import scala.Right +class KlientIntegrationTest extends WordSpec with Matchers with JacksonJsonMarshaller with ModelFactory with ClientFactory { "Nakadi Client" should { "parse multiple PartitionResolutionStrategy" in { val Right(result) = executeCall(client.partitionStrategies()) result.size should be > 0 } - - + //TODO: Change it when this endpoint is implemented by nakadi + "parse exsiting validationStrategies" in { + val result = executeCall(client.validationStrategies()) + result shouldBe Right(None) + } + //TODO: Change it when this endpoint is implemented by nakadi + "parse exsiting enrishment-strategies" in { + val result = executeCall(client.enrichmentStrategies()) + result shouldBe Right(None) + } + //TODO: Change when all events are valid + "parse existing eventTypes" in { + val Right(result) = executeCall(client.eventTypes()) + result.size should be > 0 + } + "create a new eventType" in { + val eventType = createUniqueEventType() + executeCall(client.newEventType(eventType)) shouldBe None + } + "get EventType" in { + val eventType = createUniqueEventType() + executeCall(client.newEventType(eventType)) shouldBe None + executeCall(client.eventType(eventType.name)) shouldBe Right(Some(eventType)) + + } + "delete EventType" in { + val eventType = createUniqueEventType() + executeCall(client.newEventType(eventType)) shouldBe None + executeCall(client.deleteEventType(eventType.name)) shouldBe None + } + "Create the event itself" in { + import spray.json._ + //Matches the one defined in the schema of + + case class EventExample(orderNumber: String, metadata: EventMetadata) extends Event + implicit val eventExample: TypeReference[Seq[EventExample]] = new TypeReference[Seq[EventExample]] {} + + val event = new EventExample("22301982837", createEventMetadata()) + val eventType = createUniqueEventType() + executeCall(client.newEventType(eventType)) shouldBe None + executeCall(client.newEvents[EventExample](eventType.name, List(event))) shouldBe None + } + } + private def assertIsNotImplementedYet[T](input: Either[ClientError, Option[List[T]]]) = { + input match { + case Left(error) => error.status shouldBe Some(404) + case Right(result) => + println(" #### " + result) + fail + } } private def executeCall[T](call: => Future[T]): T = { Await.result(call, 10.second) diff --git a/model/src/main/scala/org/zalando/nakadi/client/model/Model.scala b/model/src/main/scala/org/zalando/nakadi/client/model/Model.scala index da32493..f18c6c5 100644 --- a/model/src/main/scala/org/zalando/nakadi/client/model/Model.scala +++ b/model/src/main/scala/org/zalando/nakadi/client/model/Model.scala @@ -1,5 +1,9 @@ package org.zalando.nakadi.client.model +import com.fasterxml.jackson.core.`type`.TypeReference +import com.fasterxml.jackson.module.scala.JsonScalaEnumeration +import com.fasterxml.jackson.annotation.JsonProperty + // Updated untill commit 7839be3 // Compare @@ -16,13 +20,12 @@ package org.zalando.nakadi.client.model * @param additionalProperties Default value is true * @param title */ -case class Event( - eventType: EventType, - additionalProperties: Boolean, - title: String) +trait Event { + def metadata(): EventMetadata +} /** - * + * * Metadata for this Event. Contains commons fields for both Business and DataChange Events. Most are enriched by Nakadi upon reception, but they in general MIGHT be set by the client. * @param eid Identifier of this Event. Clients are allowed to generate this and this SHOULD be guaranteed to be unique from the perspective of the producer. Consumers MIGHT use this value to assert uniqueness of reception of the Event. * @param eventType The EventType of this Event. This is enriched by Nakadi on reception of the Event based on the endpoint where the Producer sent the Event to. If provided MUST match the endpoint. Failure to do so will cause rejection of the Event. @@ -40,24 +43,23 @@ case class EventMetadata( receivedAt: Option[String], parentEids: Seq[String], flowId: Option[String], - partition:Option[String], - metadata: Map[String, Any]) + partition: Option[String]) /** * A Business Event. Usually represents a status transition in a Business process. * - * @param metadata Metadata for this Event. Contains commons fields for both Business and DataChange Events. Most are enriched by Nakadi upon reception, but they in general MIGHT be set by the client. */ -case class BusinessEvent(metadata: EventMetadata) +trait BusinessEvent extends Event /** * Indicators of a `DataChangeEvent`'s referred data type and the type of operations done on them. * @param dataType The datatype of the `DataChangeEvent`. * @param dataOp The type of operation executed on the entity. * C: Creation * U: Update * D: Deletion * S: Snapshot */ -case class DataChangeEventQualifier( - dataType: String, - dataOperation: DataOperation.Value) +trait DataChangeEventQualifier { + def dataType(): String + def dataOperation(): DataOperation.Value +} /** * A Data change Event. Represents a change on a resource. @@ -66,10 +68,11 @@ case class DataChangeEventQualifier( * @param eventQualifier Indicators of a `DataChangeEvent`'s referred data type and the type of operations done on them. * @param metadata Metadata for this Event. Contains commons fields for both Business and DataChange Events. Most are enriched by Nakadi upon reception, but they in general MIGHT be set by the client */ -case class DataChangeEvent[T]( //TODO: Maybe DataChangeEvent must subclass DataChangeEventQualifier and EventMetadata via trait +case class DataChangeEvent[T]( data: T, - eventQualifier: DataChangeEventQualifier, - metadata: EventMetadata) + dataType: String, + @JsonScalaEnumeration(classOf[DataOperationType]) dataOperation: DataOperation.Value, + metadata: EventMetadata) extends DataChangeEventQualifier with Event /** * @ param problemType An absolute URI that identifies the problem type. When dereferenced, it SHOULD provide human-readable API documentation for the problem type (e.g., using HTML). This Problem object is the same as provided by https://github.com/zalando/problem @@ -85,7 +88,7 @@ case class Problem( detail: Option[String], instance: Option[String]) -case class Metrics(metrics: String) //TODO: It is not defined yet! +case class Metrics(metrics: Map[String, Any]) //TODO: It is not defined yet! /** * Partition information. Can be helpful when trying to start a stream using an unmanaged API. This information is not related to the state of the consumer clients. @@ -108,11 +111,12 @@ case class Cursor( /** * One chunk of events in a stream. A batch consists of an array of `Event`s plus a `Cursor` pointing to the offset of the last Event in the stream. The size of the array of Event is limited by the parameters used to initialize a Stream. If acting as a keep alive message (see `GET /event-type/{name}/events`) the events array will be omitted. Sequential batches might repeat the cursor if no new events arrive. * @param cursor The cursor point to an event in the stream. - * @param events The Event definition will be externalized in future versions of this document. A basic payload of an Event. The actual schema is dependent on the information configured for the EventType, as is its enforcement (see POST /event-types). Setting of metadata properties are dependent on the configured enrichment as well. For explanation on default configurations of validation and enrichment, see documentation of `EventType.type`. For concrete examples of what will be enforced by Nakadi see the objects BusinessEvent and DataChangeEvent below. + * @param events The Event definition will be externalized in future versions of this document. A basic payload of an Event. The actual schema is dependent on the information configured for the EventType, as is its enforcement (see POST /event-types). Setting of metadata properties are dependent on the configured enrichment as well. For explanation on default configurations of validation and enrichment, see documentation of `EventType.type`. For concrete examples of what will be enforced by Nakadi see the objects + * sEvent and DataChangeEvent below. */ -case class EventStreamBatch( +case class EventStreamBatch[T <: Event]( cursor: Cursor, - events: Seq[Event]) + events: Seq[T]) /** * An event type defines the schema and its runtime properties. @@ -131,14 +135,14 @@ case class EventStreamBatch( case class EventType( name: String, owningApplication: String, - category: EventTypeCategory.Value, + @JsonScalaEnumeration(classOf[EventTypeCategoryType]) category: EventTypeCategory.Value, validationStrategies: Option[Seq[String]], enrichmentStrategies: Option[Seq[String]], partitionStrategy: PartitionResolutionStrategy, //Different naming schema: Option[EventTypeSchema], dataKeyFields: Option[Seq[String]], - partitioningKeyFields: Option[Seq[String]], - statistics:Option[EventTypeStatistics]) + partitionKeyFields: Option[Seq[String]], + statistics: Option[EventTypeStatistics]) /** * The schema for an EventType, expected to be a json schema in yaml @@ -148,7 +152,7 @@ case class EventType( * Failure to respect the syntax will fail any operation on an EventType. */ case class EventTypeSchema( - schemaType: SchemaType.Value,//Name is type (keyword in scala) + @JsonProperty("type")@JsonScalaEnumeration(classOf[SchemaTypeType]) schemaType: SchemaType.Value, //Name is type (keyword in scala) schema: String) /** @@ -202,14 +206,16 @@ case class EventEnrichmentStrategy( */ case class BatchItemResponse( eid: Option[String], - publishingStatus: BatchItemPublishingStatus.Value, - step: Option[BatchItemStep.Value], + @JsonScalaEnumeration(classOf[BatchItemPublishingStatusType]) publishingStatus: BatchItemPublishingStatus.Value, + @JsonScalaEnumeration(classOf[BatchItemStepType]) step: Option[BatchItemStep.Value], detail: Option[String]) ///////////////////////////////// // ENUMS //////////////////////// ///////////////////////////////// +trait NakadiEnum + /** * Identifier for the type of operation to executed on the entity.
* C: Creation
@@ -218,7 +224,8 @@ case class BatchItemResponse( * S: Snapshot

* Values = CREATE("C"), UPDATE("U"), DELETE("D"), SNAPSHOT("S") */ -case object DataOperation extends Enumeration { + +case object DataOperation extends Enumeration with NakadiEnum { type DataOperation = Value val CREATE = Value("C") val UPDATE = Value("U") @@ -226,6 +233,7 @@ case object DataOperation extends Enumeration { val SNAPSHOT = Value("S") } +class DataOperationType extends TypeReference[DataOperation.type] /** * Defines the category of an EventType.
@@ -238,6 +246,7 @@ case object EventTypeCategory extends Enumeration { val BUSINESS = Value("business") } +class EventTypeCategoryType extends TypeReference[EventTypeCategory.type] /** * Indicator of the submission of the Event within a Batch.
* - SUBMITTED indicates successful submission, including commit on he underlying broker.
@@ -253,6 +262,8 @@ case object BatchItemPublishingStatus extends Enumeration { val ABORTED = Value("ABORTED") } +class BatchItemPublishingStatusType extends TypeReference[BatchItemPublishingStatus.type] + /** * Indicator of the step in the pulbishing process this Event reached. * In Items that FAILED means the step of the failure. @@ -271,8 +282,10 @@ case object BatchItemStep extends Enumeration { val PUBLISHING = Value("PUBLISHING") } +class BatchItemStepType extends TypeReference[BatchItemStep.type] -case object SchemaType extends Enumeration{ +case object SchemaType extends Enumeration { type SchemaType = Value val JSON = Value("json_schema") } +class SchemaTypeType extends TypeReference[SchemaType.type] \ No newline at end of file diff --git a/project/Build.scala b/project/Build.scala index 534ac16..f62ba36 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -31,7 +31,7 @@ lazy val commonSettings = Seq( lazy val model = withDefaults( "nakadi-klients-model", project.in(file("model")) - ) + ).settings(libraryDependencies ++= modelDeps) lazy val api = withDefaults( "nakadi-klients-api", diff --git a/project/Dependencies.scala b/project/Dependencies.scala index 2f84477..cd03dbb 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -4,6 +4,9 @@ object Dependencies { val akkaVersion = "2.4.2" + val modelDeps = {Seq( + "com.fasterxml.jackson.module" %% "jackson-module-scala" % "2.6.3" withSources() withJavadoc(), + "com.fasterxml.jackson.core" % "jackson-core" % "2.7.0" withSources() withJavadoc())} val clientDeps = { Seq( "com.typesafe.akka" %% "akka-http-spray-json-experimental" % akkaVersion withSources() withJavadoc(), @@ -11,6 +14,8 @@ object Dependencies { "com.typesafe.akka" %% "akka-http-experimental" % akkaVersion withSources() withJavadoc(), "com.typesafe.akka" %% "akka-cluster-metrics" % akkaVersion withSources() withJavadoc(), "com.typesafe.akka" %% "akka-testkit" % akkaVersion % "test" withSources() withJavadoc(), + "joda-time" % "joda-time" % "2.9.3" withSources() withJavadoc(), + "org.joda" % "joda-convert" % "1.8.1" withSources() withJavadoc(), "com.typesafe" % "config" % "1.3.0" withSources() withJavadoc(), "com.google.guava" % "guava" % "19.0" withSources() withJavadoc(), "com.typesafe.scala-logging" %% "scala-logging" % "3.1.0" withSources() withJavadoc(), @@ -23,7 +28,8 @@ object Dependencies { //"io.undertow" % "undertow-servlet" % "1.2.12.Final" % "test" withSources() withJavadoc(), "org.apache.commons" % "commons-io" % "1.3.2" % "test" withSources() withJavadoc(), //"com.google.code.findbugs" % "jsr305" % "1.3.9" % "test" withSources() withJavadoc(), - "junit" % "junit" % "4.12" % "test" withSources() withJavadoc() + "junit" % "junit" % "4.12" % "test" withSources() withJavadoc(), + "org.mockito" % "mockito-core" % "1.10.19" % "test" withSources() withJavadoc() ) } } From 4ba6d4aea3e3a11dfbb604d75fadd3c04565b4b2 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Wed, 13 Apr 2016 11:37:51 +0200 Subject: [PATCH 016/183] LAAS-60 Prefixing Serializer and Deserializer --- .../org/zalando/nakadi/client/Client.scala | 24 ++++++++--------- .../zalando/nakadi/client/ClientImpl.scala | 26 +++++++++---------- .../zalando/nakadi/client/Connection.scala | 8 +++--- .../zalando/nakadi/client/Serialization.scala | 4 +-- .../client/model/JacksonJsonMarshaller.scala | 12 ++++----- .../client/model/SprayJsonMarshaller.scala | 4 +-- .../client/SerializerDeserializerTest.scala | 2 +- .../zalando/nakadi/client/TestFactory.scala | 8 +++--- 8 files changed, 44 insertions(+), 44 deletions(-) diff --git a/client/src/main/scala/org/zalando/nakadi/client/Client.scala b/client/src/main/scala/org/zalando/nakadi/client/Client.scala index 089a8fd..4a65b44 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/Client.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/Client.scala @@ -16,7 +16,7 @@ trait Client { * curl --request GET /metrics * }}} */ - def metrics()(implicit ser: Deserializer[Metrics]): Future[Either[ClientError, Option[Metrics]]] + def metrics()(implicit ser: NakadiDeserializer[Metrics]): Future[Either[ClientError, Option[Metrics]]] /** * Returns a list of all registered EventTypes. @@ -26,7 +26,7 @@ trait Client { * }}} * */ - def eventTypes()(implicit ser: Deserializer[Seq[EventType]]): Future[Either[ClientError, Option[Seq[EventType]]]] + def eventTypes()(implicit ser: NakadiDeserializer[Seq[EventType]]): Future[Either[ClientError, Option[Seq[EventType]]]] /** * Creates a new EventType. @@ -38,7 +38,7 @@ trait Client { * @param event - The EventType to create. * */ - def newEventType(eventType: EventType)(implicit ser: Serializer[EventType]): Future[Option[ClientError]] + def newEventType(eventType: EventType)(implicit ser: NakadiSerializer[EventType]): Future[Option[ClientError]] /** * Returns the EventType identified by its name. @@ -47,7 +47,7 @@ trait Client { * }}} * @param name - Name of the EventType */ - def eventType(name: String)(implicit ser: Deserializer[EventType]): Future[Either[ClientError, Option[EventType]]] + def eventType(name: String)(implicit ser: NakadiDeserializer[EventType]): Future[Either[ClientError, Option[EventType]]] /** * Updates the EventType identified by its name. * {{{ @@ -56,7 +56,7 @@ trait Client { * @param name - Name of the EventType * @param event - Event to update */ - def updateEventType(name: String, eventType: EventType)(implicit ser: Serializer[EventType]): Future[Option[ClientError]] + def updateEventType(name: String, eventType: EventType)(implicit ser: NakadiSerializer[EventType]): Future[Option[ClientError]] /** * Deletes an EventType identified by its name. * @@ -76,7 +76,7 @@ trait Client { * @param name - Name of the EventType * @param event - Event to create */ - def newEvents[T](name: String, events: Seq[T])(implicit ser: Serializer[Seq[T]]): Future[Option[ClientError]] + def newEvents[T](name: String, events: Seq[T])(implicit ser: NakadiSerializer[Seq[T]]): Future[Option[ClientError]] /** * Request a stream delivery for the specified partitions of the given EventType. @@ -86,7 +86,7 @@ trait Client { * @param name - Name of the EventType * */ - def events[T](name: String)(implicit ser: Deserializer[T]): Future[Either[ClientError, Option[T]]] + def events[T](name: String)(implicit ser: NakadiDeserializer[T]): Future[Either[ClientError, Option[T]]] /** * List the partitions for the given EventType. @@ -95,7 +95,7 @@ trait Client { * }}} * @param name - Name of the EventType */ - def partitions(name: String)(implicit ser: Deserializer[Partition]): Future[Either[ClientError, Option[Partition]]] + def partitions(name: String)(implicit ser: NakadiDeserializer[Partition]): Future[Either[ClientError, Option[Partition]]] /** * Returns the Partition for the given EventType. @@ -106,7 +106,7 @@ trait Client { * @param partition - Partition id for the given EventType */ - def partitionById(name: String, id: String)(implicit ser: Deserializer[Partition]): Future[Either[ClientError, Option[Partition]]] + def partitionById(name: String, id: String)(implicit ser: NakadiDeserializer[Partition]): Future[Either[ClientError, Option[Partition]]] /** * Returns all of the validation strategies supported by this installation of Nakadi. @@ -115,7 +115,7 @@ trait Client { * curl --request GET /registry/validation-strategies * }}} */ - def validationStrategies()(implicit des: Deserializer[Seq[EventValidationStrategy]]): Future[Either[ClientError, Option[Seq[EventValidationStrategy]]]] + def validationStrategies()(implicit des: NakadiDeserializer[Seq[EventValidationStrategy]]): Future[Either[ClientError, Option[Seq[EventValidationStrategy]]]] /** * Returns all of the enrichment strategies supported by this installation of Nakadi. @@ -124,7 +124,7 @@ trait Client { * }}} */ - def enrichmentStrategies()(implicit des: Deserializer[Seq[EventEnrichmentStrategy]]): Future[Either[ClientError, Option[Seq[EventEnrichmentStrategy]]]] + def enrichmentStrategies()(implicit des: NakadiDeserializer[Seq[EventEnrichmentStrategy]]): Future[Either[ClientError, Option[Seq[EventEnrichmentStrategy]]]] /** * Returns all of the partitioning strategies supported by this installation of Nakadi. @@ -132,7 +132,7 @@ trait Client { * curl --request GET /registry/partitioning-strategies * }}} */ - def partitionStrategies()(implicit des: Deserializer[Seq[PartitionResolutionStrategy]]): Future[Either[ClientError, Option[Seq[PartitionResolutionStrategy]]]] + def partitionStrategies()(implicit des: NakadiDeserializer[Seq[PartitionResolutionStrategy]]): Future[Either[ClientError, Option[Seq[PartitionResolutionStrategy]]]] /** * Shuts down the communication system of the client diff --git a/client/src/main/scala/org/zalando/nakadi/client/ClientImpl.scala b/client/src/main/scala/org/zalando/nakadi/client/ClientImpl.scala index dac211c..ab12811 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/ClientImpl.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/ClientImpl.scala @@ -24,23 +24,23 @@ private[client] class ClientImpl(connection: Connection, charSet: String = "UTF- implicit val materializer = connection.materializer val logger = Logger(LoggerFactory.getLogger(this.getClass)) - def metrics()(implicit ser: Deserializer[Metrics]): Future[Either[ClientError, Option[Metrics]]] ={ + def metrics()(implicit ser: NakadiDeserializer[Metrics]): Future[Either[ClientError, Option[Metrics]]] ={ logFutureEither(connection.get(URI_METRICS).flatMap(mapToEither(_))) } - def eventTypes()(implicit ser: Deserializer[Seq[EventType]]): Future[Either[ClientError, Option[Seq[EventType]]]] = { + def eventTypes()(implicit ser: NakadiDeserializer[Seq[EventType]]): Future[Either[ClientError, Option[Seq[EventType]]]] = { logFutureEither(connection.get(URI_EVENT_TYPES).flatMap(mapToEither(_))) } - def newEventType(eventType: EventType)(implicit ser: Serializer[EventType]): Future[Option[ClientError]] = { + def newEventType(eventType: EventType)(implicit ser: NakadiSerializer[EventType]): Future[Option[ClientError]] = { logFutureOption(connection.post(URI_EVENT_TYPES, eventType).map(mapToOption(_))) } - def eventType(name: String)(implicit ser: Deserializer[EventType]): Future[Either[ClientError, Option[EventType]]] = { + def eventType(name: String)(implicit ser: NakadiDeserializer[EventType]): Future[Either[ClientError, Option[EventType]]] = { logFutureEither(connection.get(URI_EVENT_TYPE_BY_NAME.format(name)).flatMap(in => mapToEither(in))) } - def updateEventType(name: String, eventType: EventType)(implicit ser: Serializer[EventType]): Future[Option[ClientError]] = { + def updateEventType(name: String, eventType: EventType)(implicit ser: NakadiSerializer[EventType]): Future[Option[ClientError]] = { val result = connection.put(URI_EVENT_TYPE_BY_NAME.format(name), eventType) logFutureOption(result.map(in => mapToOption(in))) } @@ -49,32 +49,32 @@ private[client] class ClientImpl(connection: Connection, charSet: String = "UTF- logFutureOption(connection.delete(URI_EVENT_TYPE_BY_NAME.format(name)).map(in => mapToOption(in))) } - def newEvents[T](name: String, events: Seq[T])(implicit ser: Serializer[Seq[T]]): Future[Option[ClientError]] = { + def newEvents[T](name: String, events: Seq[T])(implicit ser: NakadiSerializer[Seq[T]]): Future[Option[ClientError]] = { logFutureOption(connection.post(URI_EVENTS_OF_EVENT_TYPE.format(name), events).map(in => mapToOption(in))) } - def events[T](name: String)(implicit ser: Deserializer[T]): Future[Either[ClientError, Option[T]]] = { + def events[T](name: String)(implicit ser: NakadiDeserializer[T]): Future[Either[ClientError, Option[T]]] = { logFutureEither(connection.get(URI_EVENTS_OF_EVENT_TYPE.format(name)).flatMap(in => mapToEither(in))) } - def partitions(name: String)(implicit ser: Deserializer[Partition]): Future[Either[ClientError, Option[Partition]]] = { + def partitions(name: String)(implicit ser: NakadiDeserializer[Partition]): Future[Either[ClientError, Option[Partition]]] = { logFutureEither(connection.get(URI_PARTITIONS_BY_EVENT_TYPE.format(name)).flatMap(in => mapToEither(in))) } - def partitionById(name: String, id: String)(implicit ser: Deserializer[Partition]): Future[Either[ClientError, Option[Partition]]] = { + def partitionById(name: String, id: String)(implicit ser: NakadiDeserializer[Partition]): Future[Either[ClientError, Option[Partition]]] = { logFutureEither(connection.get(URI_PARTITION_BY_EVENT_TYPE_AND_ID.format(name)).flatMap(in => mapToEither(in))) } - def validationStrategies()(implicit des: Deserializer[Seq[EventValidationStrategy]]): Future[Either[ClientError, Option[Seq[EventValidationStrategy]]]] = { + def validationStrategies()(implicit des: NakadiDeserializer[Seq[EventValidationStrategy]]): Future[Either[ClientError, Option[Seq[EventValidationStrategy]]]] = { logFutureEither(connection.get(URI_VALIDATION_STRATEGIES).flatMap(mapToEither(_))) } - def enrichmentStrategies()(implicit des: Deserializer[Seq[EventEnrichmentStrategy]]): Future[Either[ClientError, Option[Seq[EventEnrichmentStrategy]]]] = { + def enrichmentStrategies()(implicit des: NakadiDeserializer[Seq[EventEnrichmentStrategy]]): Future[Either[ClientError, Option[Seq[EventEnrichmentStrategy]]]] = { logFutureEither(connection.get(URI_ENRICHMENT_STRATEGIES).flatMap(mapToEither(_))) } - def partitionStrategies()(implicit des: Deserializer[Seq[PartitionResolutionStrategy]]): Future[Either[ClientError, Option[Seq[PartitionResolutionStrategy]]]] = + def partitionStrategies()(implicit des: NakadiDeserializer[Seq[PartitionResolutionStrategy]]): Future[Either[ClientError, Option[Seq[PartitionResolutionStrategy]]]] = logFutureEither(connection.get(URI_PARTITIONING_STRATEGIES).flatMap(mapToEither(_))) def stop(): Future[Terminated] = connection.stop() @@ -98,7 +98,7 @@ private[client] class ClientImpl(connection: Connection, charSet: String = "UTF- } } - def mapToEither[T](response: HttpResponse)(implicit deserializer: Deserializer[T]): Future[Either[ClientError, Option[T]]] = { + def mapToEither[T](response: HttpResponse)(implicit deserializer: NakadiDeserializer[T]): Future[Either[ClientError, Option[T]]] = { logger.debug("received [response={}]", response) response match { case HttpResponse(status, headers, entity, protocol) if (status.isSuccess()) => diff --git a/client/src/main/scala/org/zalando/nakadi/client/Connection.scala b/client/src/main/scala/org/zalando/nakadi/client/Connection.scala index e5a62df..7b2602e 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/Connection.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/Connection.scala @@ -37,8 +37,8 @@ import javax.net.ssl.X509TrustManager trait Connection { def get(endpoint: String): Future[HttpResponse] def delete(endpoint: String): Future[HttpResponse] - def post[T](endpoint: String, model: T)(implicit serializer: Serializer[T]): Future[HttpResponse] - def put[T](endpoint: String, model: T)(implicit serializer: Serializer[T]): Future[HttpResponse] + def post[T](endpoint: String, model: T)(implicit serializer: NakadiSerializer[T]): Future[HttpResponse] + def put[T](endpoint: String, model: T)(implicit serializer: NakadiSerializer[T]): Future[HttpResponse] def stop(): Future[Terminated] def materializer(): ActorMaterializer @@ -99,12 +99,12 @@ private[client] class ConnectionImpl(host: String, port: Int, tokenProvider: () executeCall(httpRequest(endpoint, HttpMethods.GET)) } - def put[T](endpoint: String, model: T)(implicit serializer: Serializer[T]): Future[HttpResponse] = { + def put[T](endpoint: String, model: T)(implicit serializer: NakadiSerializer[T]): Future[HttpResponse] = { logger.info("Get: {}", endpoint) executeCall(httpRequest(endpoint, HttpMethods.GET)) } - def post[T](endpoint: String, model: T)(implicit serializer: Serializer[T]): Future[HttpResponse] = { + def post[T](endpoint: String, model: T)(implicit serializer: NakadiSerializer[T]): Future[HttpResponse] = { val entity = serializer.toJson(model) logger.info("Posting to endpoint {}", endpoint) logger.debug("Data to post {}", entity) diff --git a/client/src/main/scala/org/zalando/nakadi/client/Serialization.scala b/client/src/main/scala/org/zalando/nakadi/client/Serialization.scala index 06251a9..f4da31b 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/Serialization.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/Serialization.scala @@ -4,14 +4,14 @@ package org.zalando.nakadi.client /** * */ -trait Deserializer[T] { +trait NakadiDeserializer[T] { def fromJson(from: String): T } /** * */ -trait Serializer[T] { +trait NakadiSerializer[T] { def toJson(from: T): String } diff --git a/client/src/main/scala/org/zalando/nakadi/client/model/JacksonJsonMarshaller.scala b/client/src/main/scala/org/zalando/nakadi/client/model/JacksonJsonMarshaller.scala index 5600067..2d124d0 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/model/JacksonJsonMarshaller.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/model/JacksonJsonMarshaller.scala @@ -2,8 +2,8 @@ package org.zalando.nakadi.client.model import scala.reflect.runtime.universe import org.slf4j.LoggerFactory -import org.zalando.nakadi.client.Deserializer -import org.zalando.nakadi.client.Serializer +import org.zalando.nakadi.client.NakadiDeserializer +import org.zalando.nakadi.client.NakadiSerializer import org.zalando.nakadi.client.model.BatchItemPublishingStatus.BatchItemPublishingStatus import org.zalando.nakadi.client.model.BatchItemStep.BatchItemStep import org.zalando.nakadi.client.model.DataOperation.DataOperation @@ -64,18 +64,18 @@ trait JacksonJsonMarshaller { implicit def listOfEventEnrichmentStrategyTR: TypeReference[Seq[EventEnrichmentStrategy]] = new TypeReference[Seq[EventEnrichmentStrategy]] {} implicit def listOfEventTypeTR: TypeReference[Seq[EventType]] = new TypeReference[Seq[EventType]] {} - implicit def seqDeserializer[T](implicit expectedType: TypeReference[Seq[T]]) = new Deserializer[Seq[T]] { + implicit def seqDeserializer[T](implicit expectedType: TypeReference[Seq[T]]) = new NakadiDeserializer[Seq[T]] { def fromJson(from: String): Seq[T] = defaultObjectMapper.readValue[Seq[T]](from, expectedType) } - implicit def seqSerializer[T]() = new Serializer[Seq[T]] { + implicit def seqSerializer[T]() = new NakadiSerializer[Seq[T]] { def toJson(from: Seq[T]): String = defaultObjectMapper.writeValueAsString(from) } - implicit def serializer[T](implicit expectedType: TypeReference[T]): Serializer[T] = new Serializer[T] { + implicit def serializer[T](implicit expectedType: TypeReference[T]): NakadiSerializer[T] = new NakadiSerializer[T] { def toJson(from: T): String = defaultObjectMapper.writeValueAsString(from) } - implicit def deserializer[T](implicit expectedType: TypeReference[T]): Deserializer[T] = new Deserializer[T] { + implicit def deserializer[T](implicit expectedType: TypeReference[T]): NakadiDeserializer[T] = new NakadiDeserializer[T] { def fromJson(from: String): T = defaultObjectMapper.readValue[T](from, expectedType) } diff --git a/client/src/main/scala/org/zalando/nakadi/client/model/SprayJsonMarshaller.scala b/client/src/main/scala/org/zalando/nakadi/client/model/SprayJsonMarshaller.scala index a76ef48..65cafae 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/model/SprayJsonMarshaller.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/model/SprayJsonMarshaller.scala @@ -12,8 +12,8 @@ import spray.json.JsonReader import spray.json.JsonWriter import spray.json.pimpAny import spray.json.pimpString -import org.zalando.nakadi.client.Serializer -import org.zalando.nakadi.client.Deserializer +import org.zalando.nakadi.client.NakadiSerializer +import org.zalando.nakadi.client.NakadiDeserializer //trait SprayJsonMarshaller extends SprayJsonSupport with DefaultJsonProtocol { // import ParseHelper._ diff --git a/client/src/test/scala/org/zalando/nakadi/client/SerializerDeserializerTest.scala b/client/src/test/scala/org/zalando/nakadi/client/SerializerDeserializerTest.scala index 7c0d784..c1e48eb 100644 --- a/client/src/test/scala/org/zalando/nakadi/client/SerializerDeserializerTest.scala +++ b/client/src/test/scala/org/zalando/nakadi/client/SerializerDeserializerTest.scala @@ -77,7 +77,7 @@ class SerializerDeserializerTest extends WordSpec with Matchers with JacksonJson } - def checkSerializationDeserializationProcess[T](key: String, value: T)(implicit ser: Serializer[T], des: Deserializer[T]) { + def checkSerializationDeserializationProcess[T](key: String, value: T)(implicit ser: NakadiSerializer[T], des: NakadiDeserializer[T]) { val jsonEntity = ser.toJson(value) // Marshal println("#### Json-Entity:" + jsonEntity) val scalaEntity = des.fromJson(jsonEntity) //Unmarshal diff --git a/client/src/test/scala/org/zalando/nakadi/client/TestFactory.scala b/client/src/test/scala/org/zalando/nakadi/client/TestFactory.scala index e9bc112..3c86a8d 100644 --- a/client/src/test/scala/org/zalando/nakadi/client/TestFactory.scala +++ b/client/src/test/scala/org/zalando/nakadi/client/TestFactory.scala @@ -28,16 +28,16 @@ trait ClientFactory { case class EventTypesActions(client: Client) extends JacksonJsonMarshaller{ - def create(event: EventType)(implicit ser: Serializer[EventType]) = { + def create(event: EventType)(implicit ser: NakadiSerializer[EventType]) = { executeCall(client.newEventType(event)) } - def update(event: EventType)(implicit ser: Serializer[EventType]) = { + def update(event: EventType)(implicit ser: NakadiSerializer[EventType]) = { executeCall(client.updateEventType(event.name, event)) } - def get(name: String)(implicit ser: Deserializer[EventType]) = { + def get(name: String)(implicit ser: NakadiDeserializer[EventType]) = { executeCall(client.eventType(name)) } - def getAll()(implicit ser: Deserializer[Seq[EventType]]) = { + def getAll()(implicit ser: NakadiDeserializer[Seq[EventType]]) = { executeCall(client.eventTypes()) } def delete(name: String) = { From 1faa39c80582c55055be2279dfac2261d17f6c9c Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Thu, 14 Apr 2016 16:02:49 +0200 Subject: [PATCH 017/183] techjira:LAAS-60 Model adapt to neweste chanages in the API --- .../org/zalando/nakadi/client/Client.scala | 18 +++-- .../zalando/nakadi/client/ClientImpl.scala | 72 +++++++++++-------- .../zalando/nakadi/client/Connection.scala | 37 +++++----- .../client/model/JacksonJsonMarshaller.scala | 56 +++++++-------- .../zalando/nakadi/client/TestFactory.scala | 33 ++++----- .../nakadi/client/util/TestScalaEntity.scala | 15 ++-- .../client/EventTypesIntegrationTest.scala | 12 ++-- .../nakadi/client/KlientIntegrationTest.scala | 7 +- .../nakadi/client/example/ClientExample.scala | 34 +++++++++ .../client/example}/EventPublisher.scala | 3 +- .../client/example}/EventSubscriber.scala | 12 ++-- .../client/example}/EventTransformer.scala | 3 +- .../zalando/nakadi/client/model/Model.scala | 53 ++++++++------ 13 files changed, 208 insertions(+), 147 deletions(-) create mode 100644 it/src/test/scala/org/zalando/nakadi/client/example/ClientExample.scala rename {client/src/main/scala/org/zalando/nakadi/client/event => it/src/test/scala/org/zalando/nakadi/client/example}/EventPublisher.scala (95%) rename {client/src/main/scala/org/zalando/nakadi/client/event => it/src/test/scala/org/zalando/nakadi/client/example}/EventSubscriber.scala (61%) rename {client/src/main/scala/org/zalando/nakadi/client/event => it/src/test/scala/org/zalando/nakadi/client/example}/EventTransformer.scala (93%) diff --git a/client/src/main/scala/org/zalando/nakadi/client/Client.scala b/client/src/main/scala/org/zalando/nakadi/client/Client.scala index 4a65b44..6bfd0c3 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/Client.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/Client.scala @@ -1,11 +1,19 @@ package org.zalando.nakadi.client import scala.concurrent.Future -import org.zalando.nakadi.client.model.{ EventEnrichmentStrategy, EventType, EventValidationStrategy, Partition, PartitionResolutionStrategy } +import org.zalando.nakadi.client.model._ import akka.actor.Terminated import akka.http.scaladsl.model.HttpResponse import org.zalando.nakadi.client.model.Metrics +case class StreamParameters(cursor: Option[String], // + batchLimit: Option[Integer], + streamLimit: Option[Integer], + batchFlushTimeout: Option[Integer], + streamTimeout: Option[Integer], + streamKeepAliveLimit: Option[Integer], + flowId: Option[String]) + trait Client { import Client._ @@ -86,7 +94,7 @@ trait Client { * @param name - Name of the EventType * */ - def events[T](name: String)(implicit ser: NakadiDeserializer[T]): Future[Either[ClientError, Option[T]]] + def events[T](name: String, params: Option[StreamParameters])(implicit ser: NakadiDeserializer[T]): Future[Either[ClientError, Option[T]]] /** * List the partitions for the given EventType. @@ -115,7 +123,7 @@ trait Client { * curl --request GET /registry/validation-strategies * }}} */ - def validationStrategies()(implicit des: NakadiDeserializer[Seq[EventValidationStrategy]]): Future[Either[ClientError, Option[Seq[EventValidationStrategy]]]] + def validationStrategies()(implicit des: NakadiDeserializer[Seq[EventValidationStrategy.Value]]): Future[Either[ClientError, Option[Seq[EventValidationStrategy.Value]]]] /** * Returns all of the enrichment strategies supported by this installation of Nakadi. @@ -124,7 +132,7 @@ trait Client { * }}} */ - def enrichmentStrategies()(implicit des: NakadiDeserializer[Seq[EventEnrichmentStrategy]]): Future[Either[ClientError, Option[Seq[EventEnrichmentStrategy]]]] + def enrichmentStrategies()(implicit des: NakadiDeserializer[Seq[EventEnrichmentStrategy.Value]]): Future[Either[ClientError, Option[Seq[EventEnrichmentStrategy.Value]]]] /** * Returns all of the partitioning strategies supported by this installation of Nakadi. @@ -132,7 +140,7 @@ trait Client { * curl --request GET /registry/partitioning-strategies * }}} */ - def partitionStrategies()(implicit des: NakadiDeserializer[Seq[PartitionResolutionStrategy]]): Future[Either[ClientError, Option[Seq[PartitionResolutionStrategy]]]] + def partitionStrategies()(implicit des: NakadiDeserializer[Seq[PartitionStrategy.Value]]): Future[Either[ClientError, Option[Seq[PartitionStrategy.Value]]]] /** * Shuts down the communication system of the client diff --git a/client/src/main/scala/org/zalando/nakadi/client/ClientImpl.scala b/client/src/main/scala/org/zalando/nakadi/client/ClientImpl.scala index ab12811..565a116 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/ClientImpl.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/ClientImpl.scala @@ -1,30 +1,28 @@ package org.zalando.nakadi.client -import scala.{ Left, Right } +import scala.Left +import scala.Right import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.Future import scala.concurrent.duration.DurationInt -import akka.http.scaladsl.model.HttpResponse import org.slf4j.LoggerFactory -import org.zalando.nakadi.client.model.{ EventEnrichmentStrategy, EventType, EventValidationStrategy, Partition, PartitionResolutionStrategy } +import org.zalando.nakadi.client.model._ import com.typesafe.scalalogging.Logger import Client.ClientError import akka.actor.Terminated +import akka.http.scaladsl.model.HttpHeader +import akka.http.scaladsl.model.HttpResponse import akka.http.scaladsl.model.HttpResponse -import scala.util.Try -import scala.util.Failure -import scala.util.Success -import spray.json.DeserializationException import akka.http.scaladsl.model.StatusCodes import akka.http.scaladsl.unmarshalling.Unmarshal -import org.zalando.nakadi.client.model.Metrics +import akka.http.scaladsl.model.headers.RawHeader private[client] class ClientImpl(connection: Connection, charSet: String = "UTF-8") extends Client { import Client._ implicit val materializer = connection.materializer val logger = Logger(LoggerFactory.getLogger(this.getClass)) - def metrics()(implicit ser: NakadiDeserializer[Metrics]): Future[Either[ClientError, Option[Metrics]]] ={ + def metrics()(implicit ser: NakadiDeserializer[Metrics]): Future[Either[ClientError, Option[Metrics]]] = { logFutureEither(connection.get(URI_METRICS).flatMap(mapToEither(_))) } @@ -33,7 +31,7 @@ private[client] class ClientImpl(connection: Connection, charSet: String = "UTF- } def newEventType(eventType: EventType)(implicit ser: NakadiSerializer[EventType]): Future[Option[ClientError]] = { - logFutureOption(connection.post(URI_EVENT_TYPES, eventType).map(mapToOption(_))) + logFutureOption(connection.post(URI_EVENT_TYPES, eventType).flatMap(mapToOption(_))) } def eventType(name: String)(implicit ser: NakadiDeserializer[EventType]): Future[Either[ClientError, Option[EventType]]] = { @@ -42,22 +40,22 @@ private[client] class ClientImpl(connection: Connection, charSet: String = "UTF- def updateEventType(name: String, eventType: EventType)(implicit ser: NakadiSerializer[EventType]): Future[Option[ClientError]] = { val result = connection.put(URI_EVENT_TYPE_BY_NAME.format(name), eventType) - logFutureOption(result.map(in => mapToOption(in))) + logFutureOption(result.flatMap(in => mapToOption(in))) } def deleteEventType(name: String): Future[Option[ClientError]] = { - logFutureOption(connection.delete(URI_EVENT_TYPE_BY_NAME.format(name)).map(in => mapToOption(in))) + logFutureOption(connection.delete(URI_EVENT_TYPE_BY_NAME.format(name)).flatMap(in => mapToOption(in))) } def newEvents[T](name: String, events: Seq[T])(implicit ser: NakadiSerializer[Seq[T]]): Future[Option[ClientError]] = { - logFutureOption(connection.post(URI_EVENTS_OF_EVENT_TYPE.format(name), events).map(in => mapToOption(in))) + logFutureOption(connection.post(URI_EVENTS_OF_EVENT_TYPE.format(name), events).flatMap(in => mapToOption(in))) } - def events[T](name: String)(implicit ser: NakadiDeserializer[T]): Future[Either[ClientError, Option[T]]] = { + def events[T](name: String, params: Option[StreamParameters])(implicit ser: NakadiDeserializer[T]): Future[Either[ClientError, Option[T]]] = { + val headers = createHeaders(params) logFutureEither(connection.get(URI_EVENTS_OF_EVENT_TYPE.format(name)).flatMap(in => mapToEither(in))) } - def partitions(name: String)(implicit ser: NakadiDeserializer[Partition]): Future[Either[ClientError, Option[Partition]]] = { logFutureEither(connection.get(URI_PARTITIONS_BY_EVENT_TYPE.format(name)).flatMap(in => mapToEither(in))) } @@ -66,15 +64,15 @@ private[client] class ClientImpl(connection: Connection, charSet: String = "UTF- logFutureEither(connection.get(URI_PARTITION_BY_EVENT_TYPE_AND_ID.format(name)).flatMap(in => mapToEither(in))) } - def validationStrategies()(implicit des: NakadiDeserializer[Seq[EventValidationStrategy]]): Future[Either[ClientError, Option[Seq[EventValidationStrategy]]]] = { + def validationStrategies()(implicit des: NakadiDeserializer[Seq[EventValidationStrategy.Value]]): Future[Either[ClientError, Option[Seq[EventValidationStrategy.Value]]]] = { logFutureEither(connection.get(URI_VALIDATION_STRATEGIES).flatMap(mapToEither(_))) } - def enrichmentStrategies()(implicit des: NakadiDeserializer[Seq[EventEnrichmentStrategy]]): Future[Either[ClientError, Option[Seq[EventEnrichmentStrategy]]]] = { + def enrichmentStrategies()(implicit des: NakadiDeserializer[Seq[EventEnrichmentStrategy.Value]]): Future[Either[ClientError, Option[Seq[EventEnrichmentStrategy.Value]]]] = { logFutureEither(connection.get(URI_ENRICHMENT_STRATEGIES).flatMap(mapToEither(_))) } - def partitionStrategies()(implicit des: NakadiDeserializer[Seq[PartitionResolutionStrategy]]): Future[Either[ClientError, Option[Seq[PartitionResolutionStrategy]]]] = + def partitionStrategies()(implicit des: NakadiDeserializer[Seq[PartitionStrategy.Value]]): Future[Either[ClientError, Option[Seq[PartitionStrategy.Value]]]] = logFutureEither(connection.get(URI_PARTITIONING_STRATEGIES).flatMap(mapToEither(_))) def stop(): Future[Terminated] = connection.stop() @@ -83,14 +81,14 @@ private[client] class ClientImpl(connection: Connection, charSet: String = "UTF- //# HELPER METHODS # //#################### - def logFutureEither[A, T](future: Future[Either[ClientError, T]]): Future[Either[ClientError, T]] = { + private[client] def logFutureEither[A, T](future: Future[Either[ClientError, T]]): Future[Either[ClientError, T]] = { future recover { case e: Throwable => logger.error("A unexpected error occured", e) Left(ClientError("Error: " + e.getMessage, None)) } } - def logFutureOption(future: Future[Option[ClientError]]): Future[Option[ClientError]] = { + private[client] def logFutureOption(future: Future[Option[ClientError]]): Future[Option[ClientError]] = { future recover { case e: Throwable => logger.error("A unexpected error occured", e) @@ -98,7 +96,7 @@ private[client] class ClientImpl(connection: Connection, charSet: String = "UTF- } } - def mapToEither[T](response: HttpResponse)(implicit deserializer: NakadiDeserializer[T]): Future[Either[ClientError, Option[T]]] = { + private[client] def mapToEither[T](response: HttpResponse)(implicit deserializer: NakadiDeserializer[T]): Future[Either[ClientError, Option[T]]] = { logger.debug("received [response={}]", response) response match { case HttpResponse(status, headers, entity, protocol) if (status.isSuccess()) => @@ -125,25 +123,43 @@ private[client] class ClientImpl(connection: Connection, charSet: String = "UTF- } } - def mapToOption[T](response: HttpResponse): Option[ClientError] = { + private[client] def mapToOption[T](response: HttpResponse): Future[Option[ClientError]] = { response.status match { case status if (status.isSuccess()) => logger.debug("Success. http-status: %s", status.intValue().toString()) - None + Future.successful(None) case status if (status.isRedirection()) => val msg = "Redirection - http-status: %s, reason[%s]".format(status.intValue().toString(), status.reason()) logger.info(msg) response.entity.toStrict(10.second).map { body => logger.debug("Redirection - http-status: %s, reason[%s], body:[%s]".format(status.intValue().toString(), status.reason(), body.data.decodeString(charSet))) } - Option(ClientError(msg, Some(status.intValue()))) + Future.successful(Option(ClientError(msg, Some(status.intValue())))) case status if (status.isFailure()) => response.entity.toStrict(10.second).map { body => - logger.debug("Failure - http-status: %s, reason[%s], body:[%s]".format(status.intValue().toString(), status.reason(), body.data.decodeString(charSet))) + val msg = "Failure - http-status: %s, reason[%s], body:[%s]".format(status.intValue().toString(), status.reason(), body.data.decodeString(charSet)) + logger.warn(msg) + Option(ClientError(msg, Some(status.intValue()))) } - val msg = "Failure - http-status: %s, reason[%s], body:[%s]".format(status.intValue().toString(), status.reason(), response.entity.dataBytes) - logger.warn(msg) - Option(ClientError(msg, Some(status.intValue()))) + } + } + + private[client] def createHeaders(params: Option[StreamParameters]): List[HttpHeader] = { + params match { + case Some(StreamParameters(cursor, _, _, _, _, _, flowId)) => + val parameters = List(("X-nakadi-cursors", cursor), ("X-Flow-Id", flowId)) + for { (key, optional) <- parameters; value <- optional } yield RawHeader(key, value) + case None => Nil + } + } + private[client] def createQueryParams(params: Option[StreamParameters]): List[(String, Any)] = { + params match { + case Some(StreamParameters(_, batchLimit, streamLimit, batchFlushTimeout, streamTimeout, streamKeepAliveLimit, _)) => + val parameters = List(("batch_limit", batchLimit), ("stream_limit", streamLimit), // + ("batch_flush_timeout", batchFlushTimeout), ("stream_timeout", streamTimeout), // + ("stream_keep_alive_limit", streamKeepAliveLimit)) + for { (key, optional) <- parameters; value <- optional } yield (key -> value) + case None => Nil } } diff --git a/client/src/main/scala/org/zalando/nakadi/client/Connection.scala b/client/src/main/scala/org/zalando/nakadi/client/Connection.scala index 7b2602e..c1f9415 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/Connection.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/Connection.scala @@ -2,15 +2,11 @@ package org.zalando.nakadi.client import java.security.SecureRandom import java.security.cert.X509Certificate - import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.Future import scala.concurrent.duration.DurationInt - import org.slf4j.LoggerFactory - import com.typesafe.scalalogging.Logger - import akka.actor.ActorSystem import akka.actor.Terminated import akka.http.scaladsl.Http @@ -33,9 +29,12 @@ import akka.stream.scaladsl.Source import javax.net.ssl.SSLContext import javax.net.ssl.TrustManager import javax.net.ssl.X509TrustManager +import akka.http.scaladsl.model.HttpHeader +import scala.collection.immutable.Seq trait Connection { def get(endpoint: String): Future[HttpResponse] + def get(endpoint: String, headers: Seq[HttpHeader]): Future[HttpResponse] def delete(endpoint: String): Future[HttpResponse] def post[T](endpoint: String, model: T)(implicit serializer: NakadiSerializer[T]): Future[HttpResponse] def put[T](endpoint: String, model: T)(implicit serializer: NakadiSerializer[T]): Future[HttpResponse] @@ -96,24 +95,28 @@ private[client] class ConnectionImpl(host: String, port: Int, tokenProvider: () def get(endpoint: String): Future[HttpResponse] = { logger.info("Get: {}", endpoint) - executeCall(httpRequest(endpoint, HttpMethods.GET)) + executeCall(httpRequest(endpoint, HttpMethods.GET,Nil)) + } + def get(endpoint: String, headers: Seq[HttpHeader]): Future[HttpResponse] = { + logger.info("Get: {}", endpoint) + executeCall(httpRequest(endpoint, HttpMethods.GET,headers)) } def put[T](endpoint: String, model: T)(implicit serializer: NakadiSerializer[T]): Future[HttpResponse] = { logger.info("Get: {}", endpoint) - executeCall(httpRequest(endpoint, HttpMethods.GET)) + executeCall(httpRequest(endpoint, HttpMethods.GET,Nil)) } def post[T](endpoint: String, model: T)(implicit serializer: NakadiSerializer[T]): Future[HttpResponse] = { val entity = serializer.toJson(model) - logger.info("Posting to endpoint {}", endpoint) - logger.debug("Data to post {}", entity) - executeCall(httpRequestWithPayload(endpoint, entity, HttpMethods.POST)) + logger.info("Posting to endpoint {}", endpoint) + logger.debug("Data to post {}", entity) + executeCall(httpRequestWithPayload(endpoint, entity, HttpMethods.POST)) } - + def delete(endpoint: String): Future[HttpResponse] = { logger.info("Delete: {}", endpoint) - executeCall(httpRequest(endpoint, HttpMethods.DELETE)) + executeCall(httpRequest(endpoint, HttpMethods.DELETE,Nil)) } private def executeCall(request: HttpRequest): Future[HttpResponse] = { @@ -124,23 +127,23 @@ private[client] class ConnectionImpl(host: String, port: Int, tokenProvider: () logError(response) response } - + private def logError(future: Future[Any]) { future recover { case e: Throwable => logger.error("Failed to call endpoint with: ", e.getMessage) } } - private def httpRequest(url: String, httpMethod: HttpMethod): HttpRequest = { - HttpRequest(uri = url, method = httpMethod).withHeaders(headers.Authorization(OAuth2BearerToken(tokenProvider())), - headers.Accept(MediaRange(`application/json`))) + private def httpRequest(url: String, httpMethod: HttpMethod, additionalHeaders: Seq[HttpHeader]): HttpRequest = { + val allHeaders:Seq[HttpHeader] = additionalHeaders :+ headers.Accept(MediaRange(`application/json`)) :+ headers.Authorization(OAuth2BearerToken(tokenProvider())) + HttpRequest(uri = url, method = httpMethod).withHeaders(allHeaders) } - + private def httpRequestWithPayload(url: String, entity: String, httpMethod: HttpMethod): HttpRequest = { HttpRequest(uri = url, method = httpMethod) // .withHeaders(headers.Authorization(OAuth2BearerToken(tokenProvider())), headers.Accept(MediaRange(`application/json`))) - .withEntity(ContentType(`application/json`),entity) + .withEntity(ContentType(`application/json`), entity) } def stop(): Future[Terminated] = actorSystem.terminate() diff --git a/client/src/main/scala/org/zalando/nakadi/client/model/JacksonJsonMarshaller.scala b/client/src/main/scala/org/zalando/nakadi/client/model/JacksonJsonMarshaller.scala index 2d124d0..f94d0fc 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/model/JacksonJsonMarshaller.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/model/JacksonJsonMarshaller.scala @@ -1,53 +1,47 @@ package org.zalando.nakadi.client.model import scala.reflect.runtime.universe +import scala.reflect.runtime.universe +import scala.reflect.runtime.universe._ import org.slf4j.LoggerFactory import org.zalando.nakadi.client.NakadiDeserializer import org.zalando.nakadi.client.NakadiSerializer -import org.zalando.nakadi.client.model.BatchItemPublishingStatus.BatchItemPublishingStatus -import org.zalando.nakadi.client.model.BatchItemStep.BatchItemStep -import org.zalando.nakadi.client.model.DataOperation.DataOperation -import org.zalando.nakadi.client.model.EventTypeCategory.EventTypeCategory -import org.zalando.nakadi.client.model.SchemaType.SchemaType +import com.fasterxml.jackson.annotation.JsonInclude import com.fasterxml.jackson.core.JsonFactory -import com.fasterxml.jackson.core.JsonGenerator import com.fasterxml.jackson.core.JsonParser -import com.fasterxml.jackson.core.Version import com.fasterxml.jackson.core.`type`.TypeReference import com.fasterxml.jackson.databind.DeserializationContext import com.fasterxml.jackson.databind.DeserializationFeature import com.fasterxml.jackson.databind.JsonDeserializer -import com.fasterxml.jackson.databind.JsonSerializer import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.databind.PropertyNamingStrategy import com.fasterxml.jackson.databind.SerializationFeature -import com.fasterxml.jackson.databind.SerializerProvider import com.fasterxml.jackson.databind.deser.DeserializationProblemHandler -import com.fasterxml.jackson.databind.module.SimpleModule +import com.fasterxml.jackson.databind.deser.std.ObjectArrayDeserializer import com.fasterxml.jackson.module.scala.DefaultScalaModule import com.typesafe.scalalogging.Logger -import scala.reflect._ -import scala.reflect.runtime.universe -import scala.reflect.runtime.universe._ -import com.fasterxml.jackson.databind.ser.std.StdScalarSerializer -import com.fasterxml.jackson.annotation.JsonInclude - +import com.fasterxml.jackson.databind.`type`.ArrayType +import com.fasterxml.jackson.databind.jsontype.TypeDeserializer +//case class CustomDeserialier(array: ArrayType, des: JsonDeserializer[AnyRef], elem: TypeDeserializer) extends ObjectArrayDeserializer(array, des, elem) { +// override def getNullValue():ArrayList={ +// null +// } +//} trait JacksonJsonMarshaller { val logger = Logger(LoggerFactory.getLogger(this.getClass)) val factory = new JsonFactory(); - // All TypeReferences implicit def problemTR: TypeReference[Problem] = new TypeReference[Problem] {} implicit def metricsTR: TypeReference[Metrics] = new TypeReference[Metrics] {} implicit def partitionTR: TypeReference[Partition] = new TypeReference[Partition] {} implicit def cursorTR: TypeReference[Cursor] = new TypeReference[Cursor] {} implicit def eventTypeSchemaTR: TypeReference[EventTypeSchema] = new TypeReference[EventTypeSchema] {} - implicit def eventValidationStrategyTR: TypeReference[EventValidationStrategy] = new TypeReference[EventValidationStrategy] {} - implicit def partitionResolutionStrategyTR: TypeReference[PartitionResolutionStrategy] = new TypeReference[PartitionResolutionStrategy] {} - implicit def eventEnrichmentStrategyTR: TypeReference[EventEnrichmentStrategy] = new TypeReference[EventEnrichmentStrategy] {} + implicit def eventValidationStrategyTR: TypeReference[EventValidationStrategy.Value] = new TypeReference[EventValidationStrategy.Value] {} + implicit def partitionResolutionStrategyTR: TypeReference[PartitionStrategy.Value] = new TypeReference[PartitionStrategy.Value] {} + implicit def eventEnrichmentStrategyTR: TypeReference[EventEnrichmentStrategy.Value] = new TypeReference[EventEnrichmentStrategy.Value] {} implicit def dataChangeEventQualifierTR: TypeReference[DataChangeEventQualifier] = new TypeReference[DataChangeEventQualifier] {} implicit def eventTypeStatisticsTR: TypeReference[EventTypeStatistics] = new TypeReference[EventTypeStatistics] {} implicit def eventTypeTR: TypeReference[EventType] = new TypeReference[EventType] {} @@ -59,16 +53,18 @@ trait JacksonJsonMarshaller { implicit def dataChangeEventTR: TypeReference[DataChangeEvent[Any]] = new TypeReference[DataChangeEvent[Any]] {} //Lists - implicit def listOfPartitionResolutionStrategyTR: TypeReference[Seq[PartitionResolutionStrategy]] = new TypeReference[Seq[PartitionResolutionStrategy]] {} - implicit def listOfEventValidationStrategyTR: TypeReference[Seq[EventValidationStrategy]] = new TypeReference[Seq[EventValidationStrategy]] {} - implicit def listOfEventEnrichmentStrategyTR: TypeReference[Seq[EventEnrichmentStrategy]] = new TypeReference[Seq[EventEnrichmentStrategy]] {} + implicit def listOfPartitionResolutionStrategyTR: TypeReference[Seq[PartitionStrategy.Value]] = new TypeReference[Seq[PartitionStrategy.Value]] {} + implicit def listOfEventValidationStrategyTR: TypeReference[Seq[EventValidationStrategy.Value]] = new TypeReference[Seq[EventValidationStrategy.Value]] {} + implicit def listOfEventEnrichmentStrategyTR: TypeReference[Seq[EventEnrichmentStrategy.Value]] = new TypeReference[Seq[EventEnrichmentStrategy.Value]] {} implicit def listOfEventTypeTR: TypeReference[Seq[EventType]] = new TypeReference[Seq[EventType]] {} - implicit def seqDeserializer[T](implicit expectedType: TypeReference[Seq[T]]) = new NakadiDeserializer[Seq[T]] { - def fromJson(from: String): Seq[T] = defaultObjectMapper.readValue[Seq[T]](from, expectedType) - } - implicit def seqSerializer[T]() = new NakadiSerializer[Seq[T]] { - def toJson(from: Seq[T]): String = defaultObjectMapper.writeValueAsString(from) + + + implicit def optionalDeserializer[T](implicit expectedType: TypeReference[T]): NakadiDeserializer[Option[T]] = new NakadiDeserializer[Option[T]] { + def fromJson(from: String): Option[T] = { + + defaultObjectMapper.readValue[Option[T]](from, expectedType) + } } implicit def serializer[T](implicit expectedType: TypeReference[T]): NakadiSerializer[T] = new NakadiSerializer[T] { @@ -84,6 +80,7 @@ trait JacksonJsonMarshaller { .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) .setPropertyNamingStrategy(PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES) .setSerializationInclusion(JsonInclude.Include.NON_EMPTY) + // .enable(DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT) .addHandler(new DeserializationProblemHandler() { override def handleUnknownProperty(ctxt: DeserializationContext, jp: JsonParser, deserializer: JsonDeserializer[_], @@ -92,8 +89,7 @@ trait JacksonJsonMarshaller { logger.warn(s"unknown property occurred in JSON representation: [beanOrClass=$beanOrClass, property=$propertyName]") true } - }) + }) - } diff --git a/client/src/test/scala/org/zalando/nakadi/client/TestFactory.scala b/client/src/test/scala/org/zalando/nakadi/client/TestFactory.scala index 3c86a8d..7f72d81 100644 --- a/client/src/test/scala/org/zalando/nakadi/client/TestFactory.scala +++ b/client/src/test/scala/org/zalando/nakadi/client/TestFactory.scala @@ -4,18 +4,17 @@ import scala.concurrent.Await import scala.concurrent.Future import scala.concurrent.duration.DurationInt import scala.util.Random -import org.joda.time.format.ISODateTimeFormat + +import org.joda.time.DateTime import org.zalando.nakadi.client.model.Event import org.zalando.nakadi.client.model.EventMetadata +import org.zalando.nakadi.client.model.EventMetadata import org.zalando.nakadi.client.model.EventType import org.zalando.nakadi.client.model.EventTypeCategory import org.zalando.nakadi.client.model.EventTypeSchema -import org.zalando.nakadi.client.model.PartitionResolutionStrategy -import org.zalando.nakadi.client.model.SchemaType -//import org.zalando.nakadi.client.model.SprayJsonMarshaller -import org.joda.time.DateTime -import org.zalando.nakadi.client.model.EventMetadata import org.zalando.nakadi.client.model.JacksonJsonMarshaller +import org.zalando.nakadi.client.model.PartitionStrategy +import org.zalando.nakadi.client.model.SchemaType trait ClientFactory { val host = "" @@ -23,10 +22,9 @@ trait ClientFactory { val port = 443 val client = new ClientImpl(Connection.newConnection(host, port, OAuth2Token, true, false), "UTF-8") - } -case class EventTypesActions(client: Client) extends JacksonJsonMarshaller{ +case class EventTypesActions(client: Client) extends JacksonJsonMarshaller { def create(event: EventType)(implicit ser: NakadiSerializer[EventType]) = { executeCall(client.newEventType(event)) @@ -34,10 +32,10 @@ case class EventTypesActions(client: Client) extends JacksonJsonMarshaller{ def update(event: EventType)(implicit ser: NakadiSerializer[EventType]) = { executeCall(client.updateEventType(event.name, event)) } - def get(name: String)(implicit ser: NakadiDeserializer[EventType]) = { + def get(name: String)(implicit ser: NakadiDeserializer[Option[EventType]]) = { executeCall(client.eventType(name)) } - def getAll()(implicit ser: NakadiDeserializer[Seq[EventType]]) = { + def getAll()(implicit ser: NakadiDeserializer[Option[Seq[EventType]]]) = { executeCall(client.eventTypes()) } def delete(name: String) = { @@ -51,23 +49,22 @@ case class EventTypesActions(client: Client) extends JacksonJsonMarshaller{ trait ModelFactory { val x = Random.alphanumeric - def partitionStrategy() = new PartitionResolutionStrategy("hash", None) def paritionKeyFields() = List("order_number") def schemaDefinition() = """{ "properties": { "order_number": { "type": "string" } } }""" def eventTypeSchema() = new EventTypeSchema(SchemaType.JSON, schemaDefinition) - + def createUniqueEventType(): EventType = { //Unique events mean unique name new EventType("test-client-integration-event-" + Random.nextInt() + "-" + Random.nextInt() + Random.nextInt(), // "laas-team", // - EventTypeCategory.BUSINESS, None, None, // - partitionStrategy, Option(eventTypeSchema), // - None, Option(paritionKeyFields), None) + EventTypeCategory.UNDEFINED, None, Nil, // + Some(PartitionStrategy.RANDOM), Option(eventTypeSchema), // + None, Option(paritionKeyFields), None) } def createEventMetadata(): EventMetadata = { - val length=5 - val eid= java.util.UUID.randomUUID.toString + val length = 5 + val eid = java.util.UUID.randomUUID.toString val occurredAt = new DateTime().toString() - new EventMetadata(eid,None,occurredAt,None,Nil,None,None) + new EventMetadata(eid, None, occurredAt, None, Nil, None, None) } } \ No newline at end of file diff --git a/client/src/test/scala/org/zalando/nakadi/client/util/TestScalaEntity.scala b/client/src/test/scala/org/zalando/nakadi/client/util/TestScalaEntity.scala index f4717b5..43d4e47 100644 --- a/client/src/test/scala/org/zalando/nakadi/client/util/TestScalaEntity.scala +++ b/client/src/test/scala/org/zalando/nakadi/client/util/TestScalaEntity.scala @@ -2,7 +2,6 @@ package org.zalando.nakadi.client.util import org.zalando.nakadi.client.model._ import org.zalando.nakadi.client.model.Problem -import org.zalando.nakadi.client.model.PartitionResolutionStrategy import org.zalando.nakadi.client.model.Partition import org.zalando.nakadi.client.model.Metrics import org.zalando.nakadi.client.model.EventValidationStrategy @@ -26,21 +25,21 @@ object TestScalaEntity { val partition = new Partition("partition", "oldestAvailableOffset", "newestAvailableOffset") val cursor = new Cursor("partition", "offset") val eventTypeSchema = new EventTypeSchema(SchemaType.JSON, "schema") - val eventValidationStrategy = new EventValidationStrategy("name", Option("doc")) - val partitionResolutionStrategy = new PartitionResolutionStrategy("name", Option("doc")) - val eventEnrichmentStrategy = new EventEnrichmentStrategy("name", Option("doc")) + val eventValidationStrategy = EventValidationStrategy.NONE + val partitionResolutionStrategy = PartitionStrategy.HASH + val eventEnrichmentStrategy = EventEnrichmentStrategy.METADATA //Complex objects val eventTypeStatistics = new EventTypeStatistics(Option(9281002), Option(19283), Option(21), Option(312)) - val eventType = new EventType("name", "owner", EventTypeCategory.BUSINESS, Option(List("validationStrategies")), Option(List("enrichmentStrategies")), partitionResolutionStrategy, Option(eventTypeSchema), Option(List("dataKeyFields")), Option(List("partitioningKeyFields")), Option(eventTypeStatistics)) + val eventType = new EventType("name", "owner", EventTypeCategory.BUSINESS, Option(List(EventValidationStrategy.NONE)), List(EventEnrichmentStrategy.METADATA), Some(partitionResolutionStrategy), Option(eventTypeSchema), Option(List("dataKeyFields")), Option(List("partitioningKeyFields")), Option(eventTypeStatistics)) val eventMetadata = new EventMetadata("eid", Option(eventType), "occurredAt", Option("receivedAt"), List("parentEids"), Option("flowId"), Option("partition")) - case class MyEvent(name:String,metadata:EventMetadata) extends Event - val myEvent = new MyEvent("test",eventMetadata) + case class MyEvent(name:String, metadata:Option[EventMetadata]) extends Event + val myEvent = new MyEvent("test",Some(eventMetadata)) val eventStreamBatch = new EventStreamBatch(cursor, List(myEvent)) val batchItemResponse = new BatchItemResponse(Option("eid"), BatchItemPublishingStatus.SUBMITTED, Option(BatchItemStep.PUBLISHING), Option("detail")) // own event case class CommissionEntity(id:String,ql:List[String]) val commissionEntity=new CommissionEntity("id2",List("ql1","ql2")) - val dataChangeEvent = new DataChangeEvent[CommissionEntity](commissionEntity,"Critical", DataOperation.DELETE, eventMetadata) + val dataChangeEvent = new DataChangeEvent[CommissionEntity](commissionEntity,"Critical", DataOperation.DELETE, Some(eventMetadata)) } \ No newline at end of file diff --git a/it/src/test/scala/org/zalando/nakadi/client/EventTypesIntegrationTest.scala b/it/src/test/scala/org/zalando/nakadi/client/EventTypesIntegrationTest.scala index bf99568..8f42864 100644 --- a/it/src/test/scala/org/zalando/nakadi/client/EventTypesIntegrationTest.scala +++ b/it/src/test/scala/org/zalando/nakadi/client/EventTypesIntegrationTest.scala @@ -8,7 +8,7 @@ class EventTypeTest extends WordSpec with Matchers with JacksonJsonMarshaller wi val events = new EventTypesActions(client) "POST/PUT/GET/DELETE single EventType " in { - //Create event + //Create event val eventType = createUniqueEventType() val creationResult = events.create(eventType) creationResult.isDefined shouldBe false @@ -17,13 +17,13 @@ class EventTypeTest extends WordSpec with Matchers with JacksonJsonMarshaller wi checkEventTypeExists(eventType) //TODO: Enable this when PUT is supported. -// Update the event -// val updatedEvent = eventType.copy(owningApplication = "laas-team-2") -// events.update(updatedEvent) + // Update the event + // val updatedEvent = eventType.copy(owningApplication = "laas-team-2") + // events.update(updatedEvent) //Check the EventType has bee updated -// checkEventTypeExists(updatedEvent) -// checkEventTypeDoesNotExist(eventType) + // checkEventTypeExists(updatedEvent) + // checkEventTypeDoesNotExist(eventType) //Delete the created Event val deletedEvent = events.delete(eventType.name) diff --git a/it/src/test/scala/org/zalando/nakadi/client/KlientIntegrationTest.scala b/it/src/test/scala/org/zalando/nakadi/client/KlientIntegrationTest.scala index c1add84..5f136dc 100644 --- a/it/src/test/scala/org/zalando/nakadi/client/KlientIntegrationTest.scala +++ b/it/src/test/scala/org/zalando/nakadi/client/KlientIntegrationTest.scala @@ -13,7 +13,6 @@ import org.zalando.nakadi.client.ClientFactory import org.zalando.nakadi.client.ModelFactory import scala.Right class KlientIntegrationTest extends WordSpec with Matchers with JacksonJsonMarshaller with ModelFactory with ClientFactory { - "Nakadi Client" should { "parse multiple PartitionResolutionStrategy" in { val Right(result) = executeCall(client.partitionStrategies()) @@ -54,10 +53,10 @@ class KlientIntegrationTest extends WordSpec with Matchers with JacksonJsonMarsh import spray.json._ //Matches the one defined in the schema of - case class EventExample(orderNumber: String, metadata: EventMetadata) extends Event + case class EventExample(orderNumber: String, metadata: Option[EventMetadata]) extends Event implicit val eventExample: TypeReference[Seq[EventExample]] = new TypeReference[Seq[EventExample]] {} - - val event = new EventExample("22301982837", createEventMetadata()) + + val event = new EventExample("22301982837", Some(createEventMetadata())) val eventType = createUniqueEventType() executeCall(client.newEventType(eventType)) shouldBe None executeCall(client.newEvents[EventExample](eventType.name, List(event))) shouldBe None diff --git a/it/src/test/scala/org/zalando/nakadi/client/example/ClientExample.scala b/it/src/test/scala/org/zalando/nakadi/client/example/ClientExample.scala new file mode 100644 index 0000000..91adcaf --- /dev/null +++ b/it/src/test/scala/org/zalando/nakadi/client/example/ClientExample.scala @@ -0,0 +1,34 @@ +package org.zalando.nakadi.client.example + +import java.math.BigInteger +import akka.actor._ +import akka.stream.actor._ +import org.zalando.nakadi.client.ClientImpl +import org.zalando.nakadi.client.Connection +import org.zalando.nakadi.client.ClientFactory + +object Main extends App { + + val system = ActorSystem("example-stream-system") + ClientExample.startSimplePubSubExample(system) + val client = ClientExample.nakadiClient() + +} + +object ClientExample extends ClientFactory { + def nakadiClient() = client + + def startSimplePubSubExample(system: ActorSystem) { + system.log.info("Starting Publisher") + val publisherActor = system.actorOf(Props[EventPublisher]) + val publisher = ActorPublisher[BigInteger](publisherActor) + + system.log.info("Starting Subscriber") + val subscriberActor = system.actorOf(Props(new EventSubscriber(500))) + val subscriber = ActorSubscriber[BigInteger](subscriberActor) + + system.log.info("Subscribing to Publisher") + publisher.subscribe(subscriber) + } + +} \ No newline at end of file diff --git a/client/src/main/scala/org/zalando/nakadi/client/event/EventPublisher.scala b/it/src/test/scala/org/zalando/nakadi/client/example/EventPublisher.scala similarity index 95% rename from client/src/main/scala/org/zalando/nakadi/client/event/EventPublisher.scala rename to it/src/test/scala/org/zalando/nakadi/client/example/EventPublisher.scala index 47500e8..247bce4 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/event/EventPublisher.scala +++ b/it/src/test/scala/org/zalando/nakadi/client/example/EventPublisher.scala @@ -1,7 +1,6 @@ -package org.zalando.nakadi.client.event +package org.zalando.nakadi.client.example import java.math.BigInteger - import akka.actor.ActorLogging import akka.stream.actor.ActorPublisher import akka.stream.actor.ActorPublisherMessage.{ Cancel, Request } diff --git a/client/src/main/scala/org/zalando/nakadi/client/event/EventSubscriber.scala b/it/src/test/scala/org/zalando/nakadi/client/example/EventSubscriber.scala similarity index 61% rename from client/src/main/scala/org/zalando/nakadi/client/event/EventSubscriber.scala rename to it/src/test/scala/org/zalando/nakadi/client/example/EventSubscriber.scala index 30c1f00..cad64ee 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/event/EventSubscriber.scala +++ b/it/src/test/scala/org/zalando/nakadi/client/example/EventSubscriber.scala @@ -1,4 +1,4 @@ -package org.zalando.nakadi.client.event +package org.zalando.nakadi.client.example import akka.stream.actor.WatermarkRequestStrategy import java.math.BigInteger @@ -8,21 +8,21 @@ import akka.stream.actor.ActorSubscriberMessage.OnError import akka.stream.actor.ActorSubscriberMessage.OnNext import akka.stream.actor.ActorSubscriberMessage.OnComplete -class SlowSubscriber(delay: Long) extends ActorSubscriber with ActorLogging { // 1 +class EventSubscriber(delay: Long) extends ActorSubscriber with ActorLogging { // 1 val requestStrategy = WatermarkRequestStrategy(50) // 2 def receive = { case OnNext(fib: BigInteger) => // 3 - log.debug("[SlowSubscriber] Received Fibonacci Number: {}", fib) + log.debug("[EventSubscriber] Received Fibonacci Number: {}", fib) Thread.sleep(delay) case OnError(err: Exception) => // 4 - log.error(err, "[SlowSubscriber] Receieved Exception in Fibonacci Stream") + log.error(err, "[EventSubscriber] Receieved Exception in Fibonacci Stream") context.stop(self) case OnComplete => // 5 - log.info("[SlowSubscriber] Fibonacci Stream Completed!") + log.info("[EventSubscriber] Fibonacci Stream Completed!") context.stop(self) case _ => - log.info("[SlowSubscriber] Unknown!") + log.info("[EventSubscriber] Unknown!") } diff --git a/client/src/main/scala/org/zalando/nakadi/client/event/EventTransformer.scala b/it/src/test/scala/org/zalando/nakadi/client/example/EventTransformer.scala similarity index 93% rename from client/src/main/scala/org/zalando/nakadi/client/event/EventTransformer.scala rename to it/src/test/scala/org/zalando/nakadi/client/example/EventTransformer.scala index a9642ae..b210b0e 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/event/EventTransformer.scala +++ b/it/src/test/scala/org/zalando/nakadi/client/example/EventTransformer.scala @@ -1,4 +1,4 @@ -package org.zalando.nakadi.client.event +package org.zalando.nakadi.client.example import java.math.BigInteger import akka.stream.actor.ActorSubscriber @@ -10,6 +10,7 @@ import akka.stream.actor.ActorSubscriberMessage.OnNext import akka.stream.actor.ActorPublisher import scala.collection.mutable.{ Queue => MQueue } import akka.stream.actor.ActorPublisherMessage.Cancel +import scala.collection.mutable.{Queue => MQueue} class EventTransformer extends ActorSubscriber with ActorPublisher[BigInteger] { // 1 val dos = BigInteger.valueOf(2L) diff --git a/model/src/main/scala/org/zalando/nakadi/client/model/Model.scala b/model/src/main/scala/org/zalando/nakadi/client/model/Model.scala index f18c6c5..7217561 100644 --- a/model/src/main/scala/org/zalando/nakadi/client/model/Model.scala +++ b/model/src/main/scala/org/zalando/nakadi/client/model/Model.scala @@ -3,6 +3,7 @@ package org.zalando.nakadi.client.model import com.fasterxml.jackson.core.`type`.TypeReference import com.fasterxml.jackson.module.scala.JsonScalaEnumeration import com.fasterxml.jackson.annotation.JsonProperty +import com.fasterxml.jackson.databind.annotation.JsonDeserialize // Updated untill commit 7839be3 // Compare @@ -21,7 +22,7 @@ import com.fasterxml.jackson.annotation.JsonProperty * @param title */ trait Event { - def metadata(): EventMetadata + def metadata(): Option[EventMetadata] } /** @@ -49,7 +50,8 @@ case class EventMetadata( * A Business Event. Usually represents a status transition in a Business process. * */ -trait BusinessEvent extends Event +trait BusinessEvent extends Event { +} /** * Indicators of a `DataChangeEvent`'s referred data type and the type of operations done on them. @@ -72,7 +74,7 @@ case class DataChangeEvent[T]( data: T, dataType: String, @JsonScalaEnumeration(classOf[DataOperationType]) dataOperation: DataOperation.Value, - metadata: EventMetadata) extends DataChangeEventQualifier with Event + metadata: Option[EventMetadata]) extends DataChangeEventQualifier with Event /** * @ param problemType An absolute URI that identifies the problem type. When dereferenced, it SHOULD provide human-readable API documentation for the problem type (e.g., using HTML). This Problem object is the same as provided by https://github.com/zalando/problem @@ -88,7 +90,7 @@ case class Problem( detail: Option[String], instance: Option[String]) -case class Metrics(metrics: Map[String, Any]) //TODO: It is not defined yet! +case class Metrics(metrics: Map[String, Any]) /** * Partition information. Can be helpful when trying to start a stream using an unmanaged API. This information is not related to the state of the consumer clients. @@ -121,24 +123,24 @@ case class EventStreamBatch[T <: Event]( /** * An event type defines the schema and its runtime properties. * @param name Name of this EventType. Encodes the owner/responsible for this EventType. The name for the EventType SHOULD follow the pattern, but is not enforced 'stups_owning_application.event-type', for example 'gizig.price-change'. The components of the name are: * Organization: the organizational unit where the team is located; can be omitted. * Team name: name of the team responsible for owning application; can be omitted. * Owning application: SHOULD match the field owning_application; indicates * EventType name: name of this EventType; SHOULD end in ChangeEvent for DataChangeEvents; MUST be in the past tense for BusinessEvents. (TBD: how to deal with organizational changes? Should be reflected on the name of the EventType? Isn't it better to omit [organization:team] completely?) - * @param owner Indicator of the Application owning this `EventType`. + * @param owningApplication Indicator of the Application owning this `EventType`. * @param category Defines the category of this EventType. The value set will influence, if not set otherwise, the default set of validation-strategies, enrichment-strategies, and the effective_schema in the following way: - `undefined`: No predefined changes apply. `effective_schema` is exactly the same as the `EventTypeSchema`. Default validation_strategy for this `EventType` is `[{name: 'schema-validation'}]`. - `data`: Events of this category will be DataChangeEvents. `effective_schema` contains `metadata`, and adds fields `data_op` and `data_type`. The passed EventTypeSchema defines the schema of `data`. Default validation_strategy for this `EventType` is `[{name: 'datachange-schema-validation'}]`. - `business`: Events of this category will be BusinessEvents. `effective_schema` contains `metadata` and any additionally defined properties passed in the `EventTypeSchema` directly on top level of the Event. If name conflicts arise, creation of this EventType will be rejected. Default validation_strategy for this `EventType` is `[{name: 'schema-validation'}]`. - * @param effectiveSchema The effective schema of this `EventType`. The predefined schema validator will use the value of this field as base for the validation. The creation of EventTypes of different categories implies a distinct general structure for the payload of events of each type. This ultimate format the payload must adhere to is reflected in the `effective_schema`. See description of field `category` for more details of its generation. This field is informative and its contents cannot be manipulated directly. * @param validationStrategies Determines the validation that has to be executed upon reception of Events of this type. Events are rejected if any of the rules fail (see details of Problem response on the Event publishing methods). Rule evaluation order is the same as in this array. If not explicitly set, default value will respect the definition of the `EventType.category`. * @param enrichmentStrategies Determines the enrichment to be performed on an Event upon reception. Enrichment is performed once upon reception (and after validation) of an Event and is only possible on fields that are not defined on the incoming Event. See documentation for the write operation for details on behaviour in case of unsuccessful enrichment. - * @param partitionResolutionStrategy Determines how the assignment of the event to a Partition should be handled. + * @param partitionStrategy Determines how the assignment of the event to a Partition should be handled. * @param schema The schema for this EventType. This is expected to be a json schema in yaml format (other formats might be added in the future). * @param dataKeyFields Indicators of the path of the properties that constitute the primary key (identifier) of the data within this Event. If set MUST be a valid required field as defined in the schema. (TBD should be required? Is applicable only to both Business and DataChange Events?) * @param partitioningKeyFields Indicator of the field used for guaranteeing the ordering of Events of this type (used by the PartitionResolutionStrategy). If set MUST be a valid required field as defined in the schema. + * @param statistics Statistics of this EventType used for optimization purposes. Internal use of these values might change over time. (TBD: measured statistics). * */ case class EventType( name: String, owningApplication: String, @JsonScalaEnumeration(classOf[EventTypeCategoryType]) category: EventTypeCategory.Value, - validationStrategies: Option[Seq[String]], - enrichmentStrategies: Option[Seq[String]], - partitionStrategy: PartitionResolutionStrategy, //Different naming + @JsonScalaEnumeration(classOf[EventValidationStrategyType]) validationStrategies: Option[Seq[EventValidationStrategy.Value]], + @JsonScalaEnumeration(classOf[EventEnrichmentStrategyType]) enrichmentStrategies: Seq[EventEnrichmentStrategy.Value], + @JsonScalaEnumeration(classOf[PartitionStrategyType]) partitionStrategy: Option[PartitionStrategy.Value], schema: Option[EventTypeSchema], dataKeyFields: Option[Seq[String]], partitionKeyFields: Option[Seq[String]], @@ -175,27 +177,36 @@ case class EventTypeStatistics( * @param name Name of the strategy. * @param doc Documentation for the validation. */ -case class EventValidationStrategy( - name: String, - doc: Option[String]) +case object EventValidationStrategy extends Enumeration { + type EventValidationStrategyxtends = Value + val NONE = Value("None") +} +class EventValidationStrategyType extends TypeReference[EventValidationStrategy.type] /** * Defines a rule for the resolution of incoming Events into partitions. Rules might require additional parameters; see the `doc` field of the existing rules for details. See `GET /registry/partition-strategies` for a list of available rules. * @param name Name of the strategy. * @param doc Documentation for the partition resolution. */ -case class PartitionResolutionStrategy( - name: String, - doc: Option[String]) +case object PartitionStrategy extends Enumeration { + type PartitionResolutionStrategy = Value + val HASH = Value("hash") + val USER_DEFINED = Value("user_defined") + val RANDOM = Value("random") +} +class PartitionStrategyType extends TypeReference[PartitionStrategy.type] /** * Defines a rule for transformation of an incoming Event. No existing fields might be modified. In practice this is used to set automatically values in the Event upon reception (i.e. set a reception timestamp on the Event). Rules might require additional parameters; see the `doc` field of the existing rules for details. See GET /registry/enrichment-strategies for a list of available rules. * @param name Name of the strategy. * @param doc Documentation for the enrichment. */ -case class EventEnrichmentStrategy( - name: String, - doc: Option[String]) +case object EventEnrichmentStrategy extends Enumeration { + type EventEnrichmentStrategy = Value + val METADATA = Value("metadata_enrichment") +} +class EventEnrichmentStrategyType extends TypeReference[EventEnrichmentStrategy.type] + /** * A status corresponding to one individual Event's publishing attempt. * @param eid Eid of the corresponding item. Will be absent if missing on the incoming Event. @@ -214,8 +225,6 @@ case class BatchItemResponse( // ENUMS //////////////////////// ///////////////////////////////// -trait NakadiEnum - /** * Identifier for the type of operation to executed on the entity.
* C: Creation
@@ -225,7 +234,7 @@ trait NakadiEnum * Values = CREATE("C"), UPDATE("U"), DELETE("D"), SNAPSHOT("S") */ -case object DataOperation extends Enumeration with NakadiEnum { +case object DataOperation extends Enumeration { type DataOperation = Value val CREATE = Value("C") val UPDATE = Value("U") From 696f8a13b895c092dba8c9723ec377f8723a5531 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Sat, 16 Apr 2016 20:10:43 +0200 Subject: [PATCH 018/183] techjira:LAAS-60 Initial logic for stream event from http connection --- .../org/zalando/nakadi/client/Client.scala | 14 ++-- .../zalando/nakadi/client/ClientImpl.scala | 23 +----- .../zalando/nakadi/client/Connection.scala | 45 ++++++----- .../zalando/nakadi/client/HttpFactory.scala | 72 +++++++++++++++++ .../client/model/JacksonJsonMarshaller.scala | 2 +- .../zalando/nakadi/client/TestFactory.scala | 12 ++- .../nakadi/client/util/TestScalaEntity.scala | 2 +- .../client/EventTypesIntegrationTest.scala | 34 ++++---- .../client/example/EventPublisher.scala | 1 + .../client/example2/ClientExample.scala | 52 +++++++++++++ .../nakadi/client/example2/ClientMsg.scala | 33 ++++++++ .../client/example2/HttpClientExample.scala | 77 +++++++++++++++++++ .../client/example2/ServerExample.scala | 53 +++++++++++++ .../zalando/nakadi/client/model/Model.scala | 4 +- 14 files changed, 354 insertions(+), 70 deletions(-) create mode 100644 client/src/main/scala/org/zalando/nakadi/client/HttpFactory.scala create mode 100644 it/src/test/scala/org/zalando/nakadi/client/example2/ClientExample.scala create mode 100644 it/src/test/scala/org/zalando/nakadi/client/example2/ClientMsg.scala create mode 100644 it/src/test/scala/org/zalando/nakadi/client/example2/HttpClientExample.scala create mode 100644 it/src/test/scala/org/zalando/nakadi/client/example2/ServerExample.scala diff --git a/client/src/main/scala/org/zalando/nakadi/client/Client.scala b/client/src/main/scala/org/zalando/nakadi/client/Client.scala index 6bfd0c3..cfec0c7 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/Client.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/Client.scala @@ -6,13 +6,13 @@ import akka.actor.Terminated import akka.http.scaladsl.model.HttpResponse import org.zalando.nakadi.client.model.Metrics -case class StreamParameters(cursor: Option[String], // - batchLimit: Option[Integer], - streamLimit: Option[Integer], - batchFlushTimeout: Option[Integer], - streamTimeout: Option[Integer], - streamKeepAliveLimit: Option[Integer], - flowId: Option[String]) +case class StreamParameters(cursor: Option[String]=None, // + batchLimit: Option[Integer]=None, + streamLimit: Option[Integer]=None, + batchFlushTimeout: Option[Integer]=None, + streamTimeout: Option[Integer]=None, + streamKeepAliveLimit: Option[Integer]=None, + flowId: Option[String]=None) trait Client { import Client._ diff --git a/client/src/main/scala/org/zalando/nakadi/client/ClientImpl.scala b/client/src/main/scala/org/zalando/nakadi/client/ClientImpl.scala index 565a116..b1e2250 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/ClientImpl.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/ClientImpl.scala @@ -17,7 +17,7 @@ import akka.http.scaladsl.model.StatusCodes import akka.http.scaladsl.unmarshalling.Unmarshal import akka.http.scaladsl.model.headers.RawHeader -private[client] class ClientImpl(connection: Connection, charSet: String = "UTF-8") extends Client { +private[client] class ClientImpl(connection: Connection, charSet: String = "UTF-8") extends Client with HttpFactory{ import Client._ implicit val materializer = connection.materializer @@ -52,7 +52,7 @@ private[client] class ClientImpl(connection: Connection, charSet: String = "UTF- } def events[T](name: String, params: Option[StreamParameters])(implicit ser: NakadiDeserializer[T]): Future[Either[ClientError, Option[T]]] = { - val headers = createHeaders(params) + val headers = withHeaders(params) logFutureEither(connection.get(URI_EVENTS_OF_EVENT_TYPE.format(name)).flatMap(in => mapToEither(in))) } @@ -144,24 +144,5 @@ private[client] class ClientImpl(connection: Connection, charSet: String = "UTF- } } - private[client] def createHeaders(params: Option[StreamParameters]): List[HttpHeader] = { - params match { - case Some(StreamParameters(cursor, _, _, _, _, _, flowId)) => - val parameters = List(("X-nakadi-cursors", cursor), ("X-Flow-Id", flowId)) - for { (key, optional) <- parameters; value <- optional } yield RawHeader(key, value) - case None => Nil - } - } - private[client] def createQueryParams(params: Option[StreamParameters]): List[(String, Any)] = { - params match { - case Some(StreamParameters(_, batchLimit, streamLimit, batchFlushTimeout, streamTimeout, streamKeepAliveLimit, _)) => - val parameters = List(("batch_limit", batchLimit), ("stream_limit", streamLimit), // - ("batch_flush_timeout", batchFlushTimeout), ("stream_timeout", streamTimeout), // - ("stream_keep_alive_limit", streamKeepAliveLimit)) - for { (key, optional) <- parameters; value <- optional } yield (key -> value) - case None => Nil - } - } - } diff --git a/client/src/main/scala/org/zalando/nakadi/client/Connection.scala b/client/src/main/scala/org/zalando/nakadi/client/Connection.scala index c1f9415..303f162 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/Connection.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/Connection.scala @@ -32,13 +32,23 @@ import javax.net.ssl.X509TrustManager import akka.http.scaladsl.model.HttpHeader import scala.collection.immutable.Seq -trait Connection { +trait Connection extends HttpFactory{ + + + //Connection details + def host:String + def port:Int + def tokenProvider():TokenProvider + def connection():Flow[HttpRequest, HttpResponse, Future[Http.OutgoingConnection]] + def get(endpoint: String): Future[HttpResponse] def get(endpoint: String, headers: Seq[HttpHeader]): Future[HttpResponse] def delete(endpoint: String): Future[HttpResponse] def post[T](endpoint: String, model: T)(implicit serializer: NakadiSerializer[T]): Future[HttpResponse] def put[T](endpoint: String, model: T)(implicit serializer: NakadiSerializer[T]): Future[HttpResponse] + + def stop(): Future[Terminated] def materializer(): ActorMaterializer } @@ -47,7 +57,7 @@ trait Connection { * Companion object for factory methods. */ object Connection { - + /** * */ @@ -76,53 +86,54 @@ object Connection { * Class for handling the configuration and basic http calls. */ -private[client] class ConnectionImpl(host: String, port: Int, tokenProvider: () => String, securedConnection: Boolean, verifySSlCertificate: Boolean) extends Connection { +private[client] class ConnectionImpl(val host: String, val port: Int, val tokenProvider: () => String, securedConnection: Boolean, verifySSlCertificate: Boolean) extends Connection { import Connection._ private implicit val actorSystem = ActorSystem("Nakadi-Client-Connections") private implicit val http = Http(actorSystem) implicit val materializer = ActorMaterializer() private val timeout = 5.seconds + val logger = Logger(LoggerFactory.getLogger(this.getClass)) - private val connectionFlow: Flow[HttpRequest, HttpResponse, Future[Http.OutgoingConnection]] = newSslContext(securedConnection, verifySSlCertificate) match { + val connection: Flow[HttpRequest, HttpResponse, Future[Http.OutgoingConnection]] = newSslContext(securedConnection, verifySSlCertificate) match { case Some(result) => http.outgoingConnectionHttps(host, port, result) case None => - logger.warn("Disabled HTTPS, switching to HTTP only.") + logger.warn("Disabled HTTPS, switching to HTTP only!") http.outgoingConnection(host, port) } def get(endpoint: String): Future[HttpResponse] = { logger.info("Get: {}", endpoint) - executeCall(httpRequest(endpoint, HttpMethods.GET,Nil)) + executeCall(withHttpRequest(endpoint, HttpMethods.GET,Nil,tokenProvider)) } def get(endpoint: String, headers: Seq[HttpHeader]): Future[HttpResponse] = { logger.info("Get: {}", endpoint) - executeCall(httpRequest(endpoint, HttpMethods.GET,headers)) + executeCall(withHttpRequest(endpoint, HttpMethods.GET,headers,tokenProvider)) } def put[T](endpoint: String, model: T)(implicit serializer: NakadiSerializer[T]): Future[HttpResponse] = { logger.info("Get: {}", endpoint) - executeCall(httpRequest(endpoint, HttpMethods.GET,Nil)) + executeCall(withHttpRequest(endpoint, HttpMethods.GET,Nil,tokenProvider)) } def post[T](endpoint: String, model: T)(implicit serializer: NakadiSerializer[T]): Future[HttpResponse] = { val entity = serializer.toJson(model) logger.info("Posting to endpoint {}", endpoint) logger.debug("Data to post {}", entity) - executeCall(httpRequestWithPayload(endpoint, entity, HttpMethods.POST)) + executeCall(withHttpRequestAndPayload(endpoint, entity, HttpMethods.POST,tokenProvider)) } def delete(endpoint: String): Future[HttpResponse] = { logger.info("Delete: {}", endpoint) - executeCall(httpRequest(endpoint, HttpMethods.DELETE,Nil)) + executeCall(withHttpRequest(endpoint, HttpMethods.DELETE,Nil,tokenProvider)) } private def executeCall(request: HttpRequest): Future[HttpResponse] = { val response: Future[HttpResponse] = Source.single(request) - .via(connectionFlow). + .via(connection). runWith(Sink.head) logError(response) response @@ -134,18 +145,6 @@ private[client] class ConnectionImpl(host: String, port: Int, tokenProvider: () } } - private def httpRequest(url: String, httpMethod: HttpMethod, additionalHeaders: Seq[HttpHeader]): HttpRequest = { - val allHeaders:Seq[HttpHeader] = additionalHeaders :+ headers.Accept(MediaRange(`application/json`)) :+ headers.Authorization(OAuth2BearerToken(tokenProvider())) - HttpRequest(uri = url, method = httpMethod).withHeaders(allHeaders) - } - - private def httpRequestWithPayload(url: String, entity: String, httpMethod: HttpMethod): HttpRequest = { - HttpRequest(uri = url, method = httpMethod) // - .withHeaders(headers.Authorization(OAuth2BearerToken(tokenProvider())), - headers.Accept(MediaRange(`application/json`))) - .withEntity(ContentType(`application/json`), entity) - } - def stop(): Future[Terminated] = actorSystem.terminate() } diff --git a/client/src/main/scala/org/zalando/nakadi/client/HttpFactory.scala b/client/src/main/scala/org/zalando/nakadi/client/HttpFactory.scala new file mode 100644 index 0000000..e88b612 --- /dev/null +++ b/client/src/main/scala/org/zalando/nakadi/client/HttpFactory.scala @@ -0,0 +1,72 @@ +package org.zalando.nakadi.client + +import akka.http.scaladsl.model.HttpHeader +import akka.http.scaladsl.model.headers.RawHeader + +import java.security.SecureRandom +import java.security.cert.X509Certificate +import scala.concurrent.ExecutionContext.Implicits.global +import scala.concurrent.Future +import scala.concurrent.duration.DurationInt +import org.slf4j.LoggerFactory +import com.typesafe.scalalogging.Logger +import akka.actor.ActorSystem +import akka.actor.Terminated +import akka.http.scaladsl.Http +import akka.http.scaladsl.HttpsConnectionContext +import akka.http.scaladsl.model.ContentType +import akka.http.scaladsl.model.HttpMethod +import akka.http.scaladsl.model.HttpMethods +import akka.http.scaladsl.model.HttpMethods.POST +import akka.http.scaladsl.model.HttpRequest +import akka.http.scaladsl.model.HttpResponse +import akka.http.scaladsl.model.MediaRange +import akka.http.scaladsl.model.MediaTypes.`application/json` +import akka.http.scaladsl.model.Uri.apply +import akka.http.scaladsl.model.headers +import akka.http.scaladsl.model.headers.OAuth2BearerToken +import akka.stream.ActorMaterializer +import akka.stream.scaladsl.Flow +import akka.stream.scaladsl.Sink +import akka.stream.scaladsl.Source +import javax.net.ssl.SSLContext +import javax.net.ssl.TrustManager +import javax.net.ssl.X509TrustManager +import akka.http.scaladsl.model.HttpHeader +import scala.collection.immutable.Seq + +trait HttpFactory { + type TokenProvider = () => String + def withHeaders(params: Option[StreamParameters]): List[HttpHeader] = { + params match { + case Some(StreamParameters(cursor, _, _, _, _, _, flowId)) => + val parameters = List(("X-nakadi-cursors", cursor), ("X-Flow-Id", flowId)) + for { (key, optional) <- parameters; value <- optional } yield RawHeader(key, value) + case None => Nil + } + } + def withQueryParams(params: Option[StreamParameters]): List[(String, Any)] = { + params match { + case Some(StreamParameters(_, batchLimit, streamLimit, batchFlushTimeout, streamTimeout, streamKeepAliveLimit, _)) => + val parameters = List(("batch_limit", batchLimit), ("stream_limit", streamLimit), // + ("batch_flush_timeout", batchFlushTimeout), ("stream_timeout", streamTimeout), // + ("stream_keep_alive_limit", streamKeepAliveLimit)) + for { (key, optional) <- parameters; value <- optional } yield (key -> value) + case None => Nil + } + } + + def withHttpRequest(url: String, httpMethod: HttpMethod, additionalHeaders: Seq[HttpHeader], tokenProvider: TokenProvider): HttpRequest = { + val allHeaders: Seq[HttpHeader] = additionalHeaders :+ headers.Accept(MediaRange(`application/json`)) :+ headers.Authorization(OAuth2BearerToken(tokenProvider())) + HttpRequest(uri = url, method = httpMethod).withHeaders(allHeaders) + } + + def withHttpRequestAndPayload(url: String, entity: String, httpMethod: HttpMethod, tokenProvider: TokenProvider): HttpRequest = { + HttpRequest(uri = url, method = httpMethod) // + .withHeaders(headers.Authorization(OAuth2BearerToken(tokenProvider())), + headers.Accept(MediaRange(`application/json`))) + .withEntity(ContentType(`application/json`), entity) + } + +} + diff --git a/client/src/main/scala/org/zalando/nakadi/client/model/JacksonJsonMarshaller.scala b/client/src/main/scala/org/zalando/nakadi/client/model/JacksonJsonMarshaller.scala index f94d0fc..e3f3967 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/model/JacksonJsonMarshaller.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/model/JacksonJsonMarshaller.scala @@ -67,7 +67,7 @@ trait JacksonJsonMarshaller { } } - implicit def serializer[T](implicit expectedType: TypeReference[T]): NakadiSerializer[T] = new NakadiSerializer[T] { + implicit def serializer[T]: NakadiSerializer[T] = new NakadiSerializer[T] { def toJson(from: T): String = defaultObjectMapper.writeValueAsString(from) } diff --git a/client/src/test/scala/org/zalando/nakadi/client/TestFactory.scala b/client/src/test/scala/org/zalando/nakadi/client/TestFactory.scala index 7f72d81..fd5ffc7 100644 --- a/client/src/test/scala/org/zalando/nakadi/client/TestFactory.scala +++ b/client/src/test/scala/org/zalando/nakadi/client/TestFactory.scala @@ -17,13 +17,21 @@ import org.zalando.nakadi.client.model.PartitionStrategy import org.zalando.nakadi.client.model.SchemaType trait ClientFactory { - val host = "" + val host = "nakadi-sandbox." val OAuth2Token = () => "" val port = 443 - val client = new ClientImpl(Connection.newConnection(host, port, OAuth2Token, true, false), "UTF-8") + val connection = Connection.newConnection(host, port, OAuth2Token, true, false) + val client = new ClientImpl(connection, "UTF-8") } +case class EventActions(client: Client) extends JacksonJsonMarshaller { + + def create[T](name: String, event: Seq[T])(implicit ser: NakadiSerializer[Seq[T]]) = { + client.newEvents[T](name, event) + } +} + case class EventTypesActions(client: Client) extends JacksonJsonMarshaller { def create(event: EventType)(implicit ser: NakadiSerializer[EventType]) = { diff --git a/client/src/test/scala/org/zalando/nakadi/client/util/TestScalaEntity.scala b/client/src/test/scala/org/zalando/nakadi/client/util/TestScalaEntity.scala index 43d4e47..2dae52c 100644 --- a/client/src/test/scala/org/zalando/nakadi/client/util/TestScalaEntity.scala +++ b/client/src/test/scala/org/zalando/nakadi/client/util/TestScalaEntity.scala @@ -23,7 +23,7 @@ object TestScalaEntity { val problem = new Problem("problemType", "title", 312, Option("detail"), Option("instance")) val metrics = new Metrics(Map("metrics"->"test")) val partition = new Partition("partition", "oldestAvailableOffset", "newestAvailableOffset") - val cursor = new Cursor("partition", "offset") + val cursor = new Cursor(0, 0) val eventTypeSchema = new EventTypeSchema(SchemaType.JSON, "schema") val eventValidationStrategy = EventValidationStrategy.NONE val partitionResolutionStrategy = PartitionStrategy.HASH diff --git a/it/src/test/scala/org/zalando/nakadi/client/EventTypesIntegrationTest.scala b/it/src/test/scala/org/zalando/nakadi/client/EventTypesIntegrationTest.scala index 8f42864..e5c7ecb 100644 --- a/it/src/test/scala/org/zalando/nakadi/client/EventTypesIntegrationTest.scala +++ b/it/src/test/scala/org/zalando/nakadi/client/EventTypesIntegrationTest.scala @@ -2,20 +2,28 @@ package org.zalando.nakadi.client import org.scalatest.{ Matchers, WordSpec } import org.zalando.nakadi.client.model._ +import com.fasterxml.jackson.core.`type`.TypeReference class EventTypeTest extends WordSpec with Matchers with JacksonJsonMarshaller with ModelFactory with ClientFactory { - val events = new EventTypesActions(client) + val eventAction = new EventActions(client) + val eventTypeAction = new EventTypesActions(client) "POST/PUT/GET/DELETE single EventType " in { //Create event val eventType = createUniqueEventType() - val creationResult = events.create(eventType) + val creationResult = eventTypeAction.create(eventType) creationResult.isDefined shouldBe false - + //Check the created EventType checkEventTypeExists(eventType) + case class MyEventExample(orderNumber:String) + implicit def problemTR: TypeReference[MyEventExample] = new TypeReference[MyEventExample] {} + + eventAction.create("test-client-integration-event-1936085527-148383828851369665",List(MyEventExample("1872364"))) + + //TODO: Enable this when PUT is supported. // Update the event // val updatedEvent = eventType.copy(owningApplication = "laas-team-2") @@ -26,7 +34,7 @@ class EventTypeTest extends WordSpec with Matchers with JacksonJsonMarshaller wi // checkEventTypeDoesNotExist(eventType) //Delete the created Event - val deletedEvent = events.delete(eventType.name) + val deletedEvent = eventTypeAction.delete(eventType.name) deletedEvent.isEmpty shouldBe true //Is it really deleted? @@ -39,10 +47,10 @@ class EventTypeTest extends WordSpec with Matchers with JacksonJsonMarshaller wi val eventType1 = createUniqueEventType() val eventType2 = createUniqueEventType() - events.create(eventType1) + eventTypeAction.create(eventType1) checkEventTypeExists(eventType1) - events.create(eventType2) + eventTypeAction.create(eventType2) checkEventTypeExists(eventType2) //Get all EventTypes again @@ -52,15 +60,15 @@ class EventTypeTest extends WordSpec with Matchers with JacksonJsonMarshaller wi // allEvents should contain(eventType2) //Delete the 2 EventTypes - events.delete(eventType1.name) - events.delete(eventType2.name) + eventTypeAction.delete(eventType1.name) + eventTypeAction.delete(eventType2.name) //Check if the're really deleted checkEventTypeDoesNotExist(eventType1) checkEventTypeDoesNotExist(eventType2) //Get all should not contain the deleted events - val Right(Some(updatedEvents)) = events.getAll() + val Right(Some(updatedEvents)) = eventTypeAction.getAll() updatedEvents shouldNot contain(eventType1) updatedEvents shouldNot contain(eventType2) @@ -72,12 +80,12 @@ class EventTypeTest extends WordSpec with Matchers with JacksonJsonMarshaller wi //Create 2 EventTypes val eventType = createUniqueEventType() - events.create(eventType) + eventTypeAction.create(eventType) checkEventTypeExists(eventType) //Update the event val updatedEvent = eventType.copy(owningApplication = "laas-team-2") - events.update(updatedEvent) + eventTypeAction.update(updatedEvent) //Check the EventType has bee updated // checkEventTypeExists(updatedEvent) @@ -86,7 +94,7 @@ class EventTypeTest extends WordSpec with Matchers with JacksonJsonMarshaller wi } def checkEventTypeDoesNotExist(eventType: EventType) = { - val requestedEvent = events.get(eventType.name) + val requestedEvent = eventTypeAction.get(eventType.name) println(requestedEvent) requestedEvent.isRight shouldBe true val Right(result) = requestedEvent @@ -94,7 +102,7 @@ class EventTypeTest extends WordSpec with Matchers with JacksonJsonMarshaller wi } def checkEventTypeExists(eventType: EventType) = { - val Right(Some(createdEvent)) = events.get(eventType.name) + val Right(Some(createdEvent)) = eventTypeAction.get(eventType.name) createdEvent shouldBe eventType } diff --git a/it/src/test/scala/org/zalando/nakadi/client/example/EventPublisher.scala b/it/src/test/scala/org/zalando/nakadi/client/example/EventPublisher.scala index 247bce4..9c3eda8 100644 --- a/it/src/test/scala/org/zalando/nakadi/client/example/EventPublisher.scala +++ b/it/src/test/scala/org/zalando/nakadi/client/example/EventPublisher.scala @@ -5,6 +5,7 @@ import akka.actor.ActorLogging import akka.stream.actor.ActorPublisher import akka.stream.actor.ActorPublisherMessage.{ Cancel, Request } + class EventPublisher extends ActorPublisher[BigInteger] with ActorLogging { var prev = BigInteger.ZERO diff --git a/it/src/test/scala/org/zalando/nakadi/client/example2/ClientExample.scala b/it/src/test/scala/org/zalando/nakadi/client/example2/ClientExample.scala new file mode 100644 index 0000000..d50a2b8 --- /dev/null +++ b/it/src/test/scala/org/zalando/nakadi/client/example2/ClientExample.scala @@ -0,0 +1,52 @@ +package org.zalando.nakadi.client.example2 + +import scala.concurrent.ExecutionContext.Implicits.global +import akka.stream.scaladsl._ +import akka.stream.scaladsl.Keep._ +import scala.concurrent.Future +import akka.actor.ActorSystem +import akka.stream.ActorMaterializer +import akka.http.scaladsl.Http +import org.zalando.nakadi.client.Connection +import java.net.InetSocketAddress +import scala.concurrent.Future +import akka.stream.scaladsl.Tcp +import akka.actor.ActorSystem +import akka.stream.scaladsl.Tcp.ServerBinding +import akka.stream.scaladsl.Tcp.IncomingConnection +import akka.util.ByteString + +object Client extends App { + private implicit val actorSystem = ActorSystem("Nakadi-Client-Connections") + private implicit val http = Http(actorSystem) + implicit val materializer = ActorMaterializer() + val server = new Server() + val client = new Client() + client.connect() + +} + +class Client(implicit system: ActorSystem, m: ActorMaterializer) { + val connection = Tcp().outgoingConnection("127.0.0.1", 8888) + + def connect() = { + + val replParser = + Flow[String].takeWhile(_ != "q") + .concat(Source.single("BYE")) + .map(elem => ByteString(s"$elem\n")) + + val repl = Flow[ByteString] + .via(Framing.delimiter( + ByteString("\n"), + maximumFrameLength = 256, + allowTruncation = true)) + .map(_.utf8String) + .map(text => println("Server: " + text)) + .map(_ => scala.io.StdIn.readLine("> ")) + .via(replParser) + println("Connecting client") + connection.join(repl).run() + } +} + diff --git a/it/src/test/scala/org/zalando/nakadi/client/example2/ClientMsg.scala b/it/src/test/scala/org/zalando/nakadi/client/example2/ClientMsg.scala new file mode 100644 index 0000000..e122ef9 --- /dev/null +++ b/it/src/test/scala/org/zalando/nakadi/client/example2/ClientMsg.scala @@ -0,0 +1,33 @@ +package org.zalando.nakadi.client.example2 + +import org.zalando.nakadi.client.Connection +import java.net.InetSocketAddress +import scala.concurrent.Future +import akka.stream.scaladsl.Tcp +import akka.actor.ActorSystem +import akka.http.scaladsl.Http.ServerBinding + +case class SubscribeEvent(eventType: String) + +class EventReceiver(conn: Connection)(implicit val system: ActorSystem) { + + def createSource() = { + val serverConnection = Tcp().outgoingConnection(conn.host, conn.port) + + } + +} + +class EventServer(conn: Connection)(implicit val system: ActorSystem) { +// +// def listen() = { +// val binding: Future[ServerBinding] = +// Tcp().bind("127.0.0.1", 8888).to(Sink.ignore).run() +// +// binding.map { b => +// b.unbind() onComplete { +// case _ => // ... +// } +// } +// } +} \ No newline at end of file diff --git a/it/src/test/scala/org/zalando/nakadi/client/example2/HttpClientExample.scala b/it/src/test/scala/org/zalando/nakadi/client/example2/HttpClientExample.scala new file mode 100644 index 0000000..928a2de --- /dev/null +++ b/it/src/test/scala/org/zalando/nakadi/client/example2/HttpClientExample.scala @@ -0,0 +1,77 @@ +package org.zalando.nakadi.client.example2 + +import scala.Left +import scala.Right +import scala.concurrent.ExecutionContext.Implicits.global +import scala.concurrent.ExecutionContext.Implicits.global +import scala.concurrent.Future +import scala.concurrent.Future +import scala.concurrent.Future +import scala.concurrent.duration.DurationInt +import scala.util.Random + +import org.zalando.nakadi.client.ClientFactory +import org.zalando.nakadi.client.Connection +import org.zalando.nakadi.client.HttpFactory +import org.zalando.nakadi.client.StreamParameters +import org.zalando.nakadi.client.model._ +import org.zalando.nakadi.client.model.Cursor + +import akka.actor.ActorSystem +import akka.actor.ActorSystem +import akka.http.scaladsl.Http +import akka.http.scaladsl.model.HttpMethods +import akka.http.scaladsl.model.HttpResponse +import akka.http.scaladsl.model.HttpResponse +import akka.stream.ActorMaterializer +import akka.stream.scaladsl._ +import akka.stream.scaladsl.Keep._ +import akka.util.ByteString + +object HttpClient extends App { + private implicit val actorSystem = ActorSystem("Nakadi-Client-Connections") + private implicit val http = Http(actorSystem) + implicit val materializer = ActorMaterializer() + val client = new HttpClient() + client.start() +} + +class HttpClient(implicit system: ActorSystem, m: ActorMaterializer) extends ClientFactory with HttpFactory { + + def generateWord(): Seq[String] = for { a <- 0 to 200 } yield Random.alphanumeric.take(10).mkString + println(generateWord()) + val source = Source.single(generateWord()) + + source.runForeach { println } + + def start() = { + val conn = connection.connection() + val url = "/event-types/test-client-integration-event-1936085527-148383828851369665/events" + val cursor = Cursor(0, 0) + val params = Some(StreamParameters()) + val headers = withHeaders(params) + val request = withHttpRequest(url, HttpMethods.GET, headers, OAuth2Token) + val result = Source.single(request).via(conn).runForeach { + _ match { + case HttpResponse(status, headers, entity, protocol) if (status.isSuccess()) => + println(" >>>>>>>>>>>>>>>>>>>>> " + entity.dataBytes) + case HttpResponse(status, headers, entity, protocol) if (status.isSuccess()) => + println(" >>>>>>>>>>>>>>>>>>>>> Error") + } + } + + val flow = Flow[ByteString] + .via(akka.stream.scaladsl.Framing.delimiter( + ByteString("\n"), + maximumFrameLength = 256, + allowTruncation = true)) + .map(_.utf8String) + .map(_ + "!!!\n") + .map { x => + println(s"$x") + ByteString(x) + } + + } + +} \ No newline at end of file diff --git a/it/src/test/scala/org/zalando/nakadi/client/example2/ServerExample.scala b/it/src/test/scala/org/zalando/nakadi/client/example2/ServerExample.scala new file mode 100644 index 0000000..7546ecb --- /dev/null +++ b/it/src/test/scala/org/zalando/nakadi/client/example2/ServerExample.scala @@ -0,0 +1,53 @@ +package org.zalando.nakadi.client.example2 + +import scala.concurrent.ExecutionContext.Implicits.global +import akka.stream.scaladsl._ +import akka.stream.scaladsl.Keep._ +import scala.concurrent.Future +import akka.actor.ActorSystem +import akka.stream.ActorMaterializer +import akka.http.scaladsl.Http +import org.zalando.nakadi.client.Connection +import java.net.InetSocketAddress +import scala.concurrent.Future +import akka.stream.scaladsl.Tcp +import akka.actor.ActorSystem +import akka.stream.scaladsl.Tcp.ServerBinding +import akka.stream.scaladsl.Tcp.IncomingConnection +import akka.util.ByteString + +object Server extends App { + private implicit val actorSystem = ActorSystem("Nakadi-Client-Connections") + private implicit val http = Http(actorSystem) + implicit val materializer = ActorMaterializer() + val server = new Server() + val client = new Client() + server.start() + +} + +class Server(implicit actorSystem: ActorSystem, m: ActorMaterializer) { + + def start() = { + val connections: Source[IncomingConnection, Future[ServerBinding]] = Tcp().bind("127.0.0.1", 8888) + + connections runForeach { connection => + println(s"New connection from: ${connection.remoteAddress}") + + val echo = Flow[ByteString] + .via(akka.stream.scaladsl.Framing.delimiter( + ByteString("\n"), + maximumFrameLength = 256, + allowTruncation = true)) + .map(_.utf8String) + .map(_ + "!!!\n") + .map { x => + println(s"$x") + ByteString(x) + } + + connection.handleWith(echo) + } + } + +} \ No newline at end of file diff --git a/model/src/main/scala/org/zalando/nakadi/client/model/Model.scala b/model/src/main/scala/org/zalando/nakadi/client/model/Model.scala index 7217561..e9256bb 100644 --- a/model/src/main/scala/org/zalando/nakadi/client/model/Model.scala +++ b/model/src/main/scala/org/zalando/nakadi/client/model/Model.scala @@ -107,8 +107,8 @@ case class Partition( * @param offset Offset of the event being pointed to. */ case class Cursor( - partition: String, - offset: String) + partition: Integer, + offset: Integer) /** * One chunk of events in a stream. A batch consists of an array of `Event`s plus a `Cursor` pointing to the offset of the last Event in the stream. The size of the array of Event is limited by the parameters used to initialize a Stream. If acting as a keep alive message (see `GET /event-type/{name}/events`) the events array will be omitted. Sequential batches might repeat the cursor if no new events arrive. From 94ed9eee612c4718e74dd7239d3a4436f6756e83 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Sun, 17 Apr 2016 00:16:14 +0200 Subject: [PATCH 019/183] techjira:LAAS-60 Finally!! Managed to keeep the connection open and get the events sequentially!! --- .../client/EventTypesIntegrationTest.scala | 3 ++ .../client/example2/HttpClientExample.scala | 54 +++++++++---------- 2 files changed, 28 insertions(+), 29 deletions(-) diff --git a/it/src/test/scala/org/zalando/nakadi/client/EventTypesIntegrationTest.scala b/it/src/test/scala/org/zalando/nakadi/client/EventTypesIntegrationTest.scala index e5c7ecb..52f313d 100644 --- a/it/src/test/scala/org/zalando/nakadi/client/EventTypesIntegrationTest.scala +++ b/it/src/test/scala/org/zalando/nakadi/client/EventTypesIntegrationTest.scala @@ -21,6 +21,9 @@ class EventTypeTest extends WordSpec with Matchers with JacksonJsonMarshaller wi case class MyEventExample(orderNumber:String) implicit def problemTR: TypeReference[MyEventExample] = new TypeReference[MyEventExample] {} + eventAction.create("test-client-integration-event-1936085527-148383828851369665",List(MyEventExample("1872361"))) + eventAction.create("test-client-integration-event-1936085527-148383828851369665",List(MyEventExample("1872362"))) + eventAction.create("test-client-integration-event-1936085527-148383828851369665",List(MyEventExample("1872363"))) eventAction.create("test-client-integration-event-1936085527-148383828851369665",List(MyEventExample("1872364"))) diff --git a/it/src/test/scala/org/zalando/nakadi/client/example2/HttpClientExample.scala b/it/src/test/scala/org/zalando/nakadi/client/example2/HttpClientExample.scala index 928a2de..5dfbe3c 100644 --- a/it/src/test/scala/org/zalando/nakadi/client/example2/HttpClientExample.scala +++ b/it/src/test/scala/org/zalando/nakadi/client/example2/HttpClientExample.scala @@ -1,31 +1,17 @@ package org.zalando.nakadi.client.example2 -import scala.Left -import scala.Right -import scala.concurrent.ExecutionContext.Implicits.global -import scala.concurrent.ExecutionContext.Implicits.global -import scala.concurrent.Future -import scala.concurrent.Future import scala.concurrent.Future -import scala.concurrent.duration.DurationInt import scala.util.Random -import org.zalando.nakadi.client.ClientFactory -import org.zalando.nakadi.client.Connection -import org.zalando.nakadi.client.HttpFactory -import org.zalando.nakadi.client.StreamParameters -import org.zalando.nakadi.client.model._ +import org.zalando.nakadi.client.{ ClientFactory, HttpFactory, StreamParameters } import org.zalando.nakadi.client.model.Cursor - -import akka.actor.ActorSystem +import scala.concurrent.ExecutionContext.Implicits.global import akka.actor.ActorSystem import akka.http.scaladsl.Http -import akka.http.scaladsl.model.HttpMethods -import akka.http.scaladsl.model.HttpResponse -import akka.http.scaladsl.model.HttpResponse +import akka.http.scaladsl.model.{ HttpMethods, HttpRequest, HttpResponse } +import akka.http.scaladsl.model.headers.RawHeader import akka.stream.ActorMaterializer -import akka.stream.scaladsl._ -import akka.stream.scaladsl.Keep._ +import akka.stream.scaladsl.{ Flow, Source } import akka.util.ByteString object HttpClient extends App { @@ -42,23 +28,33 @@ class HttpClient(implicit system: ActorSystem, m: ActorMaterializer) extends Cli println(generateWord()) val source = Source.single(generateWord()) - source.runForeach { println } + // source.runForeach { println } def start() = { - val conn = connection.connection() + val conn: Flow[HttpRequest, HttpResponse, Future[Http.OutgoingConnection]] = connection.connection() val url = "/event-types/test-client-integration-event-1936085527-148383828851369665/events" val cursor = Cursor(0, 0) val params = Some(StreamParameters()) - val headers = withHeaders(params) + val headers = RawHeader("Accept", "application/x-json-stream") :: withHeaders(params) val request = withHttpRequest(url, HttpMethods.GET, headers, OAuth2Token) - val result = Source.single(request).via(conn).runForeach { - _ match { - case HttpResponse(status, headers, entity, protocol) if (status.isSuccess()) => - println(" >>>>>>>>>>>>>>>>>>>>> " + entity.dataBytes) - case HttpResponse(status, headers, entity, protocol) if (status.isSuccess()) => - println(" >>>>>>>>>>>>>>>>>>>>> Error") + Source.single(request) + .via(conn) + .map(_.entity.dataBytes) + // .flatten(FlattenStrategy.concat) +// .map(_.decodeString("UTF-8")) + .runForeach(_.runForeach { x => println(" >>> "+x.decodeString("UTF-8")) }).onComplete { _ => + println("Shutting down") + system.terminate() } - } + + // val result = Source.single(request).via(conn).runForeach { + // _ match { + // case HttpResponse(status, headers, entity, protocol) if (status.isSuccess()) => + // println(" >>>>>>>>>>>>>>>>>>>>> " + entity.dataBytes) + // case HttpResponse(status, headers, entity, protocol) if (status.isSuccess()) => + // println(" >>>>>>>>>>>>>>>>>>>>> Error") + // } + // } val flow = Flow[ByteString] .via(akka.stream.scaladsl.Framing.delimiter( From 5d6b811cb73a2b13710af5abf0bfd0cef77613b1 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Fri, 22 Apr 2016 09:11:40 +0200 Subject: [PATCH 020/183] techjira:LAAS-60 Initial logic for stream event from http connection --- .../zalando/nakadi/client/HttpFactory.scala | 2 +- .../zalando/nakadi/client/TestFactory.scala | 3 +- .../client/EventTypesIntegrationTest.scala | 17 ++- .../client/done/EventConsumerActor.scala | 27 ++++ .../client/done/EventPublishingActor.scala | 18 +++ .../nakadi/client/example2/Ecample3.scala | 95 +++++++++++++ .../client/example2/HttpClientExample.scala | 130 +++++++++++------- .../client/logic/ReactiveStreamsSupport.scala | 16 +++ .../nakadi/client/logic/Receiver.scala | 56 ++++++++ .../nakadi/client/logic/ReceiverGraph.scala | 56 ++++++++ .../zalando/nakadi/client/logic/package.scala | 7 + project/Dependencies.scala | 1 + 12 files changed, 371 insertions(+), 57 deletions(-) create mode 100644 it/src/test/scala/org/zalando/nakadi/client/done/EventConsumerActor.scala create mode 100644 it/src/test/scala/org/zalando/nakadi/client/done/EventPublishingActor.scala create mode 100644 it/src/test/scala/org/zalando/nakadi/client/example2/Ecample3.scala create mode 100644 it/src/test/scala/org/zalando/nakadi/client/logic/ReactiveStreamsSupport.scala create mode 100644 it/src/test/scala/org/zalando/nakadi/client/logic/Receiver.scala create mode 100644 it/src/test/scala/org/zalando/nakadi/client/logic/ReceiverGraph.scala create mode 100644 it/src/test/scala/org/zalando/nakadi/client/logic/package.scala diff --git a/client/src/main/scala/org/zalando/nakadi/client/HttpFactory.scala b/client/src/main/scala/org/zalando/nakadi/client/HttpFactory.scala index e88b612..bdd04c6 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/HttpFactory.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/HttpFactory.scala @@ -40,7 +40,7 @@ trait HttpFactory { def withHeaders(params: Option[StreamParameters]): List[HttpHeader] = { params match { case Some(StreamParameters(cursor, _, _, _, _, _, flowId)) => - val parameters = List(("X-nakadi-cursors", cursor), ("X-Flow-Id", flowId)) + val parameters = List(("X-Nakadi-Cursors", cursor), ("X-Flow-Id", flowId)) for { (key, optional) <- parameters; value <- optional } yield RawHeader(key, value) case None => Nil } diff --git a/client/src/test/scala/org/zalando/nakadi/client/TestFactory.scala b/client/src/test/scala/org/zalando/nakadi/client/TestFactory.scala index fd5ffc7..dca5db0 100644 --- a/client/src/test/scala/org/zalando/nakadi/client/TestFactory.scala +++ b/client/src/test/scala/org/zalando/nakadi/client/TestFactory.scala @@ -17,11 +17,12 @@ import org.zalando.nakadi.client.model.PartitionStrategy import org.zalando.nakadi.client.model.SchemaType trait ClientFactory { - val host = "nakadi-sandbox." + val host = "nakadi-sandbox.aruha." val OAuth2Token = () => "" val port = 443 val connection = Connection.newConnection(host, port, OAuth2Token, true, false) val client = new ClientImpl(connection, "UTF-8") + } diff --git a/it/src/test/scala/org/zalando/nakadi/client/EventTypesIntegrationTest.scala b/it/src/test/scala/org/zalando/nakadi/client/EventTypesIntegrationTest.scala index 52f313d..8d7bcd4 100644 --- a/it/src/test/scala/org/zalando/nakadi/client/EventTypesIntegrationTest.scala +++ b/it/src/test/scala/org/zalando/nakadi/client/EventTypesIntegrationTest.scala @@ -14,19 +14,18 @@ class EventTypeTest extends WordSpec with Matchers with JacksonJsonMarshaller wi val eventType = createUniqueEventType() val creationResult = eventTypeAction.create(eventType) creationResult.isDefined shouldBe false - + //Check the created EventType checkEventTypeExists(eventType) - case class MyEventExample(orderNumber:String) + case class MyEventExample(orderNumber: String) implicit def problemTR: TypeReference[MyEventExample] = new TypeReference[MyEventExample] {} - - eventAction.create("test-client-integration-event-1936085527-148383828851369665",List(MyEventExample("1872361"))) - eventAction.create("test-client-integration-event-1936085527-148383828851369665",List(MyEventExample("1872362"))) - eventAction.create("test-client-integration-event-1936085527-148383828851369665",List(MyEventExample("1872363"))) - eventAction.create("test-client-integration-event-1936085527-148383828851369665",List(MyEventExample("1872364"))) - - + val events = for { + a <- 0 to 4005 + } yield MyEventExample("order-"+a) +// eventAction.create("test-client-integration-event-1936085527-148383828851369665", List(MyEventExample("test-1"))) + eventAction.create("test-client-integration-event-1936085527-148383828851369665", events) + //TODO: Enable this when PUT is supported. // Update the event // val updatedEvent = eventType.copy(owningApplication = "laas-team-2") diff --git a/it/src/test/scala/org/zalando/nakadi/client/done/EventConsumerActor.scala b/it/src/test/scala/org/zalando/nakadi/client/done/EventConsumerActor.scala new file mode 100644 index 0000000..6c870fe --- /dev/null +++ b/it/src/test/scala/org/zalando/nakadi/client/done/EventConsumerActor.scala @@ -0,0 +1,27 @@ +package org.zalando.nakadi.client.done + +import akka.actor.Actor +import akka.actor.ActorLogging +import akka.stream.actor.ActorSubscriber +import akka.stream.actor.ActorSubscriberMessage.OnNext +import akka.stream.actor.RequestStrategy +import akka.util.ByteString + +class EventConsumingActor extends Actor with ActorLogging with ActorSubscriber { + + var count = 0 + + override protected def requestStrategy: RequestStrategy = new RequestStrategy { + override def requestDemand(remainingRequested: Int): Int = { + Math.max(remainingRequested, 10) + } + } + + override def receive: Receive = { + case OnNext(input: ByteString) => + count += 1 + println(s"[Got an event($count) "+ input.utf8String+"]") + case OnNext(_) => + println("Got something") + } +} diff --git a/it/src/test/scala/org/zalando/nakadi/client/done/EventPublishingActor.scala b/it/src/test/scala/org/zalando/nakadi/client/done/EventPublishingActor.scala new file mode 100644 index 0000000..571e90b --- /dev/null +++ b/it/src/test/scala/org/zalando/nakadi/client/done/EventPublishingActor.scala @@ -0,0 +1,18 @@ +package org.zalando.nakadi.client.done + +//class EventPublishingActor extends Actor with ActorLogging with ActorPublisher[Url] { +// +// val queue: mutable.Queue[Url] = mutable.Queue() +// val visited: mutable.Set[String] = mutable.Set() +// +// override def receive: Receive = { +// case url: Url => +// if (!visited(url.url)) { +// visited += url.url +// queue.enqueue(url) +// if (isActive && totalDemand > 0) { +// onNext(queue.dequeue()) +// } +// } +// } +//} \ No newline at end of file diff --git a/it/src/test/scala/org/zalando/nakadi/client/example2/Ecample3.scala b/it/src/test/scala/org/zalando/nakadi/client/example2/Ecample3.scala new file mode 100644 index 0000000..6c05ea2 --- /dev/null +++ b/it/src/test/scala/org/zalando/nakadi/client/example2/Ecample3.scala @@ -0,0 +1,95 @@ +package org.zalando.nakadi.client.example2 + +import akka.stream.actor.ActorPublisher +import akka.actor.Actor +import akka.stream.actor.RequestStrategy +import akka.actor.ActorLogging +import akka.stream.actor.ActorSubscriber +import akka.stream.actor.ActorSubscriberMessage.OnNext +import akka.actor.Props +import akka.stream.scaladsl.Sink +import akka.stream.scaladsl.Source +import akka.actor.ActorSystem +import akka.http.scaladsl.model.HttpResponse +import scala.util.Try +import java.net.URI +import akka.stream.scaladsl.Flow +import org.zalando.nakadi.client.Connection +import akka.http.scaladsl.model.HttpRequest +import scala.concurrent.Future +import org.zalando.nakadi.client.HttpFactory +import akka.http.scaladsl.unmarshalling._ +import scala.concurrent.ExecutionContext.Implicits.global +import akka.http.scaladsl.Http +import org.zalando.nakadi.client.model.JacksonJsonMarshaller +import org.zalando.nakadi.client.ClientFactory +import org.zalando.nakadi.client.StreamParameters +import akka.stream.ActorMaterializer +import akka.http.scaladsl.model.headers.RawHeader +import akka.http.scaladsl.model.HttpMethods +import com.fasterxml.jackson.core.`type`.TypeReference +import org.zalando.nakadi.client.done.EventConsumingActor + +case class MoreMessages() + +class ProducerActor(httpRequest: HttpRequest) extends Actor with ActorPublisher[HttpRequest] with HttpFactory with ActorLogging { + + override def receive: Receive = { + case request: MoreMessages => + log.info("Got a request for more messages") + onNext(httpRequest) + } +} + + +case class Url(url: String, depth: Long) + +object Test extends App with ClientFactory with HttpFactory with JacksonJsonMarshaller { + + private implicit val actorSystem = ActorSystem("Nakadi-Client-Connections") + private implicit val http = Http(actorSystem) + implicit val materializer = ActorMaterializer() + + // Request Parameters + val eventName = "/event-types/test-client-integration-event-1936085527-148383828851369665/events" + val params = Some(StreamParameters()) + val headers = RawHeader("Accept", "application/x-json-stream") :: withHeaders(params) + val request = withHttpRequest(eventName, HttpMethods.GET, headers, OAuth2Token) + + //Model + case class MyEventExample(orderNumber: String) + implicit val myEvent = new TypeReference[MyEventExample] {} + + private implicit val system = ActorSystem("Nakadi-Client-Connections") + // private implicit val http = Http(actorSystem) + // implicit val materializer = ActorMaterializer() + + val producerRef = system.actorOf(Props(classOf[EventConsumingActor], request)) + val consumerRef = system.actorOf(Props(classOf[EventConsumingActor])) + val publisher = ActorPublisher[HttpRequest](producerRef) + val subscriber = ActorSubscriber[HttpResponse](consumerRef) + + val flow: Flow[HttpRequest, HttpResponse, Future[Http.OutgoingConnection]] = connection.connection() + + import Transformers._ + + Source.fromPublisher(publisher) + .via(flow).via(validRequestFilter)//.via(toByteString) + .runWith(Sink.fromSubscriber(subscriber)) + + // .map(url => pipeline(Get(url.url)).map((url, _))) + // .mapAsync(4)(identity) + // .map(parseUrls) + // .mapConcat(identity) + // .map(url => Url(url.url, url.depth - 1)) // reduce our depth on each step + // .filter(_.depth >= 0) // our recursion exit condition + // .filter(_.url.contains("akka")) // let’s say we only want to follow urls that contain ‘akka’ + // .runWith(Sink.fromSubscriber(subscriber)) + + producerRef ! Url("https://en.wikipedia.org/wiki/Akka_(toolkit)", 2) + + def parseUrls: ((String, HttpResponse)) => Future[String] = { + case (url, resp) => + Unmarshal(resp.entity).to[String] + } +} \ No newline at end of file diff --git a/it/src/test/scala/org/zalando/nakadi/client/example2/HttpClientExample.scala b/it/src/test/scala/org/zalando/nakadi/client/example2/HttpClientExample.scala index 5dfbe3c..3790764 100644 --- a/it/src/test/scala/org/zalando/nakadi/client/example2/HttpClientExample.scala +++ b/it/src/test/scala/org/zalando/nakadi/client/example2/HttpClientExample.scala @@ -1,73 +1,111 @@ package org.zalando.nakadi.client.example2 -import scala.concurrent.Future -import scala.util.Random - -import org.zalando.nakadi.client.{ ClientFactory, HttpFactory, StreamParameters } -import org.zalando.nakadi.client.model.Cursor import scala.concurrent.ExecutionContext.Implicits.global +import scala.concurrent.Future +import org.zalando.nakadi.client.ClientFactory +import org.zalando.nakadi.client.Connection +import org.zalando.nakadi.client.HttpFactory +import org.zalando.nakadi.client.NakadiDeserializer +import org.zalando.nakadi.client.StreamParameters +import org.zalando.nakadi.client.model.JacksonJsonMarshaller +import com.fasterxml.jackson.core.`type`.TypeReference +import akka.NotUsed +import akka.actor.Actor +import akka.actor.ActorLogging import akka.actor.ActorSystem +import akka.actor.Props +import akka.actor.actorRef2Scala import akka.http.scaladsl.Http -import akka.http.scaladsl.model.{ HttpMethods, HttpRequest, HttpResponse } +import akka.http.scaladsl.model.HttpHeader +import akka.http.scaladsl.model.HttpMethods +import akka.http.scaladsl.model.HttpRequest +import akka.http.scaladsl.model.HttpResponse import akka.http.scaladsl.model.headers.RawHeader +import akka.http.scaladsl.unmarshalling.Unmarshal import akka.stream.ActorMaterializer -import akka.stream.scaladsl.{ Flow, Source } +import akka.stream.ClosedShape +import akka.stream.FlowShape +import akka.stream.Outlet +import akka.stream.OverflowStrategy +import akka.stream.actor.ActorPublisher +import akka.stream.actor.ActorSubscriber +import akka.stream.actor.ActorSubscriberMessage.OnNext +import akka.stream.actor.RequestStrategy +import akka.stream.scaladsl.Flow +import akka.stream.scaladsl.GraphDSL +import akka.stream.scaladsl.RunnableGraph +import akka.stream.scaladsl.Sink +import akka.stream.scaladsl.Source import akka.util.ByteString +import org.zalando.nakadi.client.done.EventConsumingActor +import org.zalando.nakadi.client.model.Cursor -object HttpClient extends App { +object HttpClient extends App with ClientFactory with HttpFactory with JacksonJsonMarshaller { private implicit val actorSystem = ActorSystem("Nakadi-Client-Connections") private implicit val http = Http(actorSystem) implicit val materializer = ActorMaterializer() - val client = new HttpClient() - client.start() + val eventName = "/event-types/test-client-integration-event-1936085527-148383828851369665/events" + val cursor = Cursor(0,30115) + val params = Some(StreamParameters()) + val headers = RawHeader("Accept", "application/x-json-stream") :: withHeaders(params) + val request = withHttpRequest(eventName, HttpMethods.GET, headers, OAuth2Token) + case class MyEventExample(orderNumber: String) + implicit val myEvent = new TypeReference[MyEventExample] {} + val receiver = new ReceiverGraph[MyEventExample](eventName, connection, request, headers) + receiver.listen() } -class HttpClient(implicit system: ActorSystem, m: ActorMaterializer) extends ClientFactory with HttpFactory { +class ReceiverGraph[T](eventName: String, connection: Connection, request: HttpRequest, headers: List[HttpHeader]) // +(implicit system: ActorSystem, m: ActorMaterializer, des: NakadiDeserializer[T]) { - def generateWord(): Seq[String] = for { a <- 0 to 200 } yield Random.alphanumeric.take(10).mkString - println(generateWord()) - val source = Source.single(generateWord()) + import GraphDSL.Implicits._ + def listen() = { - // source.runForeach { println } + val subscriber = ActorSubscriber[ByteString](system.actorOf(Props[EventConsumingActor])) + val sink2 = Sink.fromSubscriber(subscriber) + val flow: Flow[HttpRequest, HttpResponse, Future[Http.OutgoingConnection]] = connection.connection() - def start() = { - val conn: Flow[HttpRequest, HttpResponse, Future[Http.OutgoingConnection]] = connection.connection() - val url = "/event-types/test-client-integration-event-1936085527-148383828851369665/events" - val cursor = Cursor(0, 0) - val params = Some(StreamParameters()) - val headers = RawHeader("Accept", "application/x-json-stream") :: withHeaders(params) - val request = withHttpRequest(url, HttpMethods.GET, headers, OAuth2Token) - Source.single(request) - .via(conn) - .map(_.entity.dataBytes) - // .flatten(FlattenStrategy.concat) -// .map(_.decodeString("UTF-8")) - .runForeach(_.runForeach { x => println(" >>> "+x.decodeString("UTF-8")) }).onComplete { _ => + import Transformers._ + val req = Source.single(request) // + .via(flow) // + .buffer(100, OverflowStrategy.backpressure) // + .via(validRequestFilter).map(_.entity.dataBytes) + .runForeach(_.runWith(sink2)).onComplete { _ => println("Shutting down") system.terminate() } + } - // val result = Source.single(request).via(conn).runForeach { - // _ match { - // case HttpResponse(status, headers, entity, protocol) if (status.isSuccess()) => - // println(" >>>>>>>>>>>>>>>>>>>>> " + entity.dataBytes) - // case HttpResponse(status, headers, entity, protocol) if (status.isSuccess()) => - // println(" >>>>>>>>>>>>>>>>>>>>> Error") - // } - // } + def test() = { + import Transformers._ + val g: RunnableGraph[_] = RunnableGraph.fromGraph { + GraphDSL.create() { implicit builder => + val out: Outlet[HttpResponse] = builder.add(Source.single(request).via(connection.connection())).out - val flow = Flow[ByteString] - .via(akka.stream.scaladsl.Framing.delimiter( - ByteString("\n"), - maximumFrameLength = 256, - allowTruncation = true)) - .map(_.utf8String) - .map(_ + "!!!\n") - .map { x => - println(s"$x") - ByteString(x) + //flows + // val validFilter: FlowShape[HttpResponse, HttpResponse] = builder.add(validRequestFilter) + val transform2String: FlowShape[HttpResponse, String] = builder.add(toByteString) + // val deserializer: FlowShape[String, T] = builder.add(transformToObject) + // val printer: Inlet[Any] = builder.add(Sink.foreach[Any](x => println(" >>> " + x))).in + val in = builder.add(Sink.foreach[String] { x: String => println(s" 1>> $x ") }).in + out ~> transform2String ~> in + + ClosedShape // } + } + g.run() + } + +} + +object Transformers { + def failure(s: String) = Future.failed(new IllegalArgumentException(s"Error $s")) + def toByteString(implicit m: ActorMaterializer): Flow[HttpResponse, String, NotUsed] = Flow[HttpResponse].mapAsync(1)(Unmarshal(_).to[String]) + val validRequestFilter: Flow[HttpResponse, HttpResponse, NotUsed] = Flow[HttpResponse].filter(_.status.isSuccess()) + def transformToObject[T](implicit des: NakadiDeserializer[T]): Flow[String, T, NotUsed] = Flow[String].map { x => + println(" x " + x) + des.fromJson(x) } } \ No newline at end of file diff --git a/it/src/test/scala/org/zalando/nakadi/client/logic/ReactiveStreamsSupport.scala b/it/src/test/scala/org/zalando/nakadi/client/logic/ReactiveStreamsSupport.scala new file mode 100644 index 0000000..fb8832a --- /dev/null +++ b/it/src/test/scala/org/zalando/nakadi/client/logic/ReactiveStreamsSupport.scala @@ -0,0 +1,16 @@ +package org.zalando.nakadi.client.logic + +import akka.actor.ActorSystem +import akka.util.Timeout +import scala.concurrent.duration._ +import akka.stream.ActorMaterializer + +trait ReactiveStreamsSupport extends Logging { + implicit def system: ActorSystem + + implicit val dispatcher = system.dispatcher + + implicit val timeout = Timeout(5.seconds) + + implicit val materializer = ActorMaterializer() +} diff --git a/it/src/test/scala/org/zalando/nakadi/client/logic/Receiver.scala b/it/src/test/scala/org/zalando/nakadi/client/logic/Receiver.scala new file mode 100644 index 0000000..82e035f --- /dev/null +++ b/it/src/test/scala/org/zalando/nakadi/client/logic/Receiver.scala @@ -0,0 +1,56 @@ +package org.zalando.nakadi.client.logic +// +//import java.net.InetSocketAddress +//import akka.actor.ActorSystem +//import akka.stream.scaladsl.GraphDSL.Implicits._ +//import akka.stream.scaladsl._ +//import akka.util.ByteString +//import scala.concurrent.{Future, Promise} +//import akka.event.Logging +//import org.zalando.nakadi.client.Connection +//import org.zalando.nakadi.client.logic.ReactiveStreamsSupport +// +//class Receiver(connection: Connection)(implicit val system: ActorSystem) extends ReactiveStreamsSupport { +// def run(): Future[Unit] = { +// val completionPromise = Promise[Unit]() +// +// val serverConnection = connection.connection() +// +// +// val receiverFlow = GraphDSL.create() { implicit d => +// val in = Source. UndefinedSource[ByteString] +// val out = UndefinedSink[ByteString] +// +// val split = Broadcast[ByteString] +// +// val mainFlow = Flow[ByteString] +// .mapConcat(reconcileFrames.apply) +// .map(MessageData.decodeFromString) +// .map { md => +// logger.debug(s"Receiver: received msg: $md") +// createFrame(md.id) +// } +// +// in ~> split +// +// split ~> mainFlow ~> out +// split ~> OnCompleteSink[ByteString] { t => completionPromise.complete(t); () } +// +// (in, out) +// } +// +// val materializedMap = serverConnection.handleWith(receiverFlow) +// val connectFuture = serverConnection.localAddress(materializedMap) +// +// connectFuture.onSuccess { case _ => logger.debug(s"Receiver: connected to broker") } +// connectFuture.onFailure { case e: Exception => logger.error("Receiver: failed to connect to broker", e) } +// +// completionPromise.future +// } +//} +// +//object SimpleReceiver extends App with SimpleServerSupport with Logging { +// val receiver: Receiver = new Receiver(receiveServerAddress) +// import receiver.system.dispatcher +// receiver.run().onComplete(result => logger.info("Receiver: completed with result " + result)) +//} \ No newline at end of file diff --git a/it/src/test/scala/org/zalando/nakadi/client/logic/ReceiverGraph.scala b/it/src/test/scala/org/zalando/nakadi/client/logic/ReceiverGraph.scala new file mode 100644 index 0000000..0a8e72b --- /dev/null +++ b/it/src/test/scala/org/zalando/nakadi/client/logic/ReceiverGraph.scala @@ -0,0 +1,56 @@ +package org.zalando.nakadi.client.logic + +import scala.concurrent.Future + +import org.zalando.nakadi.client.Connection + +import akka.actor.ActorSystem +import akka.http.scaladsl.Http +import akka.http.scaladsl.model.HttpHeader +import akka.http.scaladsl.model.HttpRequest +import akka.http.scaladsl.model.HttpResponse +import akka.stream.ActorMaterializer +import akka.stream.ClosedShape +import akka.stream.FlowShape +import akka.stream.Inlet +import akka.stream.Outlet +import akka.stream.scaladsl.Flow +import akka.stream.scaladsl.GraphDSL +import akka.stream.scaladsl.RunnableGraph +import akka.stream.scaladsl.Sink +import akka.stream.scaladsl.Source +import akka.util.ByteString + +class ReceiverGraph[T](eventName: String, connection: Connection, request: HttpRequest, headers: List[HttpHeader]) // +(implicit system: ActorSystem, m: ActorMaterializer) { + import GraphDSL.Implicits._ + + def listen() = { + + val g: RunnableGraph[_] = RunnableGraph.fromGraph(GraphDSL.create() { implicit builder => + + val flow: Flow[HttpRequest, HttpResponse, Future[Http.OutgoingConnection]] = connection.connection() + + // Source + val out: Outlet[HttpResponse] = builder.add(Source.single(request).via(flow)).out + val transform2ByteString: FlowShape[HttpResponse, ByteString] = builder.add(null) + val transform2String: FlowShape[ByteString, String] = builder.add(null) + val deserialize2Object: FlowShape[String, T] = builder.add(null) + val filterMessagesWithEvents: FlowShape[T, T] = builder.add(null) + val publishEvent2listener: Inlet[Any] = builder.add(Sink.ignore).in + + + // Graph + out ~> transform2ByteString ~> transform2String ~> + deserialize2Object ~> filterMessagesWithEvents ~> publishEvent2listener + + ClosedShape // + + }) + + g.run() + + } + + +} \ No newline at end of file diff --git a/it/src/test/scala/org/zalando/nakadi/client/logic/package.scala b/it/src/test/scala/org/zalando/nakadi/client/logic/package.scala new file mode 100644 index 0000000..60125dd --- /dev/null +++ b/it/src/test/scala/org/zalando/nakadi/client/logic/package.scala @@ -0,0 +1,7 @@ +package org.zalando.nakadi.client + +import com.typesafe.scalalogging.StrictLogging +package object logic { + type Logging = StrictLogging + +} \ No newline at end of file diff --git a/project/Dependencies.scala b/project/Dependencies.scala index cd03dbb..c51c8f4 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -12,6 +12,7 @@ object Dependencies { "com.typesafe.akka" %% "akka-http-spray-json-experimental" % akkaVersion withSources() withJavadoc(), "com.typesafe.akka" %% "akka-actor" % akkaVersion withSources() withJavadoc(), "com.typesafe.akka" %% "akka-http-experimental" % akkaVersion withSources() withJavadoc(), + "com.typesafe.akka" %% "akka-stream" % akkaVersion withSources() withJavadoc(), "com.typesafe.akka" %% "akka-cluster-metrics" % akkaVersion withSources() withJavadoc(), "com.typesafe.akka" %% "akka-testkit" % akkaVersion % "test" withSources() withJavadoc(), "joda-time" % "joda-time" % "2.9.3" withSources() withJavadoc(), From b54d92e17dda7ebba56f4c174a22ff9b3bb5315e Mon Sep 17 00:00:00 2001 From: Benjamin Friedrich Date: Mon, 25 Apr 2016 14:04:51 +0200 Subject: [PATCH 021/183] Update Main.java --- src/test/java/org/zalando/nakadi/client/utils/Main.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/org/zalando/nakadi/client/utils/Main.java b/src/test/java/org/zalando/nakadi/client/utils/Main.java index a02f234..962184d 100644 --- a/src/test/java/org/zalando/nakadi/client/utils/Main.java +++ b/src/test/java/org/zalando/nakadi/client/utils/Main.java @@ -19,7 +19,7 @@ public static void main(final String[] args) throws Exception { .withEndpoint(new URI("localhost")) .withPort(8080) // test config? .withSecuredConnection(false) - .withJavaTokenProvider(() -> "b099a178-acf7-467b-b106-4c0656285153") + .withJavaTokenProvider(() -> "") .buildJavaClient(); System.out.println("-METRICS--> " + client.getMetrics().get()); From a55f1feb1ddf089d25bf22a49f1abd2afbef0031 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Mon, 25 Apr 2016 21:47:17 +0200 Subject: [PATCH 022/183] techjira:LAAS-60 Listener logic implemented!! --- .../org/zalando/nakadi/client/Client.scala | 49 +- .../zalando/nakadi/client/ClientImpl.scala | 52 +- .../zalando/nakadi/client/Connection.scala | 95 +- .../zalando/nakadi/client/HttpFactory.scala | 6 +- .../client/actor/EventConsumingActor.scala | 55 + .../client/model/JacksonJsonMarshaller.scala | 1 + .../zalando/nakadi/client/TestFactory.scala | 2 +- .../nakadi/client/util/TestScalaEntity.scala | 6 +- examples/scalajs-play-core-react/.gitignore | 54 + .../scalajs-play-core-react/LICENSE-2.0.txt | 202 + examples/scalajs-play-core-react/README.md | 30 + examples/scalajs-play-core-react/activator | 334 + .../activator-launch-1.3.7.jar | Bin 0 -> 1213545 bytes .../scalajs-play-core-react/client/.gitignore | 1 + .../client/src/main/scala/SPA.scala | 13 + .../src/main/scala/component/Menu.scala | 13 + .../core/material/MaterialComponent.scala | 44 + .../main/scala/core/material/package.scala | 16 + .../main/scala/core/reactive/RxObserver.scala | 24 + .../scala/core/service/AbstractClient.scala | 31 + .../client/src/main/scala/layout/Layout.scala | 13 + .../src/main/scala/layout/MainLayout.scala | 10 + .../scala/layout/ReactLayoutWithMenu.scala | 58 + .../src/main/scala/pages/MyScreenPage.scala | 34 + .../main/scala/router/ApplicationRouter.scala | 31 + .../src/main/scala/service/SampleClient.scala | 6 + .../project/Build.scala | 58 + .../project/Settings.scala | 128 + .../project/build.properties | 4 + .../project/plugins.sbt | 13 + .../server/src/main/assets/main.less | 0 .../src/main/assets/material/material.css | 9751 +++++++++++++++++ .../src/main/assets/material/material.js | 3865 +++++++ .../src/main/assets/material/material.min.css | 9 + .../main/assets/material/material.min.css.map | 1 + .../src/main/assets/material/material.min.js | 10 + .../main/assets/material/material.min.js.map | 1 + .../src/main/resources/application.conf | 4 + .../main/scala/GlobalApplicationLoader.scala | 35 + .../scala/controllers/ApiController.scala | 20 + .../scala/controllers/DemoController.scala | 11 + .../scala/controllers/ServiceController.scala | 38 + .../src/main/scala/modules/Controllers.scala | 13 + .../src/main/scala/modules/Service.scala | 8 + .../main/scala/service/SampleApiImpl.scala | 10 + .../src/main/twirl/views/index.scala.html | 20 + .../shared/.js/.gitignore | 7 + .../src/main/scala/demo/SampleApi.scala | 5 + .../src/main/scala/demo/SampleApi.sjsir | Bin 0 -> 323 bytes .../client/ClientSubscriptionTest.scala | 49 + .../nakadi/client/KlientIntegrationTest.scala | 2 +- .../nakadi/client/example2/ClientMsg.scala | 33 - .../client/example2/HttpClientExample.scala | 2 +- .../nakadi/client/example2/Subscriber.scala | 59 + .../zalando/nakadi/client/model/Model.scala | 8 +- 55 files changed, 15230 insertions(+), 114 deletions(-) create mode 100644 client/src/main/scala/org/zalando/nakadi/client/actor/EventConsumingActor.scala create mode 100644 examples/scalajs-play-core-react/.gitignore create mode 100644 examples/scalajs-play-core-react/LICENSE-2.0.txt create mode 100644 examples/scalajs-play-core-react/README.md create mode 100755 examples/scalajs-play-core-react/activator create mode 100644 examples/scalajs-play-core-react/activator-launch-1.3.7.jar create mode 100644 examples/scalajs-play-core-react/client/.gitignore create mode 100644 examples/scalajs-play-core-react/client/src/main/scala/SPA.scala create mode 100644 examples/scalajs-play-core-react/client/src/main/scala/component/Menu.scala create mode 100644 examples/scalajs-play-core-react/client/src/main/scala/core/material/MaterialComponent.scala create mode 100644 examples/scalajs-play-core-react/client/src/main/scala/core/material/package.scala create mode 100644 examples/scalajs-play-core-react/client/src/main/scala/core/reactive/RxObserver.scala create mode 100644 examples/scalajs-play-core-react/client/src/main/scala/core/service/AbstractClient.scala create mode 100644 examples/scalajs-play-core-react/client/src/main/scala/layout/Layout.scala create mode 100644 examples/scalajs-play-core-react/client/src/main/scala/layout/MainLayout.scala create mode 100644 examples/scalajs-play-core-react/client/src/main/scala/layout/ReactLayoutWithMenu.scala create mode 100644 examples/scalajs-play-core-react/client/src/main/scala/pages/MyScreenPage.scala create mode 100644 examples/scalajs-play-core-react/client/src/main/scala/router/ApplicationRouter.scala create mode 100644 examples/scalajs-play-core-react/client/src/main/scala/service/SampleClient.scala create mode 100644 examples/scalajs-play-core-react/project/Build.scala create mode 100644 examples/scalajs-play-core-react/project/Settings.scala create mode 100644 examples/scalajs-play-core-react/project/build.properties create mode 100644 examples/scalajs-play-core-react/project/plugins.sbt create mode 100644 examples/scalajs-play-core-react/server/src/main/assets/main.less create mode 100644 examples/scalajs-play-core-react/server/src/main/assets/material/material.css create mode 100644 examples/scalajs-play-core-react/server/src/main/assets/material/material.js create mode 100644 examples/scalajs-play-core-react/server/src/main/assets/material/material.min.css create mode 100644 examples/scalajs-play-core-react/server/src/main/assets/material/material.min.css.map create mode 100644 examples/scalajs-play-core-react/server/src/main/assets/material/material.min.js create mode 100644 examples/scalajs-play-core-react/server/src/main/assets/material/material.min.js.map create mode 100644 examples/scalajs-play-core-react/server/src/main/resources/application.conf create mode 100644 examples/scalajs-play-core-react/server/src/main/scala/GlobalApplicationLoader.scala create mode 100644 examples/scalajs-play-core-react/server/src/main/scala/controllers/ApiController.scala create mode 100644 examples/scalajs-play-core-react/server/src/main/scala/controllers/DemoController.scala create mode 100644 examples/scalajs-play-core-react/server/src/main/scala/controllers/ServiceController.scala create mode 100644 examples/scalajs-play-core-react/server/src/main/scala/modules/Controllers.scala create mode 100644 examples/scalajs-play-core-react/server/src/main/scala/modules/Service.scala create mode 100644 examples/scalajs-play-core-react/server/src/main/scala/service/SampleApiImpl.scala create mode 100644 examples/scalajs-play-core-react/server/src/main/twirl/views/index.scala.html create mode 100644 examples/scalajs-play-core-react/shared/.js/.gitignore create mode 100644 examples/scalajs-play-core-react/shared/src/main/scala/demo/SampleApi.scala create mode 100644 examples/scalajs-play-core-react/shared/src/main/scala/demo/SampleApi.sjsir create mode 100644 it/src/test/scala/org/zalando/nakadi/client/ClientSubscriptionTest.scala delete mode 100644 it/src/test/scala/org/zalando/nakadi/client/example2/ClientMsg.scala create mode 100644 it/src/test/scala/org/zalando/nakadi/client/example2/Subscriber.scala diff --git a/client/src/main/scala/org/zalando/nakadi/client/Client.scala b/client/src/main/scala/org/zalando/nakadi/client/Client.scala index cfec0c7..c1c9047 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/Client.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/Client.scala @@ -4,18 +4,27 @@ import scala.concurrent.Future import org.zalando.nakadi.client.model._ import akka.actor.Terminated import akka.http.scaladsl.model.HttpResponse -import org.zalando.nakadi.client.model.Metrics -case class StreamParameters(cursor: Option[String]=None, // - batchLimit: Option[Integer]=None, - streamLimit: Option[Integer]=None, - batchFlushTimeout: Option[Integer]=None, - streamTimeout: Option[Integer]=None, - streamKeepAliveLimit: Option[Integer]=None, - flowId: Option[String]=None) +case class ClientError(msg: String, status: Option[Int]) + +case class StreamParameters(cursor: Option[Cursor] = None, // + batchLimit: Option[Integer] = None, + streamLimit: Option[Integer] = None, + batchFlushTimeout: Option[Integer] = None, + streamTimeout: Option[Integer] = None, + streamKeepAliveLimit: Option[Integer] = None, + flowId: Option[String] = None) { +} + +trait Listener[T] { + def id: String + def onSubscribed(): Unit + def onUnsubscribed(): Unit + def onReceive(sourceUrl: String, cursor: Cursor, event: T): Unit + def onError(sourceUrl: String, cursor: Cursor, error: ClientError): Unit +} trait Client { - import Client._ /** * Retrieve monitoring metrics @@ -103,7 +112,7 @@ trait Client { * }}} * @param name - Name of the EventType */ - def partitions(name: String)(implicit ser: NakadiDeserializer[Partition]): Future[Either[ClientError, Option[Partition]]] + def partitions(name: String)(implicit ser: NakadiDeserializer[Seq[Partition]]): Future[Either[ClientError, Option[Seq[Partition]]]] /** * Returns the Partition for the given EventType. @@ -148,9 +157,23 @@ trait Client { def stop(): Future[Terminated] -} + /** + * Registers the subscription of a listener to start streaming events from a partition in non-blocking fashion. + * + * @eventType - Name of the EventType to listen for. + * @parameters - Parameters for the streaming of events. + * @listener - Listener to pass the event to when it is received. + */ + def subscribe[T](eventType: String, parameters: StreamParameters, listener: Listener[T])(implicit ser: NakadiDeserializer[T]): Future[Option[ClientError]] + /** + * Removes the subscription of a listener, to stop streaming events from a partition. + * + * @eventType - Name of the EventType. + * @listener - Listener to unsubscribe from the streaming events. + */ + def unsubscribe[T](eventType: String, listener: Listener[T]): Future[Option[ClientError]] -object Client { - case class ClientError(msg: String, status: Option[Int]) } + + diff --git a/client/src/main/scala/org/zalando/nakadi/client/ClientImpl.scala b/client/src/main/scala/org/zalando/nakadi/client/ClientImpl.scala index b1e2250..251c8d6 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/ClientImpl.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/ClientImpl.scala @@ -1,24 +1,23 @@ package org.zalando.nakadi.client -import scala.Left -import scala.Right +import scala.{ Left, Right } import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.Future import scala.concurrent.duration.DurationInt + import org.slf4j.LoggerFactory import org.zalando.nakadi.client.model._ + import com.typesafe.scalalogging.Logger -import Client.ClientError + import akka.actor.Terminated -import akka.http.scaladsl.model.HttpHeader -import akka.http.scaladsl.model.HttpResponse -import akka.http.scaladsl.model.HttpResponse +import akka.http.scaladsl.model.{ HttpHeader, HttpMethod, HttpMethods, HttpResponse, MediaRange } +import akka.http.scaladsl.model.MediaTypes.`application/json` import akka.http.scaladsl.model.StatusCodes +import akka.http.scaladsl.model.headers.{ Accept, RawHeader } import akka.http.scaladsl.unmarshalling.Unmarshal -import akka.http.scaladsl.model.headers.RawHeader -private[client] class ClientImpl(connection: Connection, charSet: String = "UTF-8") extends Client with HttpFactory{ - import Client._ +private[client] class ClientImpl(connection: Connection, charSet: String = "UTF-8") extends Client with HttpFactory { implicit val materializer = connection.materializer val logger = Logger(LoggerFactory.getLogger(this.getClass)) @@ -52,11 +51,11 @@ private[client] class ClientImpl(connection: Connection, charSet: String = "UTF- } def events[T](name: String, params: Option[StreamParameters])(implicit ser: NakadiDeserializer[T]): Future[Either[ClientError, Option[T]]] = { - val headers = withHeaders(params) - logFutureEither(connection.get(URI_EVENTS_OF_EVENT_TYPE.format(name)).flatMap(in => mapToEither(in))) + val headers = withHeaders(params) :+ Accept(MediaRange(`application/json`)) + logFutureEither(connection.get(URI_EVENTS_OF_EVENT_TYPE.format(name), headers).flatMap(in => mapToEither(in))) } - def partitions(name: String)(implicit ser: NakadiDeserializer[Partition]): Future[Either[ClientError, Option[Partition]]] = { + def partitions(name: String)(implicit ser: NakadiDeserializer[Seq[Partition]]): Future[Either[ClientError, Option[Seq[Partition]]]] = { logFutureEither(connection.get(URI_PARTITIONS_BY_EVENT_TYPE.format(name)).flatMap(in => mapToEither(in))) } @@ -68,7 +67,7 @@ private[client] class ClientImpl(connection: Connection, charSet: String = "UTF- logFutureEither(connection.get(URI_VALIDATION_STRATEGIES).flatMap(mapToEither(_))) } - def enrichmentStrategies()(implicit des: NakadiDeserializer[Seq[EventEnrichmentStrategy.Value]]): Future[Either[ClientError, Option[Seq[EventEnrichmentStrategy.Value]]]] = { + def enrichmentStrategies()(implicit des: NakadiDeserializer[Seq[EventEnrichmentStrategy.Value]]): Future[Either[ClientError, Option[Seq[EventEnrichmentStrategy.Value]]]] = { logFutureEither(connection.get(URI_ENRICHMENT_STRATEGIES).flatMap(mapToEither(_))) } @@ -77,6 +76,33 @@ private[client] class ClientImpl(connection: Connection, charSet: String = "UTF- def stop(): Future[Terminated] = connection.stop() + def subscribe[T](eventType: String, params: StreamParameters, listener: Listener[T])(implicit ser: NakadiDeserializer[T]): Future[Option[ClientError]] = { + //TODO: Validate here before starting listening + (eventType, params, listener) match { + + case (_, _, listener) if listener == null => + Future.successful(Option(ClientError("Listener may not be empty(null)!", None))) + + case (eventType, _, _) if Option(eventType).isEmpty || eventType == "" => + Future.successful(Option(ClientError("Eventype may not be empty(null)!", None))) + + case (eventType, StreamParameters(cursor, _, _, _, _, _, _), listener) if Option(eventType).isDefined => + val url = if (cursor.isDefined) + URI_PARTITION_BY_EVENT_TYPE_AND_ID.format(eventType, cursor.get.partition) + else + URI_EVENTS_OF_EVENT_TYPE.format(eventType) + + val request = withHttpRequest(url, HttpMethods.GET, + RawHeader("Accept", "application/x-json-stream") :: withHeaders(Option(params)), //Headers + connection.tokenProvider()) + + logger.info("Subscribing listener {} on eventType {} with cursor {} ", listener.id, eventType, cursor) + connection.subscribe(url, eventType, request, listener) + Future.successful(None) + } + } + def unsubscribe[T](eventType: String, listener: Listener[T]): Future[Option[ClientError]] = ??? + //#################### //# HELPER METHODS # //#################### diff --git a/client/src/main/scala/org/zalando/nakadi/client/Connection.scala b/client/src/main/scala/org/zalando/nakadi/client/Connection.scala index 303f162..a3fa4c9 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/Connection.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/Connection.scala @@ -2,62 +2,49 @@ package org.zalando.nakadi.client import java.security.SecureRandom import java.security.cert.X509Certificate +import scala.collection.immutable.Seq import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.Future import scala.concurrent.duration.DurationInt import org.slf4j.LoggerFactory +import org.zalando.nakadi.client.actor.EventConsumer +import org.zalando.nakadi.client.model.Cursor import com.typesafe.scalalogging.Logger -import akka.actor.ActorSystem -import akka.actor.Terminated -import akka.http.scaladsl.Http -import akka.http.scaladsl.HttpsConnectionContext -import akka.http.scaladsl.model.ContentType -import akka.http.scaladsl.model.HttpMethod -import akka.http.scaladsl.model.HttpMethods -import akka.http.scaladsl.model.HttpMethods.POST -import akka.http.scaladsl.model.HttpRequest -import akka.http.scaladsl.model.HttpResponse -import akka.http.scaladsl.model.MediaRange -import akka.http.scaladsl.model.MediaTypes.`application/json` -import akka.http.scaladsl.model.Uri.apply -import akka.http.scaladsl.model.headers -import akka.http.scaladsl.model.headers.OAuth2BearerToken -import akka.stream.ActorMaterializer -import akka.stream.scaladsl.Flow -import akka.stream.scaladsl.Sink -import akka.stream.scaladsl.Source -import javax.net.ssl.SSLContext -import javax.net.ssl.TrustManager -import javax.net.ssl.X509TrustManager -import akka.http.scaladsl.model.HttpHeader -import scala.collection.immutable.Seq +import akka.actor.{ Props, ActorSystem, ActorLogging, _ } +import akka.http.scaladsl.{ Http, HttpsConnectionContext } +import akka.http.scaladsl.model.{ HttpHeader, _ } +import akka.stream.{ ActorMaterializer, OverflowStrategy } +import akka.stream.actor.{ ActorSubscriber, RequestStrategy } +import akka.stream.scaladsl.{ Flow, Sink, Source } +import akka.util.ByteString +import javax.net.ssl.{ SSLContext, TrustManager, X509TrustManager } +import org.zalando.nakadi.client.actor.EventConsumer.ShutdownMsg + +trait Connection extends HttpFactory { -trait Connection extends HttpFactory{ - - //Connection details - def host:String - def port:Int - def tokenProvider():TokenProvider - def connection():Flow[HttpRequest, HttpResponse, Future[Http.OutgoingConnection]] - + def host: String + def port: Int + def tokenProvider(): TokenProvider + def connection(): Flow[HttpRequest, HttpResponse, Future[Http.OutgoingConnection]] + def get(endpoint: String): Future[HttpResponse] def get(endpoint: String, headers: Seq[HttpHeader]): Future[HttpResponse] + def stream(endpoint: String, headers: Seq[HttpHeader]): Future[HttpResponse] def delete(endpoint: String): Future[HttpResponse] def post[T](endpoint: String, model: T)(implicit serializer: NakadiSerializer[T]): Future[HttpResponse] def put[T](endpoint: String, model: T)(implicit serializer: NakadiSerializer[T]): Future[HttpResponse] + def subscribe[T](url: String, eventType: String, request: HttpRequest, listener: Listener[T])(implicit des: NakadiDeserializer[T]) - - def stop(): Future[Terminated] def materializer(): ActorMaterializer } /** - * Companion object for factory methods. + * Companion object with factory methods. */ object Connection { - + /** * */ @@ -92,8 +79,7 @@ private[client] class ConnectionImpl(val host: String, val port: Int, val tokenP private implicit val actorSystem = ActorSystem("Nakadi-Client-Connections") private implicit val http = Http(actorSystem) implicit val materializer = ActorMaterializer() - private val timeout = 5.seconds - + private val actors: Map[String, Actor] = Map() val logger = Logger(LoggerFactory.getLogger(this.getClass)) @@ -106,28 +92,32 @@ private[client] class ConnectionImpl(val host: String, val port: Int, val tokenP def get(endpoint: String): Future[HttpResponse] = { logger.info("Get: {}", endpoint) - executeCall(withHttpRequest(endpoint, HttpMethods.GET,Nil,tokenProvider)) + executeCall(withHttpRequest(endpoint, HttpMethods.GET, Nil, tokenProvider)) } def get(endpoint: String, headers: Seq[HttpHeader]): Future[HttpResponse] = { logger.info("Get: {}", endpoint) - executeCall(withHttpRequest(endpoint, HttpMethods.GET,headers,tokenProvider)) + executeCall(withHttpRequest(endpoint, HttpMethods.GET, headers, tokenProvider)) + } + def stream(endpoint: String, headers: Seq[HttpHeader]): Future[HttpResponse] = { + logger.info("Streaming on Get: {}", endpoint) + executeCall(withHttpRequest(endpoint, HttpMethods.GET, headers, tokenProvider)) //TODO: Change to stream single event } def put[T](endpoint: String, model: T)(implicit serializer: NakadiSerializer[T]): Future[HttpResponse] = { logger.info("Get: {}", endpoint) - executeCall(withHttpRequest(endpoint, HttpMethods.GET,Nil,tokenProvider)) + executeCall(withHttpRequest(endpoint, HttpMethods.GET, Nil, tokenProvider)) } def post[T](endpoint: String, model: T)(implicit serializer: NakadiSerializer[T]): Future[HttpResponse] = { val entity = serializer.toJson(model) logger.info("Posting to endpoint {}", endpoint) logger.debug("Data to post {}", entity) - executeCall(withHttpRequestAndPayload(endpoint, entity, HttpMethods.POST,tokenProvider)) + executeCall(withHttpRequestAndPayload(endpoint, entity, HttpMethods.POST, tokenProvider)) } def delete(endpoint: String): Future[HttpResponse] = { logger.info("Delete: {}", endpoint) - executeCall(withHttpRequest(endpoint, HttpMethods.DELETE,Nil,tokenProvider)) + executeCall(withHttpRequest(endpoint, HttpMethods.DELETE, Nil, tokenProvider)) } private def executeCall(request: HttpRequest): Future[HttpResponse] = { @@ -146,5 +136,24 @@ private[client] class ConnectionImpl(val host: String, val port: Int, val tokenP } def stop(): Future[Terminated] = actorSystem.terminate() + + def subscribe[T](url: String, eventType: String, request: HttpRequest, listener: Listener[T])(implicit des: NakadiDeserializer[T]) = { + import EventConsumer._ + case class MyEventExample(orderNumber: String) + val subscriberRef = actorSystem.actorOf(Props(classOf[EventConsumer[T]], url, eventType, listener, des)) + val subscriber = ActorSubscriber[ByteString](subscriberRef) + val sink2 = Sink.fromSubscriber(subscriber) + val flow: Flow[HttpRequest, HttpResponse, Future[Http.OutgoingConnection]] = connection + + val req = Source.single(request) // + .via(flow) // + .buffer(100, OverflowStrategy.backpressure) // + .map(x => x.entity.dataBytes) + .runForeach(_.runWith(sink2)).onComplete { _ => + subscriberRef ! ShutdownMsg + println("Shutting down") + } + } + } diff --git a/client/src/main/scala/org/zalando/nakadi/client/HttpFactory.scala b/client/src/main/scala/org/zalando/nakadi/client/HttpFactory.scala index bdd04c6..cd20710 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/HttpFactory.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/HttpFactory.scala @@ -2,6 +2,7 @@ package org.zalando.nakadi.client import akka.http.scaladsl.model.HttpHeader import akka.http.scaladsl.model.headers.RawHeader +import akka.http.scaladsl.model.headers.Accept import java.security.SecureRandom import java.security.cert.X509Certificate @@ -40,7 +41,8 @@ trait HttpFactory { def withHeaders(params: Option[StreamParameters]): List[HttpHeader] = { params match { case Some(StreamParameters(cursor, _, _, _, _, _, flowId)) => - val parameters = List(("X-Nakadi-Cursors", cursor), ("X-Flow-Id", flowId)) + val c = if (cursor.isDefined) Option("[{partition: " + cursor.get.partition + ", offset: " + cursor.get.offset + "}]") else None + val parameters = List(("X-Nakadi-Cursors", c), ("X-Flow-Id", flowId)) for { (key, optional) <- parameters; value <- optional } yield RawHeader(key, value) case None => Nil } @@ -57,7 +59,7 @@ trait HttpFactory { } def withHttpRequest(url: String, httpMethod: HttpMethod, additionalHeaders: Seq[HttpHeader], tokenProvider: TokenProvider): HttpRequest = { - val allHeaders: Seq[HttpHeader] = additionalHeaders :+ headers.Accept(MediaRange(`application/json`)) :+ headers.Authorization(OAuth2BearerToken(tokenProvider())) + val allHeaders: Seq[HttpHeader] = additionalHeaders :+ headers.Authorization(OAuth2BearerToken(tokenProvider())) HttpRequest(uri = url, method = httpMethod).withHeaders(allHeaders) } diff --git a/client/src/main/scala/org/zalando/nakadi/client/actor/EventConsumingActor.scala b/client/src/main/scala/org/zalando/nakadi/client/actor/EventConsumingActor.scala new file mode 100644 index 0000000..a85fb48 --- /dev/null +++ b/client/src/main/scala/org/zalando/nakadi/client/actor/EventConsumingActor.scala @@ -0,0 +1,55 @@ +package org.zalando.nakadi.client.actor + +import akka.actor.Actor +import akka.actor.ActorLogging +import akka.stream.actor.ActorSubscriber +import akka.stream.actor.ActorSubscriberMessage.OnNext +import akka.stream.actor.RequestStrategy +import akka.util.ByteString +import org.zalando.nakadi.client.Listener +import org.zalando.nakadi.client.NakadiDeserializer +import org.zalando.nakadi.client.model.Cursor +import scala.util.Try +import scala.util.Success +import scala.util.Failure +import org.zalando.nakadi.client.ClientError + +object EventConsumer { + + case class Msg( + msg: ByteString) + + case class ShutdownMsg() +} + +class EventConsumer[T](url: String, eventType: String, listener: Listener[T], ser: NakadiDeserializer[T]) extends Actor with ActorLogging with ActorSubscriber { + import EventConsumer._ + var count = 0 + + override protected def requestStrategy: RequestStrategy = new RequestStrategy { + override def requestDemand(remainingRequested: Int): Int = { + Math.max(remainingRequested, 10) + } + } + + override def receive: Receive = { + case OnNext(msg: ByteString) => + val message = msg.utf8String + if (message.contains("events")){ + + count += 1 + println(s"[Got event nr $count for type $eventType and cursor * and message $message ] ") + // Try(ser.fromJson(msg.utf8String)) match { + // case Success(event) => + // listener.onReceive(eventType, cursor, event) + // case Failure(error) => + // val errorMsg = "Failed to Deserialize with error:" + error.getMessage + listener.onError(eventType, null, ClientError("Failed to Deserialize with an error!", None)) + } + // } + case OnNext(_) => + println("Got something") + } +} + + diff --git a/client/src/main/scala/org/zalando/nakadi/client/model/JacksonJsonMarshaller.scala b/client/src/main/scala/org/zalando/nakadi/client/model/JacksonJsonMarshaller.scala index e3f3967..fea71a1 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/model/JacksonJsonMarshaller.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/model/JacksonJsonMarshaller.scala @@ -57,6 +57,7 @@ trait JacksonJsonMarshaller { implicit def listOfEventValidationStrategyTR: TypeReference[Seq[EventValidationStrategy.Value]] = new TypeReference[Seq[EventValidationStrategy.Value]] {} implicit def listOfEventEnrichmentStrategyTR: TypeReference[Seq[EventEnrichmentStrategy.Value]] = new TypeReference[Seq[EventEnrichmentStrategy.Value]] {} implicit def listOfEventTypeTR: TypeReference[Seq[EventType]] = new TypeReference[Seq[EventType]] {} + implicit def listOfPartitionTR: TypeReference[Seq[Partition]] = new TypeReference[Seq[Partition]] {} diff --git a/client/src/test/scala/org/zalando/nakadi/client/TestFactory.scala b/client/src/test/scala/org/zalando/nakadi/client/TestFactory.scala index dca5db0..bb99e55 100644 --- a/client/src/test/scala/org/zalando/nakadi/client/TestFactory.scala +++ b/client/src/test/scala/org/zalando/nakadi/client/TestFactory.scala @@ -17,7 +17,7 @@ import org.zalando.nakadi.client.model.PartitionStrategy import org.zalando.nakadi.client.model.SchemaType trait ClientFactory { - val host = "nakadi-sandbox.aruha." + val host = "nakadi-sandbox.my-test.fernan.do" val OAuth2Token = () => "" val port = 443 val connection = Connection.newConnection(host, port, OAuth2Token, true, false) diff --git a/client/src/test/scala/org/zalando/nakadi/client/util/TestScalaEntity.scala b/client/src/test/scala/org/zalando/nakadi/client/util/TestScalaEntity.scala index 2dae52c..dd8c59f 100644 --- a/client/src/test/scala/org/zalando/nakadi/client/util/TestScalaEntity.scala +++ b/client/src/test/scala/org/zalando/nakadi/client/util/TestScalaEntity.scala @@ -22,8 +22,8 @@ object TestScalaEntity { // Simple bjects composed out of scalar typers only (without dependency to other Object-models) val problem = new Problem("problemType", "title", 312, Option("detail"), Option("instance")) val metrics = new Metrics(Map("metrics"->"test")) - val partition = new Partition("partition", "oldestAvailableOffset", "newestAvailableOffset") - val cursor = new Cursor(0, 0) + val partition = new Partition(0, 132, 4423) + val cursor = new Cursor(0, Some(0)) val eventTypeSchema = new EventTypeSchema(SchemaType.JSON, "schema") val eventValidationStrategy = EventValidationStrategy.NONE val partitionResolutionStrategy = PartitionStrategy.HASH @@ -38,7 +38,7 @@ object TestScalaEntity { val eventStreamBatch = new EventStreamBatch(cursor, List(myEvent)) val batchItemResponse = new BatchItemResponse(Option("eid"), BatchItemPublishingStatus.SUBMITTED, Option(BatchItemStep.PUBLISHING), Option("detail")) - // own event + // custom event case class CommissionEntity(id:String,ql:List[String]) val commissionEntity=new CommissionEntity("id2",List("ql1","ql2")) val dataChangeEvent = new DataChangeEvent[CommissionEntity](commissionEntity,"Critical", DataOperation.DELETE, Some(eventMetadata)) diff --git a/examples/scalajs-play-core-react/.gitignore b/examples/scalajs-play-core-react/.gitignore new file mode 100644 index 0000000..5f3abf9 --- /dev/null +++ b/examples/scalajs-play-core-react/.gitignore @@ -0,0 +1,54 @@ +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm + +*.iml + +## Directory-based project format: +.idea/ +# if you remove the above rule, at least ignore the following: + +# User-specific stuff: +# .idea/workspace.xml +# .idea/tasks.xml +# .idea/dictionaries +# .idea/shelf + +# Sensitive or high-churn files: +# .idea/dataSources.ids +# .idea/dataSources.xml +# .idea/sqlDataSources.xml +# .idea/dynamic.xml +# .idea/uiDesigner.xml + +# Gradle: +# .idea/gradle.xml +# .idea/libraries + +# Mongo Explorer plugin: +# .idea/mongoSettings.xml + +## File-based project format: +*.ipr +*.iws + +## Plugin-specific files: + +# IntelliJ +/out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties +target/ +lib_managed/ +src_managed/ +project/boot/ +.history +.cache diff --git a/examples/scalajs-play-core-react/LICENSE-2.0.txt b/examples/scalajs-play-core-react/LICENSE-2.0.txt new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/examples/scalajs-play-core-react/LICENSE-2.0.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/examples/scalajs-play-core-react/README.md b/examples/scalajs-play-core-react/README.md new file mode 100644 index 0000000..e09bba0 --- /dev/null +++ b/examples/scalajs-play-core-react/README.md @@ -0,0 +1,30 @@ +# ScalaJS and Play base project with React and MDL + +This branch using [ScalaJS - React](https://github.com/japgolly/scalajs-react) with [MDL](http://www.getmdl.io/) in the client side + +This project using [Macwire](https://github.com/adamw/macwire) for dependency injection and ignore Play layout. + +The frontend-backend communication is based is [autowire](https://github.com/lihaoyi/autowire) and [boopickle](https://github.com/ochrons/boopickle) + +## The application structure + +The application have three parts. + +* shared +* server +* client + +#### Shared + +The shared directory contains the frontend and backend shared codes. We put here the communication objects and the API traits. + +#### Server + +This is the play server directory. The application override the default play application loader because it's using MacWire for dependency injection and [Play SIRD](https://www.playframework.com/documentation/2.4.x/ScalaSirdRouter) for the routing. + +##### Depth + +The application loading started in the `GlobalApplicationLoader` class. The `BuiltInComponentFromContextWithPlayWorkaround` class is a workaround to Play, because Play doesn't handle well the big file upload, when you not using guice for dependency injection. +The Routing is in the `ApplicationComponents` class, this file depend to the `Controller` and `Service` trait which contains the dependent classes. + +The `ApiController` methods will handle the different API calls. Each api entry point must exists in the `ApplicationComponents` router (See sampleApi call) diff --git a/examples/scalajs-play-core-react/activator b/examples/scalajs-play-core-react/activator new file mode 100755 index 0000000..df609ab --- /dev/null +++ b/examples/scalajs-play-core-react/activator @@ -0,0 +1,334 @@ +#!/usr/bin/env bash + +### ------------------------------- ### +### Helper methods for BASH scripts ### +### ------------------------------- ### + +realpath () { +( + TARGET_FILE="$1" + + cd "$(dirname "$TARGET_FILE")" + TARGET_FILE=$(basename "$TARGET_FILE") + + COUNT=0 + while [ -L "$TARGET_FILE" -a $COUNT -lt 100 ] + do + TARGET_FILE=$(readlink "$TARGET_FILE") + cd "$(dirname "$TARGET_FILE")" + TARGET_FILE=$(basename "$TARGET_FILE") + COUNT=$(($COUNT + 1)) + done + + if [ "$TARGET_FILE" == "." -o "$TARGET_FILE" == ".." ]; then + cd "$TARGET_FILE" + TARGET_FILEPATH= + else + TARGET_FILEPATH=/$TARGET_FILE + fi + + # make sure we grab the actual windows path, instead of cygwin's path. + if ! is_cygwin; then + echo "$(pwd -P)/$TARGET_FILE" + else + echo $(cygwinpath "$(pwd -P)/$TARGET_FILE") + fi +) +} + +# TODO - Do we need to detect msys? + +# Uses uname to detect if we're in the odd cygwin environment. +is_cygwin() { + local os=$(uname -s) + case "$os" in + CYGWIN*) return 0 ;; + *) return 1 ;; + esac +} + +# This can fix cygwin style /cygdrive paths so we get the +# windows style paths. +cygwinpath() { + local file="$1" + if is_cygwin; then + echo $(cygpath -w $file) + else + echo $file + fi +} + +# Make something URI friendly +make_url() { + url="$1" + local nospaces=${url// /%20} + if is_cygwin; then + echo "/${nospaces//\\//}" + else + echo "$nospaces" + fi +} + +# Detect if we should use JAVA_HOME or just try PATH. +get_java_cmd() { + if [[ -n "$JAVA_HOME" ]] && [[ -x "$JAVA_HOME/bin/java" ]]; then + echo "$JAVA_HOME/bin/java" + else + echo "java" + fi +} + +echoerr () { + echo 1>&2 "$@" +} +vlog () { + [[ $verbose || $debug ]] && echoerr "$@" +} +dlog () { + [[ $debug ]] && echoerr "$@" +} +execRunner () { + # print the arguments one to a line, quoting any containing spaces + [[ $verbose || $debug ]] && echo "# Executing command line:" && { + for arg; do + if printf "%s\n" "$arg" | grep -q ' '; then + printf "\"%s\"\n" "$arg" + else + printf "%s\n" "$arg" + fi + done + echo "" + } + + exec "$@" +} +addJava () { + dlog "[addJava] arg = '$1'" + java_args=( "${java_args[@]}" "$1" ) +} +addApp () { + dlog "[addApp] arg = '$1'" + sbt_commands=( "${app_commands[@]}" "$1" ) +} +addResidual () { + dlog "[residual] arg = '$1'" + residual_args=( "${residual_args[@]}" "$1" ) +} +addDebugger () { + addJava "-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=$1" +} +addConfigOpts () { + dlog "[addConfigOpts] arg = '$*'" + for item in $* + do + addJava "$item" + done +} +# a ham-fisted attempt to move some memory settings in concert +# so they need not be messed around with individually. +get_mem_opts () { + local mem=${1:-1024} + local meta=$(( $mem / 4 )) + (( $meta > 256 )) || meta=256 + (( $meta < 1024 )) || meta=1024 + + # default is to set memory options but this can be overridden by code section below + memopts="-Xms${mem}m -Xmx${mem}m" + if [[ "${java_version}" > "1.8" ]]; then + extmemopts="-XX:MetaspaceSize=64m -XX:MaxMetaspaceSize=${meta}m" + else + extmemopts="-XX:PermSize=64m -XX:MaxPermSize=${meta}m" + fi + + if [[ "${java_opts}" == *-Xmx* ]] || [[ "${java_opts}" == *-Xms* ]] || [[ "${java_opts}" == *-XX:MaxPermSize* ]] || [[ "${java_opts}" == *-XX:ReservedCodeCacheSize* ]] || [[ "${java_opts}" == *-XX:MaxMetaspaceSize* ]]; then + # if we detect any of these settings in ${java_opts} we need to NOT output our settings. + # The reason is the Xms/Xmx, if they don't line up, cause errors. + memopts="" + extmemopts="" + fi + + echo "${memopts} ${extmemopts}" +} +require_arg () { + local type="$1" + local opt="$2" + local arg="$3" + if [[ -z "$arg" ]] || [[ "${arg:0:1}" == "-" ]]; then + die "$opt requires <$type> argument" + fi +} +is_function_defined() { + declare -f "$1" > /dev/null +} + +# If we're *not* running in a terminal, and we don't have any arguments, then we need to add the 'ui' parameter +detect_terminal_for_ui() { + [[ ! -t 0 ]] && [[ "${#residual_args}" == "0" ]] && { + addResidual "ui" + } + # SPECIAL TEST FOR MAC + [[ "$(uname)" == "Darwin" ]] && [[ "$HOME" == "$PWD" ]] && [[ "${#residual_args}" == "0" ]] && { + echo "Detected MAC OSX launched script...." + echo "Swapping to UI" + addResidual "ui" + } +} + +# Processes incoming arguments and places them in appropriate global variables. called by the run method. +process_args () { + while [[ $# -gt 0 ]]; do + case "$1" in + -h|-help) usage; exit 1 ;; + -v|-verbose) verbose=1 && shift ;; + -d|-debug) debug=1 && shift ;; + -mem) require_arg integer "$1" "$2" && app_mem="$2" && shift 2 ;; + -jvm-debug) + if echo "$2" | grep -E ^[0-9]+$ > /dev/null; then + addDebugger "$2" && shift + else + addDebugger 9999 + fi + shift ;; + -java-home) require_arg path "$1" "$2" && java_cmd="$2/bin/java" && shift 2 ;; + -D*) addJava "$1" && shift ;; + -J*) addJava "${1:2}" && shift ;; + *) addResidual "$1" && shift ;; + esac + done + + is_function_defined process_my_args && { + myargs=("${residual_args[@]}") + residual_args=() + process_my_args "${myargs[@]}" + } +} + +# Actually runs the script. +run() { + # TODO - check for sane environment + + # process the combined args, then reset "$@" to the residuals + process_args "$@" + detect_terminal_for_ui + set -- "${residual_args[@]}" + argumentCount=$# + + #check for jline terminal fixes on cygwin + if is_cygwin; then + stty -icanon min 1 -echo > /dev/null 2>&1 + addJava "-Djline.terminal=jline.UnixTerminal" + addJava "-Dsbt.cygwin=true" + fi + + # run sbt + execRunner "$java_cmd" \ + "-Dactivator.home=$(make_url "$activator_home")" \ + $(get_mem_opts $app_mem) \ + ${java_opts[@]} \ + ${java_args[@]} \ + -jar "$app_launcher" \ + "${app_commands[@]}" \ + "${residual_args[@]}" + + local exit_code=$? + if is_cygwin; then + stty icanon echo > /dev/null 2>&1 + fi + exit $exit_code +} + +# Loads a configuration file full of default command line options for this script. +loadConfigFile() { + cat "$1" | sed '/^\#/d' +} + +### ------------------------------- ### +### Start of customized settings ### +### ------------------------------- ### +usage() { + cat < [options] + + Command: + ui Start the Activator UI + new [name] [template-id] Create a new project with [name] using template [template-id] + list-templates Print all available template names + -h | -help Print this message + + Options: + -v | -verbose Make this runner chattier + -d | -debug Set sbt log level to debug + -mem Set memory options (default: $sbt_mem, which is $(get_mem_opts $sbt_mem)) + -jvm-debug Turn on JVM debugging, open at the given port. + + # java version (default: java from PATH, currently $(java -version 2>&1 | grep version)) + -java-home Alternate JAVA_HOME + + # jvm options and output control + -Dkey=val Pass -Dkey=val directly to the java runtime + -J-X Pass option -X directly to the java runtime + (-J is stripped) + + # environment variables (read from context) + JAVA_OPTS Environment variable, if unset uses "" + SBT_OPTS Environment variable, if unset uses "" + ACTIVATOR_OPTS Environment variable, if unset uses "" + +In the case of duplicated or conflicting options, the order above +shows precedence: environment variables lowest, command line options highest. +EOM +} + +### ------------------------------- ### +### Main script ### +### ------------------------------- ### + +declare -a residual_args +declare -a java_args +declare -a app_commands +declare -r real_script_path="$(realpath "$0")" +declare -r activator_home="$(realpath "$(dirname "$real_script_path")")" +declare -r app_version="1.3.7" + +declare -r app_launcher="${activator_home}/activator-launch-${app_version}.jar" +declare -r script_name=activator +java_cmd=$(get_java_cmd) +declare -r java_opts=( "${ACTIVATOR_OPTS[@]}" "${SBT_OPTS[@]}" "${JAVA_OPTS[@]}" "${java_opts[@]}" ) +userhome="$HOME" +if is_cygwin; then + # cygwin sets home to something f-d up, set to real windows homedir + userhome="$USERPROFILE" +fi +declare -r activator_user_home_dir="${userhome}/.activator" +declare -r java_opts_config_home="${activator_user_home_dir}/activatorconfig.txt" +declare -r java_opts_config_version="${activator_user_home_dir}/${app_version}/activatorconfig.txt" + +# Now check to see if it's a good enough version +declare -r java_version=$("$java_cmd" -version 2>&1 | awk -F '"' '/version/ {print $2}') +if [[ "$java_version" == "" ]]; then + echo + echo No java installations was detected. + echo Please go to http://www.java.com/getjava/ and download + echo + exit 1 +elif [[ ! "$java_version" > "1.6" ]]; then + echo + echo The java installation you have is not up to date + echo Activator requires at least version 1.6+, you have + echo version $java_version + echo + echo Please go to http://www.java.com/getjava/ and download + echo a valid Java Runtime and install before running Activator. + echo + exit 1 +fi + +# if configuration files exist, prepend their contents to the java args so it can be processed by this runner +# a "versioned" config trumps one on the top level +if [[ -f "$java_opts_config_version" ]]; then + addConfigOpts $(loadConfigFile "$java_opts_config_version") +elif [[ -f "$java_opts_config_home" ]]; then + addConfigOpts $(loadConfigFile "$java_opts_config_home") +fi + +run "$@" diff --git a/examples/scalajs-play-core-react/activator-launch-1.3.7.jar b/examples/scalajs-play-core-react/activator-launch-1.3.7.jar new file mode 100644 index 0000000000000000000000000000000000000000..c37f87925a03a91e48d553edfa786c771da9b388 GIT binary patch literal 1213545 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
  1. j<(KQdWc3nAN#jQVt`1*aMAR=!k#ksc`bl4Pk9+7@X!Ao?(p$#t zq%4w_2y7yPpzRR{4qDGmBaPdh@Oo&fc6jw=r1QHQnaNNHtVACQuS``G*=O*6?%B2z zb7@hO`k`M4g~DG#-2!;+Tn;|rW~9@Swr1rYx~y=^8jJWD(Z2#nT3Sp)_jL#_w_`B@ z1a28{e;z1d#c({xdYfEP|LczPpTboVzajGaR^}f64b0ftyP^+{uP6`5)kKa(q}tcB?z7tLP`Ml)lK$=m^# zk<85k=Y4VQ2y(*TWtIf&Ssrvhr#2JI) zm6*ITejtu*xHK)D;oPW}cI&aqzo~NON4jdlh(8dT)Vbbfy)A)9Q(L-xe4i0>!a*3p z;ZM|X)Zx^U)Wg(!%CrT!gxSI2rK}2r#@}Mc2(g3E=`BXHSy%frf}xuWH;Jm4 zDgPiVgkptj0vO=Nv|p0a5nbC%MwT&Ls(&$v+v6H6^2^L4dL6nXPY6P>yEZQ!K~Foz z)XhyczWRD;c2wSTj6JBvOE=8utNZxKN5)VE8XoMiLr z=Fwb*4H7h}2knMA8DAt2OtiBOY0A3Xj zi&FX>c^p5K?}c~AntSHjMkr&`@hl-WKbvIU#Tb#gcb&HzrtsA`k~n3^$hJDA(Relj zmnE7uD%OoYlJ=G`N{bO#c65?}21@ub8}NuiHF>-VNgY>xgIiDzLTm^>>_&fJ z10KDm!VjmBtJX&)EOF+AKKPgsewr9QcvmBYf#rVEY2{6L5SeeK zXsORRZPprYq*4p4fJ116FG1>Xmx@jtl*8NI-`rhksTr;PUbnmWGendK*+!|j7dVoa zg#LV(WDd$k$aX51si}!+$4l$o+h!e`w$P3*=mS*(SCxa-_HU}wHsXMGG*`4I?lmqv zZiM*(G7c(d9#L364(BHe@$D3UG&oTenJU{(+XU8F{4GB+DyjTN@k;H5mbH0+tFCj zP0D@nVuIm)RJ$4XV*%2+&cRt7E`HUBI?7bcw{BV`bP)EOp&pyV(I*_UdriQOes!h? zeDAsn@^_-bX$krOV0opg)Ffi-01=f5xNR7IW7QTwU?r{2$fd{pv7WU`ShYb5Dt>p1 z?hVX6SiA2t1<-a0yVPcsRD}jkwM7ZfIXGagi#UAf&F&u2Qg@$DROAs?g~Tc)4=&s7 zWY5O*w$@2UP3ztQJksmHcT^5i(x?FXu$c`A$0yhK>hikA_KQTu55ic0ZEDD(noI>1 zYD~DMK-6CQ;BNM8ETdh5h#N#^)J+KKkiX(d(f;My8$2_eD;H@X>w5uwT&%_ky&=FvN7 z8L;Nvr_yW+uaioTjPsyFebi~x9J(24xhhvMY^akg4r-J^br1FBt;39HubQlJr5G48 zaS_2qQ>pYr7%K{JIJQR#Vy45q;^)5shcmvduSozvSvZh5Cxl zDO)CW^VlYE2H5&xoiJCL&#+1dn~4$)nO+CKDABD%9dFyv2UkUwU1w-h*4Md-HW*A@c2__R) zL1rsa_m$6ScFSX~n^?b{AF)-;xcSZbtNs}suD=ZpE4Ku zeLNB$`_y#LjK^15_D45g?jH`}KjT?JG^1>oe4cw}`Q8SynJ)m&q@0qdBJX5`bx&w? z+ai>VHc(A0Di_tuzB{ixiSXXjLnxRVH>!w zqB_(_1@X5;!tSbf5WT~J-EG2(0C>F8KEDs7J-D7?fa6zry%LuOmeR#9K3-Lp?vD=H z`-YFK6W1qfy;nP&zRBA?1zW_LJ25> z*mxT}KOvC(FlpYm7F%;+?RG)Oo)$oDp)Z&d4L4xztM=G*#m3imBLHck9L)->#kf?< z94kT5!KqcAEri}yDQWgO)1rE$eoC*mRGi&Uue8;BI!9T32tCpiq7*B`IQp1=gnDE+ z?!A>z!qC)dc*`QmFm8WnS@-k3;59DGd3!&>Dn{83VZnC8=5C_yZ}0hJRmMB=R)T>4 zYoW;Y5AP8*w=w!pS7P!;0aN%bwqxbtZurIqbN<~+{_QQ+awBg{`?m`dzq`qhn8X8O z@$!vH8ybJ~R%$qvRoQ9t)cDZ^C*nrk!Vi#GTgx%1w$fPsHfNchXWJQE*}Asp?^-$S zCY`E}^(MMI;e*Q}h;fndwU7|37-uz3H>G)DssIEf;o2`%=j-}Lr1h?2Ownng)Nr`VqG3lEBjx~I`JX#zHuu=?o!RJ@XO z;VHazeO^QR?z77G$$PFrF=lyUh4NqX@`&W6jnqlMsMQul0$pN{CaLpz0Ke=})bN3Z z?&KqPEb$U$&k_b(n*Psb4FDNl*~%LpA1u6tLE>OAY_*a(`2lieO(ndqQ%6hCi}1s; zH$_Ma6>?B%=tB!XZ-{mQblIJGSHC9A=t#cZ{&6`)L~xyi<;^M;|5_Z9{lhB%py@c- zDl168u`&ND+GBqg?QcYq*~S|4`pR=vd;M6I?%X#sghCRPs5uLSgd=oBhRze9N@)_>l z4s2z#+>Nh3Kb~$=yt7o>wc||UvT;=6EW>4s<{p3>$mAW*H}lIiA_|BoZ80KB4{n*Y zOR&=F_mAQlU2G02XCF>69^{fYFdAi)FXcB@t75(D>$DTj1#db%l~}J(eGLQ-Cz`Bd z<=Qo&o_@eMY%9LT^xbC*ey-3qeZIPVYpAJ~8Xa@{I7w zX{3q)mZAMP(3#pFn|@G68bt<6{x(+38Hq^=*UEvxnp}* z62?#U^4f>|w~yqQoe%cLfNRsniAheDb50?&G_#OL+hK2P?x@$v3Z~U3Xc=?}Noe!J ztuTwoJVE&MLhsCXGzrsV1sD_P;c7h?LSGecVja}4b~T=Xsi$|AlRP9{Sc6iYe;?8Mt1XXGQddNKLz_VfCDBF% zx^4L_gi8=s zI!ygq+{Iti<>h(z{u^l$8?BkztetYNGXe+e>a3pSpe3b++O(ZiuQfs;1S_s2pdcZNn(${>|Q&iwoZttuL~3OKejaL{1+8&xEg{q#3b zhT>Mc9}TCz9+WA;(CMN-UUflEHM`GxmRC?&Irx<&lL|+CyMscn;;7e6@m<7LVJ^=z(Og%L!B}q8$k{R&vfVueZyss21q2#RNTb{M%b3l;43bY z6KSlFZB{}cLkmeZn*_lxv4=*{l8V=l_qiVi)2dB|qbOV17cL1OkrNIphlPopF+=oq z_v6eRl1GIWGV%nT?|PU3&+YAWV7lQHdCMaD{!oexV}mjz4fa2RYCDz&+eY1=Nxm4YMGXBy z1(yZWqb^~vveHfKELP|)?CFGO9A0ORLez!kB5Dg4V|$*FFd1=4;rv zipYHzJH_Is-S*Xtp9=b+k3(KG32-H5G;iJyK;LvQ7u% z-EAj>`m7MXq-?8U1dEGwq-?4MH;_Nf7YUa%rG_AhESU~hd%WHlltY+Xc=h$G=GPFV z<*%Kd2GyPKkTS~J_~TxyH@}5Gq_HZp&~}x+@15YNao#JZp_rF5qcagLblZqo)jS!0 zBm}OiWE|hWuK8EdF%i)jg9Ln^ef?vaB>8RG|3#bp4?_Fj)JabTOW7$oG_RA@rdjTJ zs1*A*S&DNctjTwPX64e?C>XQk=FR_GVA!IRkZ-2`7+BXku){P_H zrd}@Z^6aLrUOtYEX}?PE>ffw{(CiHH&qq-qFBuTiSHW|&MMEQ#Q;HIj;fG^^)8#@} zj_a0}uMB&nA;nd~S*q8kkkQAO7Yj!7!&?Ob> zK03M}J_pOKGDl9v2lkDrNxV7+;Rs4xmx_V*N zx(;!~FRk~mOXgBDFq0>`8e=iRTwc}>mq~f~m7m^&vCpxE?L@|dZbn_xoY!SEv1N3p zG3XP46^QuMRWGcxoi7t}eM1O2hA<^kLA2lMcD|Anbnvd4X}Q_dZW5QL%LKra9Uwkg zMlO%sETF|-A_*Qb=r8Vrb@65%|B?%QGV%{02pt|VsBkxtIPMqY zxZ~h&@k2FZB)N6FSfXmsF1LLM+WJH&$ zT!=E*g-&3h#2Yc9Au8XC4;F3U0rPoD)jw}BZ2?uG`w@T|xRE=i!^so9dabgy_1c(< zTi~HjeC#@#t}-8FwaY7;(LH;k*_;#6OS8Wd9$$x_C!6%f9i z@Z0SC%&cXiTbeoH@geb3$9FXWiv@IG-oDV?{v&34- zyUiRb%+e9!NfrH|uxr56KH_9cO!pP#uji(Cjwhlc_<9fo$MXNQFY*71;(LTb`Knbkcr-i;L(Pj zKgO({LnZbWH#d{jrdin1yv`m1XaSjTr*P9P;log*(8kSZAmyk8Z?=s+&|{#2?=F^k zc+i(N9YcirB6niH#g0c+Z@^1IoDDr8kYh-R-a!ieMteRv{&N&eb1 z%lIqy5S)#pIB9>%~*p-;HC?2>tvBtEMjTim%-_ z$S(sC5>Fo6L$Zq=&3sW@ar)av`58KXCJjr3_3&MG_lPArHBVw>YYJ})@9v4k#pPW6 z=_`u!j~#KoreG4vHrlcxFn_(x_Z~)I?{*{?rgKlR!-ik@)mLS)J!Rs@NanITqmQ{Q z#WZMyFk5pB?pD+*WXFG{iJVA$!s`~rHe%&Zcnv~Q`T5Y>Ki6=*&=CKliB$5^B(I!=zl*rP> zn<{1U$vbQrklM3l>3ULtL=VfihUJW?s+NZ{SFZ^jxj7ogxgAOJ??!#e#tY>I3ZbMX z%EdpNb4H408V;lNo0l(OV688h*HFr8dSHWVoxfz>l5JmP-N#`z4&kS0?em3~D2J@_ znfx5?4I109#=svVXHuw@>1kfP5s!}53_)N9)CmK+>}X^vPq4g;uu%bN`OSW?$uc5S z1RpyIc`s0&uO?`aMfAl@lG9hG=0!|B<3_{s_ykArh;H8cXi#Ae574^p!KkZzz1Ddf z%)7Cd`PN~3T7wL8l)A~yA3<}X%dJ~>e1?}liFhd|5(PVm-6o9xLFuEj*lSePSDd|H zbgc0uBrk@wU7N{d^BhBJgqgFoqvI+rU?;ARkkR(v8r&ldisbp6Fzz8gAQD>|;O_B! z^x$n9GUy+rME$c;QrmO?cKCn){+X|+75rL!xH|6ex&{rbpDf^!ecb8r+{c6 z?OR&b23*{4OH>sbPMX{a0q|BK4g6O{=Wp-igSJYD zrfYmtZs+?>^&jVV`^L)GUh{y=J1Q%zH=+alxFs6oMX}Adk*LNrhsL zIRT1ysl^t~BpE&=AxNFroEsgxeiLm&ROEAf;*?q8=akkS^beEu!u8_}QL=LEI_H$k zB`jR7hd_II-KNWVgl0;d6H$BS1`kJJ;QV@$HbHQ7aY`<5p9M5{d8fuyf*??;WvH($ z9Wl&?g~Rep)Q3F__XxCjsYZf&bWD|gGlMvir<_DKA6pPht1_Ho?%gG@4X_&ECWJ}P^SY~d@~eSOLYZc7tW@aF#ZEub zC(W$Yd_}3D$)E@PHyqUVkwyqb_YX}|t?e_`1ublEy$}u+WmYIli-B}{{L6$|4xVhW7%3>oBNSky%dhBY#vd^Qu}AYgib(y9TH74xJI83=7!{ht$p{( z6|xH}gD;FWBbi6*(1l<=BubwcfvZW&?{`=4&o75mUm3=+ZL@B(p|PT|m6)kbRg`s% z+o%dsN5G5qMSrAt?HdZ~&-Cph9ne-oqM-c;S(y%3u&}{fdwL+e_+CSNH@*gW(0=FF zVRD$=3r3ieU9-n4Hns*}QQa99o25#jD@JW$<&XCX+nv<`I zjg#OQ_UFAs9yR!262G|D>x!750%oS$0 zv}J?M&6`UYc^CqQk~p9ab6>wa}=C?y|#J>yjfgUWvWX^03 zjC%MYb^bmN1XB-{c&|d`)v2WWW#N|p9<`Bn5X2vI0Jbb^IsXe=L($t3Qh_h7!9Q-z z55HYp|6kS3|Lhx=(c{1*M66$&Hbd&*h%K)bVKwt>y~zpi#yW?&9E`_txM+7eoopgQ|z_4b9(Hx zgmZlCwfJ*h?6t4wZ?VS)?=r&(2kt7vzV+V~hJ72jYYfxwzsn8N9=MAQ;~TmQ4f{TL zmlF1U=q@nKyD3AjJ%HI?Lk&~`Ppi{Vfx^MulFV-$1YF+kGy^nMU6(Ou5C zK^!y%0%5xT8}HzX7ChM^sFC)JE3j5`bhxz3+Die}DnV8n9W*Tx#G|68NF>p+ifS)qG( zTcY5xC98HScw5d%KLim39qh1#vO5bH*sJ`CJM5)rsts&^ADArAqB zK!J+lo1m zTCpZUkmX8PXgyNw#mW=~0H~=j+oT~^qeSPR^X)WKSow!{1eyAejV*=YpI_}Jt*N>T ze#qP4Df-H3-(hviHmpTD_F>po7iXaTdd`uarknqvf@95h)uOicnJ%AJF>j=Rx*pcE zP;J^I@0#O&cT|Jycvq;(^h(>HRP;i%%jtzKKP`a^3tOVck-b?Yk}|AeEI)-ch1pY3 zgc*}skIX~b>7wW-wDIGL;U{@Pjse?ZzOi(MJV&v2C?R#i!(w)U5CM1;?!$Xb+OZJE z`RMFqK|HJ+3tqu8`SBs3*1Y(u-aC-9pg=aEWA%y%30h=#kcVv8@fB5b$)aV_z^I_e z9*|2K>2()ip;G0DMfA#q>?jxil0r{}iW!TkbCsX=q%^uBC$z}J+svgs=$6SD_QYd= zeuTmKyCIH8LV`g#3v_^Sw=iw2gh89Zaiq|~YIFNZw8T9fI^3{g@dc(r{Rh275APL$ z-IVz4lk}B2=i_s68^_4Rv{{?5OBMbF@`pAG?V+97j)WVE!SG4*+)d)GK^V%S8jiw1 z1n0v_i%mEy3q2E~`Oa~Hr7`?@fFu5^Pp>ewH{PcEUkn zrf!#%Q4C`yr_lRiQ7N#>_Li7LWQ?3i_m)a(i79U2bxyf5Vqh{9bbpgaKQP>dtb1hb zT-%Zu*%h(gJ`Y-*sxyCX?Ed|+%jh&9ZKe@pyi}B{1vzhU(Vo>{euj!aZG}{9 zEd&wrDv^NO0D^?HuK;G#L7*g4>U>gNLuqY5thbIwk!r<_*}+8CD44vOCNkBy%&n?Y zVmEH1#GE!&@A+idkY>M^-bsYdgaz zeeq_Z#L!5tGZ|97_a}~rOab*+cJxAKI@S+|t<7h}ZIV$Q24@7a7+c=B;cGjZS|K_d zVypOIw3RS2Me(gKGW%r4X#L{x*gra~1bW2H^Q2N)5foeVhhHC>e2b@LPD+*^+{4bf zafu7je->oSJXUaT@?ZIC=60fjieFIGC@m`BxSCL9?Ju)<5$a58)_qw+Q&baxgPy#H zuEVt#HvTyz<)v}TJU-MzTpgQVpoDm4}JOLrBi+6d&C;ZbeU-Jw+XzFB%(Uy!MAJHx#%8c_%-%TNy_nL~2 z77ohbL;Jgpn(kw<-%QEI}^iuFcHcb`lJ z^00T}5(xo~sGu<)UJJ?Uy-K{wfC98+&JIF|M5P1XF@GtE`x+@t)on3_JT@AEmLg6w zB~rzYrcYJg2^uNUeU;Lmjoz1`3|aKeeCiWWT8f~p{UN}EFlM_<(~ZW%Zz+uP-ZhsN zQoOlFS%02L#k813T*H#GQnsWncAh*zR=->=z4r#Trlw5S6k1X%fx+06UQ)H9#tOyU zH%o{PnYKqFa<00!dbZjMZ_eycR!mCRH;7tPzCA@BnVKh0uJ-w?R`u4}Z~$TX;HqXhU?#mZ8!3HO zo`Ikof0ZlZkaNefG4bmUy*k_Bp_&`1VW;;yQ6}~Jt5l15wsJLw`et+sOz+jI^sV#< zw2qu2*+WER`>1}VTV|`BE_?`?VFC1x&-3|cr39*#qX%a__KlD81s|+qc|2M5L8$lH zk`Dz$W>D=Rk+<^aQyr(Z8=4-awX;P^XOU4YZ_S%YzN0hwflW!m8Bx4E%6b!w@}|C9 z!4)Si(bKdgth6wiCv-Q}kSeeD0@4f!Eidc>CwwL04H@3NU;2!I6FC+7q<^LLLf1OW z7dfSXJ1Qn>qtXN^i|Jxhw+Ud2luQ+Fe;^-Jo zxlxWf8e33`8bB#88(CUR*uV9I7zf^F^X1ob_k$zz(;#@vNC#Z7`R|oH;@?oCu$7~w zq1|7)HDrJ3EdE1J82LpK2^b?YotyL)tb{Z_+-RaFu6P(Duoqje7lE{in9MN;F&zydl)jl>___Ac zHso7-2FPboEIAXn7e46_;VPt$d`UC?!x<%tM>>8*2x$oR z2vqM@<=isgN#~jDe8m^|T!4|$dx7r!!OIR}nBor}8hdc2xMfUSh?kmZK$OZ% zNvFI@pq7Ca?5egr`FaSE>*}@4j$`P%c=6EIBbzE0V+w1u!v4v+Nx=M;Tk0&WT~AX) zjAdw(cjC-*jI#TlZ0Grb)WWr`w3?v0tnmZ@a?|G2Y#s3Xvf!bYwXrzKmYIZVwOw5Q-HZH9 zO5O2qe^^1jgal;?esg7$nVFi8^V4%EsNL*LpV}b~9*>w0)}8Ve(#Qj}b3Mnqp*val zLTu_SNqX61mm6<~TX$23hewXT(Y~@iAMCV;Kg3|Co2GB6IIH>sAV7->(CmJb5E92* zBi~yUPFXPT64J+Rrl#A^n*0rWm9>S%Yjh>5xcnsAo8rxs)iWZK1#64$5$@dFV#7BQ z-j)m?Qgk$lRld}{W?_zNpWNaLh41NBLK^2fv2p*&p<+kHkvKU*Y#U=1>Peu#Qmbts zF8N@8o6Xu`Cg(9SBDKm1>~pTeUN<-A!}ujPg5bFtr94p+Gx^XB0$)0C`%VW*3m8W^ z-A59`6ZaLhWAVG2yO#wCL84XG8`4^{=p#H)!1Z)w?1Syey{(>ZQ}l`RG*?%4QUbQt zST}xI%|i^8@zFf-`N8sg5qK^)c@cOus?v3AiMVq486qA*chf_hdp~51dhbk5-)lc$ zl@JqOdKLAgY@$r5Y@=PuclkT6?Al~F-D`wj0bWvMoDTvA*XoZo?ce)S|GP=7zeR(> z<%cBRg1r%B!f?0D^gQ@b$;GlZWAIw2eBpf8w~&y9c!sS+z@tlpV8!3^$xvTkmz(VF zRU!UqzrezzgTk_tytUPDTCb*oUq#r%ZQ&I$^5}=vsMHcih#=pC+OQs2_CS{UFmKr@ zfF{{Nc*OEN#DI9(X3R|q&bIMgnH7JDZ{;X4KL(L?+kPIOwzo#5e=7ZnfIRz*R>!je z-v{sUtr4zfDlE*#^Zt_&i-MORD0Q_puZH>1zpONa81_bmDj<5u@~wcAs%ax3n0w=q2{vP??ngH=mRsZ4jXW~qyY9zoET+liM4Veq#>2Fk|T7n74Om4qJwX=ubF?*s^)PyEfmY64$NQ z@vN~pV`stp5!q?8L-@5$U*)3EO0=BSVfWIcGZHwm9}4hpTw|acJc}|*snj{H#2qRm z+m%`+G`&Y(uz+I|bXRe_?=Um81rf`~;{ON<)M^)>1vqmQweYS-ls@3LEng7A(|^{? zEQp_R0=KBkHCHzQ*~^Z?OLR2krYs9%MFQrlIa{BstLbZRf|P+(;~6)e>Z7g?TIQjr zdu=7U_ji>+KOUNFMzke8H35q1Sc3ApP9upo9-jw})6mPV6O_U~O=iDS72&%a#=$?m zdSoof{PGi)5OU7MA35185lU<-n+i3oY8RfD&zDn%7#=hgCmn@Ia7ddKSiyG_!^%Ly zLDdj|)cSFi8lR{!^gQMg`}}QKTep}2V~gl#;#6YXcVb8MdG5{?ghFD_-$%aK+qWlC z;a_9V28TKKg|V`^yY@E-8uB-r?!wf?!XoS55(clK~xf*)eZ;cKemLKex9EgtdxvEdO z7lIy?;6m0rpwBY9O%F$aK$geL5xyah7Z0!3oZWemj)b@$xZh2$L))wenCMXp;nD(X ztxiF@q0Mf(=f=X zDL%qcZRDX+NVf&)7!2~nzh%qT{E%iOd&P@llZlIbRS7W=6eE5p{P$XiD^B3xT30Zmlce zR$94xca3R=l|b#T9Wyt=ASh6YVuV*l(+$b^yb;YQTx`52&VUpK``i zaur8AUp>%=>2-HPfas-&$D-63(wWc_R%pGt@UgiAnUPXpKR+0~R{c5}>`d;_`srsl z7V~EX@z#;?R?JyQ!SA&3oDt=`$jXhFNZ8IP9}pW*dnAU;H;zE`^5xON%dYh^S2bG! znNBjKx~R;9l#gt;?MA@h5^q=Rs(4_8NAz~KeC<{?jbJhdP6xekLFh-5&Dk+)AfRSf zMTpFm=ycK4=zZ6bi!{EiHev7J9J3NRXu^Awd7HayQa`PIheiprvTDia2t}fQ91g3= zL{;T($WB81m> z*JfOAnsQ{L%e>3A+cH*dyjGt2^ZE3a?+qvD2XK^{ii(P69G#8^h^_`#1a|;;LUTvw z3)BiF@+Iri1O49GhnWU%mAiq87|5Lv59H)dXwF@kQ7{~#%e{!OOw5Ia z?J_7suc=C)QTtJ)Wdj(JGQ?k*#T!buA^coM&naRyS!Y%|q~2I28^pp0-$IjV?QoRr zUZb0v7tVb7{&U@N`4U_5%7MtN!&Qt`D0Ce%3w(V)rtWFYg=GCg>()ZU*yfhE zF~#B=W5fs1R{zmOmCDf*g&R@sFs}vdQ>E4d_RKAiDJLa4#F$d?LKuYWB+UUzCw6uo zecW}Hzl%(9NQ}ZD++sR4o5KoR_mE#-_iZ(@iga}&SQJ^8FyV-(JjDTo%D8ZjLK)b z?9gQ!x~~~x@X1_``DO9s-EfAbD#d1_lhalD<`zf@`%d9{I~o8B4j5JHU~WEb(yx+r z%9y4A>oyAm^Ky*qA4yFt)h@F|DLsqRcKqdbANDcFCniwbmX@4SOi90p39}4~-=yMU zm=`OMpGpM+stT3+p=%GEy0DjQPNIjGWa6*k_Nju?b+cKhM$!#uXEDPK7p)IzXp)-8 z4(l@G!ym2aL0WP~g9ApB&}r_#4Y{bxg`t;?GBp>d zf}9M@t=-`_s8*7gHCu?Of+qKNX`{;SyxNYsk6i2rQuevgSXNT?@|xpzK0HjmgM-~Q z<&dftvIm~yyAezXbV7DU0Y{^!&*E<$#qqWGzkI9Nraiunog6tKJ-&>ceDRND-J#6o z!z47V|8~Va>M{v`wL2$#+1AZ{^PyMgWYBt^>6-`WM7dzVv8BaVM@L$VRD3Q$kU=-y z2FZtlA2Z$xn#^|>W&#m1UWEq4Bm3ql<{M$XO^fDyamhW5wf-Qgm-tgBf;Qm;&QI~q zCw~(g#%2o?hKk^3~pX=;!& z%mzhoFp(lE69U(p{!Kxd1lvjbi!Yec+f38<-HJ!&@Xx4H)`ggTXj0F&^9tabPeJ$pfm%HMmhz@#eQtX!D0TNIUS4!U zq3ZN~!%sjZ*cf>z>$3`Hpb()Hv?e#z9^)oI|Jp7ODsK!zql4IZfQ#7pTmy#CsVpr+ zDGWENDuie1q41SYAm`w9_K#JEd1X+5oih(z+>?fWRv@83v??)lLFA9xb|b>Rf=Et} z__nuNo^#_%w}@ng!JaT7izrkucA0+Of~I~Twa2rz<0Y|oi4c_twpF@$a+q``n+_3!7nuNe2gAo~# z4N0;)9t!Medp22*UrMx5)jxLR{R^kWRoIvEK)!lq4*#dT`MX-yf9j@^vHa^E4)|MX zLRw!>zoyb_T^MPAxKu|jG6Yi|DT_#XVv9mkk7y*pK=J6if*c$iSl%=4P66Ia?(D*8 zt;Llk63oJL1+(y!Q6(u7q)VpdyGkITrhK8MdhzIRrRjhcuB9J$D#spqnozx%u^w_I z6kepzAR~o|w$!%!KBXGyeaqabEyE7;Ba@sa_VXfZ_RT~dziK%OAo;$nE75OcqwPP4 zn5*PPsWjD75I{yE#io_TO`f; zF3^nM;R1T*G%S|K?zo5ewamIk4|cfVELZn`0Z;y??dnSQ?_dA5+R_n~aGgLb|5Iyo z#ly{m?ahqP*V_CxC~!@Uxx}T^#L?{Nk7fuTz95q4#oF26Ej!zsj$K0P1Y!&J0uB3N z&)w{a=_0b~FPfa|CLPz{%F|Yy6tMXF$I7g8xlOWRKJBM6mSDqNiY!BNbk4R=;ga*} z-tS3_iTZz=9&uc3c2axN!wUC*HdhpYfD7qjlYjvXBt}6+0`LEt;FZLpP)i0Q+gkAV z-?13Z@7Pz@&fZW8T*UhO^%bvT>W-?6=4G9-ZA9^Q#YZ<tA+2!p;R zA%s~v&vMa13Wdt5purbD1edDeGcu++EB%dYoUGTG_Z>&cu2jq<`5TI{mfO|abo&#o z-E?>3=ZD?;wbvHCIk>Mme64tPdnK(>9k%d;Sx`~4GcO~ zbvS!$)Vtg(SW|`7x*oA+&t9iboDoB@diW!Q3P6#_dYnZYcwPBGHjVX#P^{@Q?DQEM z>}(vbkN(7Y8=~oV!5hY8Y#RI;ABz#XJ1b3~Acexeo^RXw2v}P-e{8Iu z<+A{{d^<%PDhjK;=K*B(j5*CQDpcGJE3^A`=NVtRmb8|VP5Wga+3o@K>&En4t;cn% z%vP7MO0JkG#n+T?MCMHn!YBnRsbDP|gOMN!@|a-R14yEkCif6iq@U${TNiEJ-MC7_ z6zL1)I8D@@gg-jiVA z-n~n~an;ZD>XJ*SbE(Di#auR-eu#4wTQ+L9lX86(8743*bNlU^GW>;zY&Hy5$g5d3 z2O`$QDgb-MSOrR6?L-}IMV%$93}{5ha3nJsH0Yy7gHyt~AN)=!hIdkng_bFBRD3d% z=3=UW6n&qg^yw(EeX?Y#E^uahErxCaIW(DvVKCzMlhHVyj?v0UT@@*sQIg$@NfzJ2 zG+`)tin@Mu5VkaPs|A8(U+?po(D^d#P~P_Hs3!DJ%b7Q3s&s~s!z;v2F5;?_Fkwe* zn+vaaRzx@r6@oCJ)e?-gF)FIrwwX6=B~bCL5B&FIY#~8_VlHt>XwY!G*fm8HFO)Pv zU>3i9jr|w*;JV@P91u!e))9DGDC@FdIK+U@LIgTi5KS;oFgO?&qh~!LY}Rs%F>!T2YQLknt3}$Z@J$MZLaGVjqtcbbPulL~9=IU6{> zuDfN#H2xUKc-*_OVqGi~;=#)@j29~6idRi3mvuv-sPrZ` z)Lb!R8)~29VWdJlpi+;&wk{lM*rIOe5}g@8|K5;i%tDod&LRu5L4c6PVg6oIk{3Gs zTOSe=<9I=s`tyM4UTlN&GC|z9`<n{=PqJ>rW{-RK&21CA7hI02rkO17$|G4-Zujiq-nD*k#=#xi zs*$nW`o?KmKPSK00)mFKAyA2+Y5_gM+u#?;4#-E~#f!9#bs_AhQ{OolS2Bjb&zGNg zA3Ty<#(FY~?mPL$m-S>KtvJ8J5@*QW(GW+twaFO8Sz0et7GIEj3bnznr5VAlVjqV9 zEwLTPU5heUu~97qk!x~(yjfYx-yy6+zCh7}EjJ>}bw!yNKX9txy3w+4Iga<6o-_I& zcn@pMK-hb6FUWE{Qtv!A7O6UO(HP)3qV1A7g>|s`NPP2~EQ^ItDVxXkPO^DxT~7rf zk&vS>lOsQ|I-SizlV;?~Hn8c5+?C(#r_j-1C9c8)yC)ur0MRFUV zv3E=xVzB+Nax(Gg2zf7Bbq2aIaQK2ArH4#V+kU}!N${GiP_yv@4{z(86G?uh?`E-0 z$z)pW{6n7w9YY;;!eU#3uN3z_qY)-?A!>C%ON9DPT$6Z7d2@*4meb4{FdL=Kkhb;i zFV?W@DwNkG7;Uuv5pDe5>-g=J4xY7?0B4GSSV)cv(!X@sJzEb?fL*WP?|i5TH@`H0 z2|MgaEMa1zfv&Egpu*$1rfba+KON4?7wyr`b(h=hu(=V%F2)waYwuEWY%OwIDbMqI zxxa^R*Js72d+)NYGt-gm=Ln69AOIby5y!Nj6+(?kns_7E=@?moOF&cL8rQJD#b9-> z*6GU@I=g%+-xn4Mb#mtt^a*6wS~*mqVNG}u$rC5uKpmek2D4)3L6t!dim&GG+48GE z#SbYnWn&NogkHn9er-%o|B5y+$)L(}L?Izh7t48M3=);_XRO%~P2-m+oiS!k*x~A` z{W{R5lV~!eW?}p!w&z?oOTsnwnf^wYF(Y05(H z(XoA9!D=fmqGL&%d8PJkmSkQzQoG!SpAWa**~^S1okU(Qfg(tr-fhK=;D4T80Hn|9TtpQ zp&#a%3kPzoQoi^YN@ux)-G}EIs-ohp^K%J3*QXWZ_T?`WL4kS4J`aXVsDG@WvHyl5 z{&&^*|7Kzv5c~O8eYfvrN~PBdlR7@s*KPv6p1umLTs#_g^4+HmK}}?UuI^x75}f z#?GU*ko&gi81%_P>EH%{MLrwJAha^GejPDE?W#?zSzCM-?mS>wwtosYUD$qNU}m=O zdu{@qn9MO4J#tbIakbhKMsw$d|60WXjop(-Jh3Il8gMu<|7P~sa=*aryyZ|z2Xiv^ zGn=`)^yg1bNK}XMr!=ZAC(x!?orph6=Q(I*6kki~HXuW0hA9;%PqXN?@k)CVK%}F6 z(0#s znGaYMiE)~d-=yV0aQ8X~U=Cf=gErD*khk7+VC^slaei{DVWFAs{B+=ef?DW7yzlV9 z5s*`9D)R$czWd!`8pR>xQ#k8LZ0^VmyPc&;wdEc(7EgOSvda!B^FHI=OB`OeB@Qax zUV%8N=mBPLI+LBWZkD9R5lY{XXuqeR*(H)HSdQzlKi=pz^D9F1%W`Z2)YL%B(n8Di zUxU80uyOIM;9}LuANS?|Qfd5Oj9$ouT}&PR1?4>zHSMQVzyh5y*=ovosfZ>_K0Dd# z^GL5PeNf&&0DGo`S=cNCKh)UMuW@3c4fHGY?(+?N*5)2|xEOa*?{Aml)4CV-+>1M@ zRgUE(*gn@fJ~%pAsJ7RBdfaq=g(*LwHE{?W#ci-!2l=ExSz+HzxAVRwe@hFcqA0e} ziYAo`%-qmK<<65Pvz1CxqyPbPhs9%U`r=*J$ZTdi-+YkE)7c>PFxh|`Rc{EvHe^t1 z0J*{s4KDYU1dQ2?3Sj4B*DQMk>ymJ2dv(`09NU%as&;*J3l|1a!`0)R*e#uFs&Zyt z@iwD{d(TOo)_A2|}INScf8FzH^@f;+6PaOao>l~L}Qno}Sg$jh`ZP0*`FU@5nY8Z0)$GLH>510kch44Yc1;Z(Yp&qHrA?7RFAr zMKx<^<<&lY{~@Z?s%-H-t1uC0fIuIH;ry5R*GGDpeU~R zAy3z(!X82M1L^%a%ap7;$&L-z8~cF z*d}$Lq+t(~yET^0xp(3oAZJzYd8H6=Hc+>32eJyt;k`TXSbG$#7pGR*#3Cyh25dbM z_5JKlZgY-|S(t)G^dd)4&7yPcskQH3e?`y52OLVuMo%Zm9IZ=j$R-vS-E%#7M@l_! zD7U_4zRDl3#V5Pgw5+GpOmY3g1tm%bcet!mR(6$wLlG8hZvs z|Fp-uzemypNv1szzWjA)Lun1Z>46U{?;nltv42Y@|0;8UYt8>UsNvCLGGKe$-tPed zU=e2$7+;arS6Sg&Y!toSs0XjF+URLNfyY-Fdiy4Y^frHWV8RqxiK9_QC|Hh z&+l&#*0Gkj*&U}HEOLG7it|0{L=rwJ53Di4JL5+a>0!OFkN-5I~n*9il8%ta2 zu7}>^g1&gpR1Aauc?85au8aYMz;1&`Vh-H(UL3jiZUDBd>Do_zrzF#$%)9rCKfJW5 z@S^R)WKv~5D21_(23|<>28(KuO4F(l)5v<)QX`TjGm7y{K-ZF`q(V8v((7}*naLWb z34>wfie&b+=Q9ZvneE3+enj2;jA}CC2OBe@x;KWLM1H|!;qlkh_eWS-?q6209e->K z@c$ODKl0dZ?5y>{l$rk!Nd5;z2K?&NgAK$h>-77~KCfN-1XY`8a?X~Lh|MUM`Xbtp zCo#o~QBaGEOqVNg`1%sAiv}mrG|5G^li#*j5m=v&mA{&0I@Mk53JXjQ{Df#P4A7Yu zMGz)*y(5ofiiw{tl`n!a55;# zt6Y&Z)c!6~VXIGqDt5uR{s;YH~2O{+}`6v>0tkTz<5kF*1tm!@6XwQuR`0 z%n2$6WC=ob2;>~Z2{>v2LgTsMFbkuuqmmjg8pxLnfqi?vI2UJ9wPc=a-IMmDwMOTY z<@-yQ7x;FJ1g;&fRa-inmR+@NHJlElUOco7ZYsmfu(@mW$2A9yHP%bGJ zG}XBXEEScM{7tF5=yA&DxpgJb&6N80I6rYJl8w_`?i!lecbi^hKG%MnbzX{KM|ua? zl6S8PeaH3@URe_b#bZZlyg@S4f#pQajUjJtZJCSJ7Q`!nCt?JYbgP&m!+4QVZ0@N% zR=L3xg(33`9ro+t**Wj*7H`P5^DmRPpTm~h8@o(iV>%3;f%KA`G6X9@!NxBrKN!YO zrsv8#4(qYn@Y=@=9GR~FDtm&y6S_r#L!bG_(Em=w_}hF-PS?TYAJ_XdrDKIDu<(?m zq@chZ3WPj=G)X4dqR2~uAEGGYhyF*0^|iWg6o5G80`mh^8|0qU;XF2Bd!StlZioQ3 zLGLqXiu-A!@z{vbK}m)uXY-1JT)VeQ(Wwaggmif`RoMWd9#dm?FqQ( z9Cl9!+NVx7!>2#4G9-91z>?MIX$Y96J~DR{RML;s7{EmwFX|-_ClvAtVYc9h@((SZ zj0%~=XObE>&q}!RS>Ic`tY+Hn7Shjp(8?q~9ho&@=bGuu1 z(LO>-J{XuRA+?<8CVf398fj6o`w)#y0Fl60P;LTCE#T<{{6;xZMWj`XPq)~njFc{G z@pJ{|^_=RK?%W1BbQTc4^^|>V5n6n%6kk+v(e+yYRvw=DbTka`#FQEay77~bYEIHM zpW95jz)?4;NjsSCKMgzz%E}i2nFNy1gjZ??zbOcogrsK!<<7Bg5B!nMqTW~r_)!;PYyNx+5` zH^l>}o%;S)Mry1`>GuKe z1$WqX_fdc)#gtF=(XvK0`~Ff{9p(49%L@%hs*kggvPf7st7K5 zt2^n{xlgKb-*&}XJzlG{{S^#2QQsLl@WoOK{{DN5;dhPh|7=_RQd{~ZA4;b8OYH8S z|Cs37|6^?XZ=CQyAp*mMztry*^Lmw*9=xoxYT!UkO2{>GNFkcj&U~v{p~L1s}!|S-Pz8X0Bm@Bj2(Kq1+k9G=-!}kfCaSh zHa?xsa0dw-VBhYCzi#bv!zS+ubs1wzo;UfMYR!8|CaGsSizKmw(BnbuE~Wb;<0mgH znhrKL1LvFsqp!LJ80qk!O3?HsaQ}{kd$WzC*D639EvV^;a@6)V6}J)@TEi9uICYwN zCwM+)0_r>!#8OD32TdB6&!I$bOROR!&>{9yO^7)8*YEaGZ2A;Zas!#CmAOgTd6>@& zS-%2QB8!DsmjJbg^Bn1fpkCb6ohFUIrZkiK@&?bIClv-)kMVsPY%xVeo(RLwA^oLU zA#}XoygoAI!U;?1##Nyh(i+Hy0KY$nI0W+&-9j8jz<_K+J_%IVh8QfV4v`R7_LuPL z-93Ms41UIt`WJ3+InqG31jiTSkICbAQRROkscU6z^()4@1_u8&bNqXp)=)v(8Z4^p znUZ50+Y|+nd@+-NsF(_&`~Oh(j?sCpYZqu6+je8ywr$(Ct;V*E#RM~>({G&JG0u-CBO_0KCJ;TK9HKf;QlmaW~8JBZfFJrdJgD z+c+sh(uD>@kkz!Q3lI0v)@iw^8I*Yvwc;6*-lnNSUHn>SM!4m*dmx(!d=S?J%+L(g z?>>LlFb|r=!0wbbv>nPLrynw@B|=J+u$nIvFiXw9yw0iqHi$veS|YggSyAdoSpMDO zV4yL4F`CwQ0rGitZsp)cQ!a)@F;Y3Ax=)c&L-B`m8!|c4m{-J?x@ZnVm`CmjWkjJz z(te>!eygh0i%vu=(Fbb=#7I=IB#p8WAVl$!)n7X#f|?-Hj@HM45xL=*GVexhzj9Kl z`+%8Es?at+VW>hq=;m){j^1&X6D5xnU?2(|T%2RPHpCq}*xc(@^Wcqe5i(g)&rhDq zt(BHW^V0B#y!XH?o1^;4PAFP3*F``F>lTy=MJkZ)Ts->ruf2wS@5tTr6}aRHfJq%w z9Sd3-r9IeBdh|CZxx3t^qxKd$ePB4_YwwavyT&ux6Pi2XyJF!?gh=hLAWNn4`{fV{ z>do{1f@F96(O8In^Ui-8%WqY~Kc$}X-|Q5K>y0F{iu|A=B9Tc+NUvy!BHt03@ZnKW zcIFYJ@@(iOaNY0K*k6J4mg?vWnIh;o4`PAb76Rq>_08I!ww-2pI=o6YcDVGqKek3# ze|Y;S9qCT6pC2ZH7%My(;ZD6D9wv=II1Pf>uL0MEGwsycn;$zM0+%XiF6ag^#XhyC z4dHtqj36xC4}pXe$_^vd8jX!~wVx5}u$f>F9}x_>nM6M{g4yjJagL-IRm?!}0f_Ln zZ?XPGR0T#Uogv~S&3~GwtSkjulC`JOY8sLKSY5P@UKP2PrKyZ^O=X0B0z_y1GjoG_ z(FzEaXt}b(qPK#YV(65o%x#~?5uU6cGibv$YdxF^_e;s%d#hm#i9M{&ZDI8*vL8jS z-wHYQG|$JW#G?XQf-J^y&l1J)^JOMgGyplH_u|CvvZo7XL*>Zuvr5pG&rgHqv}xI? z;u0euL5>DP3r;iQ%dA36Dhrn;-FW7f_A<_wS}O4sj|Jxvm`G(AD;<7$t@$R@4#oO% zo;Mc}nt4*~*=(F|$1Z@uS<<9?1Fuzz`n7)0y_N;@8G`b88lJ|>7Ypp9`B@1*!_#Js zN_#yCnPMmq=hqNYOpmhlO5KJ}Rh;YdjV9?S{wcuA@izhoqv7ie$EsNN@ER4hVjI_@ z>d3PlrpLG;Tk@80G8uMkagS~kY7%Pmrj z;w{#qyKE+|pNVAOIup!`cS$e)_+isk$S2O~7igs;{>E<)d>L*7!scL!-F@$s^&>b% z*mMtFGl$Ht;DM)qOvc%U00DJg2*10(CvLTW?7GNdpFic}WkwNGFMgQ$dgTR2-`Ho;)~$AS5rI<$J^hi`Fr^qqL2$F?yVO=2IFEMQSn74pD{+1{WYjT%l0MxwbG#N z!P$To8D`|6WR8kV$d}LPFpexQJRW}-yJ3D zknOi<6}WL)Yu|gGemWe?s2r(vZvN?-Qqm!6!Fl`5fZFRjMrg>-u)-dDgyyn>-Fj44gB0T_Z4n)h#G_cp60ZP z8X;2aKYloO@bC+BlV$Vr;}PK24gT0{|J|+sO)(|pXy@dl`nPrcvr`hSw4n%~q0^zT zSs4CM><<+NQaPItQ^U_lW`!#*%H*TM{o03kt!o+P&iey?%{!_HWE?VCt+mDsEq zXD}(f%we`u|H!$<=j-zZrjJBNgb_n9Kr}%#Vw5t16N}D$X(6Ab4P&bydnN)n+8vl;hxg=K3> z#+26@@l-U|m+N$l`oBK60ZX_Jd=}t#8Ok2wOgBVU2RmO$NdvM532^=xz_*6O&6UywFz&a(>)~X}Z>z>@zU?)Y?>29OkT#lZeIW z;`%v9oO0cu((z(~H)Nb1AVy^7Cf@FZ;mG?Kc{Z~(Ot+=w0-73HbN7gbr!%e2yHvfr zD37=t{Ug5f*}twqP@$7@Ga$3+`D5+*dn){!!v*+PClkm2y4Qa-E|snUGf)Jd4SctI z_Fz@v3ZMv7Dd;R3s8uXvAz}uK)uO>*In!(H^FD`_%}d1lnt5TGc;WA+`}ihVHsOK< z{Inj4=^j5!U5_50azXq0JOG z>;lRLV?MqVuV%l%4`xJaYp_0uGK?ynk=AE}Oaj`_YPJ9AirzegAcfEvgJzn!`I)wb z`>Lf%?J>pPIQx@Emd6ym4tF+JBCm?|g-CPb1wq`hYZKOw7^E`!Azx#nEB6A)r3iG0CgYQrOI?}E^7D>6Od(4WcPflK zGiJ7(L^BNA)^7D!H)}*&`wgPUub?f4^qK-VoQ6v(_xUMbr&(E?oVjmH3i8I|jE#* zd2|b@u;arP<#T!kao&bI7RPnBBaS&d(}fZJyy>0a>k~2TYY=a+m$29z7oE3NGKEqW za8VAM6ZMc@HY-;Xunf+cpv1?@lQJldW+E+rkLMvXer}v9btr^krKdl=ta#3mB6o-c zK59Mf;^2bcmZ2(j++>GAs>s_FTj^MhV|as{ZPF@NcJ_e|@^lTSobHnVKE3LDNFOjU zTL;FA2Blw!#+l*QhO?u=H9`ZVN#Z8~I*sRF{4x<&T`{x(OWgmXL;ig#|3^!-vv)SN zu>Ox?S;#*d9Zl3+^)MIp|zCGUI}EF zF=XPf!}-S52dWi%cZ(?>&Qz8vs^4$Fon!i&rG2A6H5eag{EP_?&%=$y4;f^F@X8p` zb~+LlYq0sm5D^ip5rrTbFqYLA+#NE+)BoB(obOK6FMQ(KiihxZgL+}i)*2F7hilVN%a&cug&r!f&LwSUy%$g zoIsg1DMf2df%vjI49Rrn-3%yylQBKogf^Uh(UH)%fnr|QqepzzQ~cU13ZEq3d~i%r{Y)?UnTElvAx_ z7=0iW(QC(h4A5zyWX}VfkSVzv zU7qiOci^EAM%Gdn0m#jTWe*W4ZDqZ_l@0-Yc6lqMt>3z~pJ8fzBLZT-$5AyMSZf5% z>;+AH?fQkc308`KD*^=O(m&Q)f6%1*uP?GSk#(`Q{`!@Ds>x= zfo;f-%(iM7B`Fx9s84-iy3z*&*I=AZa<;O%W?5WU+jtXe9n}CGBJOmZ<)lYu*6q8^ z_u;}Q$S~b7q(HDvvd%V0zb`aI8jWLBqwKRhf+x#FN)*|5m3dco>QRXf*OwlGMF^9-eoj5DW*U-4GJGTI4wMF8kJ1$ z*26&W0S#h+wi?s*V-#%Z5DGTjA&-E=6`{ah4`-mF_t5DbKRpE=DOWS8F#;tG*d~XA z7^i-YZOC|J5(~sh+E|>MC&)ubdTx6liRrBL5sBL#3QdAfM~v*=M8#`x=~voA8St;A(#V0Rig z0LX1S*m2kM*90cxh)+fya9F2+=YMz0{%~0TH+(Ss(@Ocz);FIaUt?2qAUGn)=jK2L zU4SHnCzNWH`%ckDJ;@qPW^`GPqyQrKUzEQ4qz~$GAuV>tQ<)k^KaRga_hP!*T&$XJ zoG8ot>x6_j;<6~8ndb}4UOm2o`sCRP4{hRtL!+I26v*G`AhU1xMVb$5&yONx+Er}j zj!}{c3Txw)yC*12o@v{C3Cv?pQWC9YOEMfjE-zRcW>JAuB5Oks7&s2pDR$^x)F~0P zf35)+a3NGetw)?elXPy2%7NfiVlA{NvRhLm)s`C4*mno1O5P5pkR^KWbmUMEphV3W zne2i=sxmJVD<7qigY=A*$lKzVysZA-b0BXptIVDQJ|k=q@U=Ag(7d2IgKc#{j)@y;G(` zc<|9$sYgPVU?cQRE+`;>{^*;;o|#fWc*2{N&XLu?G;#4`WCGjYg*qdRfx$|DyH^lQ zLLDoVc~v7&09F=_0!9-(oUQl5G=MJVJ`Eb~!wA^xvK)wl=uN~o&8sp6b z*=D3LAZ%;!8mLz=Pk%?P@z}Im(MFZ*(fK)HVcW=kD%CU=w|zoVUbU-CkJ1eEQ@XjZ z+Nfz^AJ5DlYaac_oQP7eSm$ZH?Wt6x9hw^GCp&002a@r{iu)1j;qAUh2cyzSef-eA zVu)mvY@Kqi)oKWmr?1iMmq;3HVf`s5XwxQo-Hyv9oIXCWn62|V7~?-NQw;7`6zW8O zNX}U%rY%+!P}j7eju_%7jpq5|oB+^WC=U#}@!o+R=;$2vFi#*P5t@whKKw5!NTCk> z&eH3|H1CuwzLL1a_uyv|=gJnY?JvRB4>{Bxk5Q9E^Gn`@vQZuHpsZbB7=f5iPGK|( z*}Z>b-SimoI-&swJn~1U`MaX;-%TndBLi!LfA$p z7!()|_#f({vVdckF#)K%_z>L!#R&1f1<0y!^BSy@aX^a@yQc}q!!$`6R+lWap$QPT z)iBB=oj%xNv~TB{#{gv8D(^7|#yuDlV9vWE4iCMtMxE0VVY2;_8o|f7VGl#bfwRun z#LYbb!sTTl@uQ3`6~-A1i#_Y@P^3w*`O5w!ISS6_ywJ?>3Bun@a{BI?-*oUJoT*u2 z%>BVm-T)g$p3Fwbuy@vq%O{OeDe*K9x$>R_pWBPNIuW>nn-yqgvw>K29+8dj1!O;7 zDaZs>Zt=O74APPFz4xJhF#yAciSRPiKn{<_xdP3m zI@-J@%rbtod}oss$&$z{>qb*dW9^$Q@tlD)WlQEpc~fQrFt{;h+jpe|QyYRDYzW@9 zoD%GMrh>*-xq4DAecD07#5l)9!@I6+6;FX%hNPs9DO3u4!F38;{|QcF?*!P9GP?pn zUa1{v(vN4Sl7?1Cazw!Plm?T>-EQw?AIX-^pGTO#tIN6IT;s89QSa>wlP zJCdW~vn2MAT1d^nHwSMY))AodaPdJ9dPzQ}f@&k?L13WLlk%g|BML)rL-&7<6c85& z8)N&lo8b>1WXwoNK)d_J-vKlC!04kA5O3lwLIV)qXxaceV|PCfW@Iao5hOHYii6NF zl@Ru^Hl9Fk3oPlvU25o{R zTZV%&%n8TE#$=jw?-V;sj{zq0XfjouP5qMyGFBzICbJAn^)sGJ>u1 z{Umx{LloPG6~RGGK8|UxPb(-`6irm(wzLr+#170__92{gRjTco>MgQUI3RV)$ znMroUw55Y?hsoAua8k_~Ar~sU4cC61vBU)9!R|C z+7K#Jtlr!g33G`#FlG_5$}Yr@&O>cJ4JRb$g3ILRUai$8IF`R_)1C~#?g=d0RC9FB z+Z!fTB@pjWyODpKp@nR(*raVgL)dY#wkb&&=rQ?5I5u6CV6l7(9&;IcnA+U-O}-)A zt;qG^TY70yOnyg+=5}4xHPzcmjUR(cP4FklQ<`s>Gusp>U#Pjx@IW|n9UB%QM+)49 zziytQOS9nj=z3DI3=z7WK#}_l!LFDSG|!sbiYZSrX^IS`Ig!Y4s#)d~wTah@NQ0YXd!ET1vITqdbw30AKCkh*3!Z*$RP8?A>wQ7gEHCG> z=*~QEC)>*-Jk?$Dp5ZgPxWu@eYCQ0LPd`8A7Ol`vhtMsR&?ghUg0x z2A|;Aj5JpkrlpLGM`ot2%H|(vsGk~nmQ-7)KDX8$yIu-c5L>FuTB%T_K{Q3kb;?Ve z)wuKX)a+@w)trAg*4x`W`SgM%0fR8VviYr3z}JRYEGeI{K(an6j8RoImGOiMxk11W zGkfsM(+YBN7&%)Pd8+t1*;+Xa$;cxj&ONSS*&K;vn&bJ)-LK^D5j5Y38PM7}fcR7L zC-Pfh`oBc_|D9(4`vwr5ex(h7_~dF~Y~o1&uZ~-YlGWej>mi{j@|K98hvgroph4ul z0|*Ll%DIc621bax_J`t}=Ys|OP&p0Fj78s$qWs2=x0#tx&WsqhGPkmL%&u*p&S&89 zKThuE18CQoplm5yi+7s?xd6cU*1y=-!J*Jls7dD{wu#prBr+=I?~4&e4pG);69pB! zi)V8S;MJ62^tFc|z=|?L$I+mARw}`1zzJSB-Agzl4L#-t9YYhZ z&15=nc_J6436nFjf6uC~d#tN8)B>5nCnfdM2KCe|B5KbOr#7^J46LEN>Iyy|73~7Q zA_?0KH7zDZ%UA5>36(-5ie{|jh_?tyD-_cc&SV|lM)y^c(13*#P5lk*YJ2l}SpUiI zmJ+>aC9G4k+wx3fEX}#L@@`U_$e}d4D^gM-0&clkeeDOJF?5e@-NvduGqta%?Nvnt z0nTBo`?|^AHUP0&SCK@Wxig))MoDID4f!MQ1q`&A&&*Ty+Q%Nd=2O}on|w^y6y<9_ zR`?K0ETR+g`UrBy#%&5@rpCJ<2^YydHpJuL?{o8P$bQ0|0;R*eaqb)# z!mbrwww$mN($5OXMdjdhm4<^o%f{oM6e^L)f&LKr=uZ)5k`p&7Pt1sO^l{94k7*IB zJ(ie~STcBVXEbJ46GV^D9y(hngwC8#z`HaQI`=NbsOa0li1Owj1Lrpu=dMrE7ch}{ z+p!8HX)t`-&%YSg9S)2T;{gG<=8r8-oZs%i-?_1}fg`}w|4xp%9_&_l!N9xJL420NYKb`uIFJDsgW!K!y(=L^0nw@hX!i-P6M1||(AD0l6D z9n2CI1^XE6`Ln2U@hg%j*iiyd9%o&|ei71J(d#RcPgqRcV8Q(&600Z}DVQi2g1)hV zvA&r;SRIDCKQS^e)vt|53z)LbK)_#`2zdT?G532y0l1~^9{)`x|Nb~#@xN9TLU(MC zxb)&!?}1w^#>5cIDTM7o9rBR&5d?%O%r1&yyZU`G6py!-4ty@k7!e(UPNP zw71<{AJf;?wg6+VpC4`zT68uY>-74d&Zt+^s~ecrRW+8?GgKfJ&sGOLacQhq>L%(( z=1os)oi#jH(bTW;Va)nG=+nUGob7S2QDyKUQC`69KzJX1ZvDu`%<1!}Bt+#yT=o3o z;$2jz#G;c%pvu{9H`2$AK#q4Bj$#g@Laax<{dv9q!^!Ym>@BWhYK zpb5VzeO9B9(}M-Yo}+diqEgqz9dr6Cdx?yfMXr^ULUaLury;gcBAoAVI|TMXKs*s% zR8&tp!8?vrb$(t|*o-odPTNlQ6 zif3$h2GTh5efVAcTfwvro5tYUv79vyCMQ$V~Wlt#;aOyD!Po%RUq=Iyl% zSNZ-Dz$)-)lz5V9H{|lS3L{CeBHZ`4An@MYB2!|(3or`;4O<60?rArT*shHU7H-M; z7|eXftuyklV3>^1e51mZShl3CS|;vR#UrRf9yGGXln zcTxsg-#|CB_s*Xz-8MYU;2M$OqRL$;{}DRawYo88B$%YOLOcJxW{y!KakrUvx5>AU zp=wq9P^(|8ZGHZiH}i1WQ!fFWxZNL13ew-a*>Cz0e=RQoyxM;%$^@yuBmqZ$Ca(0h ziz9vP6XVy`Pb|~g`(gsv+kpCt5CM#C<%-+i&)9G^T{395Pj>S~g_j&Y#jGbDePa3` zum1CC`McaxjhE@m@wFWQ|6Vv%H|>yX6l9cW6ghBcJ{4GK8IW(9ALapVqTM3jmlbWY zSz8y;9_^)GzqcC`O=`@isT74J>PH{XRggwXj=qD=5xQhsBho+p=upGb(vNeH6P}8|mHf`xPR>M~8BnrJrAfU=BH?VK+ zxQd$OAoJN16=tP^{IpkrX|@*^SW10%FEqfVl0IpQSjk|#R-dJ7A)p3_Pa^3coEmPs z6-K1udxF8hT?~NNPngj663|OD$lE+JjxborAYhO|B)hGkX6348gAgXpdh1S*xUe+! zs_&a(nqND8BEp1#^?7FoPi--wUNQ=uPy9-5OfL$X$w8I=V2tD1fXpWiAB>Nf$1pX& zuk0IYNjEl6u$kQMR9z~3<0^MQBFz*ydwpu^VeE>bO4sSuK<*honx=cZW`S_DEWX|& zD=~Vq3avyz_}yTzja9Nlqw=f7$;C5pas>Zng6vk)P;GT`hGw_NcZR7Re6dm2BW0-?88yu=X3D&a zV8;s8rxSZ1JkcYK%oE9_GDdBE#PU1b;CxqonFlw8v4==DaawE&`8oX$m*FRKf^gv& zeoWbB=Nq=n$Txka=T&IC!SBmJgEF0UV4>T7{}oBR4kmF<0A{20$4K&j$8R?FcD5$A z&i@ty|FxjMB%Xi0`j4LhkjDZ6I4cG8T{I5!_Y>XYFlGl)Yp{?>_JjvP4gxkV7*FC% zLa7@DN90aLqQMW=Vi{TU!Xb6r6_TeH`!G>v$#!zJ4 zPjiY3I3S4i0LK<*noJs3J6JbdrC93l9^LYb9r7TaWDswefN&8&>C-n81oWv~{Gwa`@icd?SOLs&=dam|Rb$|WEa1d;0MGx< z1!aG`=zkoT+W!q?`Vac$zo+^4&eT65vVVzn|HVE!W;Hc2_$(;j36tP%AHFMrNQ}i@ z8IJV8+Yw{XePoGvt*~LZSe&uO|%-^SyOWn10AApC~LapoVdP3J@L@}jfe@C1h6S(SPOfk`GPQ*5ReH?i;N&>+z+sntOU zxh^}*rfDL}Gj@E$_FQ6IC}73@M)xAf)TrT71P(c-^l z`TzY53nv*98#_l2Is3m+O#iupiW9QffabBRAXtf#xS+@V$0YOM1b!-qpsqsNUcqZ?Ir;lCxfM1KD54-dMU%`l+HDd>p6)T$;|CapljQMQXCw?{i zhc=%vXq|@PFY%g!wU)8A-B^pcmdm%11cw6pJ49W97z*9?5UKk2a@S&`VM}$%wp=> z)vM>~p#Jh}@xgA-n>`U=5RQKg*pj~)#UJl6AiMhyt<$gB!GA9@!#|{2|LTY@2&USi zTU4M3fbywcfV=9D_~w&F>`UbjVj+8E>Hw6V00KMNBqufVAVK)yFQ)x}DL)Y}`P2Ed zWwrq*kEXZUoSuK2yM17rW42%FEeNmz`vPVOb_SLLqdhagPZ0|#old}RLl{&LjuQbI zRcO5jgGN1Ru--2+sa4BhlpmlGm_>s;L>Oq@^cbOEx4vG|u?Sgq!eL{V`Q5VEm0N_~ zM{y6WZ@|)u?lI{=qC885qP;U!SlNMs)dHnqw7GnCO?Z{3M7LIcSbHq2VbxVl5jgp5 zdB;<(2nGXHlm5vcx*88Ls%o4qO8+tVXlk|eBT1U2Lmz7bZmbAhIeO7j74O$)Mu7O(jyD)N>D7%J>}B4<3!6JFj?9Iu(Ypq)BB$8G6S^X=|M5C`};4J_|Ee(V{m}!75*{s z6Z~d=iY~UlS>QkO!-TEB(#4UVLM=2jR{1K_%Hy9mlSHytwICq_(&Cs|KkoUu;+`j6 zPMY$)x&!HfBVi-cxqrYLcQ<7qU1V`|djE^_B8$W6=;%1s>Vpq=m0?#z71oh_Ly7HF zAd4+7%r{!Rf>T3STy&kv_SN2qNSQ{=6i=?8x@YoM#@8mO!wM}mq%ip#gCxqrv8J~X z)HM2}02e2gbmQ_Hp)qhuiEvBg2aa2%A@~Av<$&7NTg2=nPp)Gav#t&g=)I1X?{+Gg zE%l|1l24dfLf>Q=TY=+GcLyT5Qw$vP8od)`yfJ&40$`%oQm`9f?Y0WH4*i(5_*gV> z#4|HE(G>>ek#ET}#j{E8J3TJtPZ056j3i~zrz5i`*z<9Oy%}#q<_n2X7wW=$=$)-8 zuCNpD5~cC6-gKku*=xd6naxCh_6khErKr+yooQPPx6Lm~Oqe6h5xgK?wimHhU-$@p zPe6l-9aD8YRuf#QRrXJbn3drs=cf`eM~?(8`^GkYveN8Z=yO&ly?*OjI>?t<@RcXG zV84n}iGABmxdv)KH*VVC`!AWfi{5D8zluomKb|?oZ)g60*>YqooSZCd&HfE8|I*DX zNw(;ynG0x%AO<5!3a~wE8WpJIG$$4L5_e9~3Naa{k#sZ&zo}RwQ zNufWpDw!^JNxJ3cuWjchdE}I6LJ4sulZ})ANXp-of+v{62YbNWw5LnTA)xDbv+r`Bz_bYAkwhj44d$1Ira|bIw2+BLM)txZbdZ3n`07R) z`i0kSqy_V6`mjHLGHLc~V0C*QV-m_}sSYN0sBKEv&d$B=gbDp#my@=WL8^@(|`F1bQB=UTL zS{RbbhpOo#xg^&gxqDqctq-6}Oxj#Rmqf)FwhEQAu=nJw0InI`PmbZXJq_Pcm!{dC zbKjMEPBQkd@c&97xf#rH>i~Ap@W;^e|7U&Y-}O&b9l*R2!#71xw%)pXV1#057z?^F z2}0AlC9IjhiPf9@jlM~f%^)CT%C>z?B*Jq~?*x}^`*9r8WO#Qps+f)afbQnA`vE&q zw;3`4iu39kx7W1O^p8&bii&a@AIRZd#yQ8489BFH1|8fpf7&V*8>r~Q=7N@+3S zx)7SYJdfISg4IkSa2AhnifEiIi5!1+IYDDVrMbLV8FJxl5S~)qB|Xy~V?cv|OVqBo zC{4qqg9as>eRkP#d;ko2>=kQ?OXYX98cjzL`#MunV3W~ATu(By1XZF7e-~<&=F~29 zWHXY!^8-F7u*VU`uiv>kXV$q%(Y^t6!?l5DqgW8s*t+u7Y`_ewP}h=3;2I^3ciQ3q^?*ZGjQ26U>pFG1AhJ>myP| zq^}wqP7yCM1L#>UP5X?fMOS&Jb9w%sA_S>YU94mUp8%4Go27I9ds%ASDeyCdHM>n7NG1 zN*h(7wP=&s2`bFS-DqZ_jg zs{K(ncEXrBT3t>uCQga3#fhogT<$ z-z=q2Ei@H?vqWnmT$%%9rx*3#*~p(f?7`#B#BcObOIJR9^T+Ze^_h5@{OZ%7N_(xJ zHt{Yfsp5VP-B?33pw%*09U3B6Z7neN%^vkF^Mv$C@cgb@)mV>eoQTVPl;~Yq)d>4! zmw`n$lKe`HdHldvc!2u_Xv_uX=7crD(h&Id9j)NIOqNvKrWLoskcTHJaUdll-@*Pj zpV%>Zro>Y37AtO&O~>tFu8I?q zjZV)Dr@M5ujxQ$_`;(kt*g=+P0=J&>rGrIEQ5kUk-=su5J4uQ2N+Y11zrppGbKei3 z1$u1PhgTo(4?5)1^OM27hR;Cc3T+RO`=ksFA(Uj|A&SG?>R>TkRDI|$#$-=>kKX9O zDDho4t8L*Bt%*4V_> z$m8ERXw|>#8o`^y)FGjU*p-b-O{5>&1L=n3EB2-eK3WEdM%b}+uo;}1x|ua>eC&C~ zzR&FGwTodaR!*PubsEMm&XFNR5Kpd~vYyWN+P-p{_Hez%_j|vi@^?@+Qja!>J}`>$ z!a2vub@U#-1szGn;r!h9iDMiGH3k+g(J1kN_cN*(&>H)+K68vN%*fp5c=m)=EGQZj zJ7R=BE6AHrccuUgM#w>il)Tjzq!UQku2#Tzw;(U^SD}F`sGZTVDAIeL9X49uPO)$jG zmZ3BE17@iW)EZ58m@dbO2bS&;FBL76&fblWiXsdOq7Nv9w5H8-vMj(&^Rf?kLha^3 zM=H$wQC~tAC&4!|uG|$XI7Rg(rs7z(m~?h-8!2@H@^OqiGT8L zpmzQ#{-rj5CkIc7i=OO^yK6Y5yVSaBjSM0z5>HPVn`&@4^R|?7bXrtwBBvSFTSI(-gqZRYd6W9gT zw@jFLi;*~(F3^RPNP#BeuH@O&Pirx(3o{=*+|_~iZRD`(D-%eU7Tp6bGgZoEIJD-~ zNrH{BIp^mnkQQQeSg8WY`q%q+yNTeE0JQgVvH<~f)KiscrYfO>^~mn+Sxf_hUMW8L zJEd)`(6N=Y(>3(u(XCc4`Y>-cnp3kR+AHg%W^+YX{+^UK*u)e1?t$jIHnx2*sU=aFTN(+|avcX{UvOH;fIuA=!_Ht0G zsT@7Vki}zsL!+>fXBiIMTsDqy^y=iLhj}+6jqqg?wk*O{F6E~IwM=;kBlF&{uZCwy z?PLvBVaHwWqFL+Wc%yguk(cs7pOy502NXIc?K?Iy=5*s-9GF8^2u%6`r3xzRA*8hm z#;^h3qC@Cd>5ot}mc}x0w1G6<-3-o9hIS$^ej8ykJ;u&&C!(^UYHB{kR?*8b-3_fl zZlhKiu3pYT!xnFxT(2N=Lro!{Tjg;)!cgA8#hm7})oAPCyPH2pa&!lM8&bY!(ESb< zjthg*BPJI?ra}1vL5KQ;s3j$$sKe!+e|7IzA5{E|7wTAtn?+SZ7;AqD$%%h($NM5Q zvx>Kvdus_!z7+e3elji!iGCNHIsr#`M6XpA;&cS@IBXdoE^zV$&aX#9ILDBvQ5WwW zck4$O!aBO&1l;w$psO)@(Au0%QN35|ozjL9XCKNs?P7Gx&tEbfrtrJdKLP3W=pTIx z`fn-rKl>GbS5w&o0=!!n=L! zwgJ@G-%O9Xl5>5(_lbR|bfb2eKb6%yE_#q{Uzczo!*!nT$iBx5*{+zqgyJciEfR#M zxUUtAC3n`&rwGAQIbjIFQ#*l%@=?eqLwTta@B%?{4zx~Kg*7-I8W4#n0z%8x*uyA>uA=vh-eNh?xvQji_ExpbK_ZE)#;TXP9R~nR4I|UKq_K zsX6vZzM|gAm-NOdndjaN@BjhFM?8E*GFM!a>HG?oK{i{k596&dle2~N=`Buy?)DIT-9Qi&uw96;t zpe*gFt`^LscUOf>EbY0hmiHgNGfOUzMh};36+)&vM7?71Pyp){s9D`7-SEhyn%|qO zf0Y6j?ZttB#bYAk+xK0rq>_zu=d?vjd`z#(su-FW5a|O z2{ny#yN<;U2$)JC4c8~YG2cLx3N1h+t(r^-oU>00VQiye=FXA?dl?YbQX`LVeBens zk!yYsD;#UKsZ&1&ZSMA1v?s@?JzPGF&YqdIB35F+E1$u(h%E|@Zsn*jup`Jqx~3jI zIEjFR)lgO0xCfwRa3Kz>k;ip*7Gtt|Rt6`e!+%74FK@16T2aW3FJdrm_i{0MPg&D0 zHzwa~X2Ofcyfq#bPvb>2uXD0#uOI`{L!*8XL*@q zqdq9!e7UwX2FAiR9X2T)JQyvYv088i7Y2+zwcAXjs=UUykw;S#w1h|^yQ9tAtj0E7 zgl3|xkW@!7+cMdgbTAjiR~604hYF?jz2$qd%ylHVA%l^=7B?QdwWxFanwMF=Q)iB9 zu&#FhTIryMx2@E{K z%391=&^WFlvm(D&BQ#jR`e{K<)AZSsBT42+k?$C?F>jojVjxSlh=Ie<`enz*^RqJp zgepJTZ8Gatf6h2;Z7P`#9Sz2H@V zC}iniqTBmb(e;jH5d&rXi{aV|Zdfo_v~WEUg0$$t=MXtM^6$rD@R-0}tHy2O>qg-N z;mzsyYU}`h0s}of9{@ca`7Lw)D1{V+_V8N=XLg&V~f*0#n(zbHb zqeB~n;c*C7rbWr}T~1^b9%-m!V)<%^vunvA!;aQs14N-7N-`*Lj~P$z)#PUSizp?| z*RA?;YF@zk`-1=mwGj~0W^3$P2unL=RCp{^GrS+l;kACm2_EF zLZXgZd}4x>7ItZg6s{WT1XJWa5;&4an;umXT?x`&(e8n6fdfY5crv#YC@q5V!i)rw zoI;RH{=|fWkg`vYd+G_Pi;j`#*D~?*eNyOMwq~s|oZNIm1u6kF-b0}%WkTLIkbiE9_?pxEUOce;fb+ClJ6N#I3dGg0|WQD1ifWRB!jXo zW1>uVP)5$pFr%SPxp*@gIg#V!1;$rp z9t_E=mZnjARotjy_?y=(&2rqYSWOc#?EepCXBia9nyu?@n#SFo#@*eaad&rjcW;Wu z-QC^Y-Jx)IcN%x+viHoKbLYf86LEjkpQ?zAsLYjLt;+9tpVc{pvvWHB;zxRcVYC|h z?$HM~_OVY&wrLsqS}3T-zH0qz%8t_YYwr$+58{pxc{ACbd8VVJUJ=R$;*u+q4Fpa+ zlNs#SN@)KQF@JYG7Q4!dlngSPbr5QQ9Yi`)xi_5lS7UR@!}bn@#BuMok=26Jb<)Wp zrVak<^>~U*)F;=R_K1lt$o=mc+p7YZ!W|sT3AnoKt0bXJlo#GZ3rrUqHh(Zl!?|2iz;31Yy28FWYebLuA4JfmHGKGUSH(` zGw)37VA@+}Ety2+)aVR5a{#AqOFf*det~Sv0*j*A4F_3GZ&lKt&~uz2fv&2#1Lxxc z)+$PxI8|&xTdbzzRNKrCaWn7db}~78Q0M1%W-t+X4lv)W^VII3$mk&>Z>l;KwQ|(n z6E}q_1eGEC_4%EfNALO8p`sVGNPSoQvh^am^lnWjZxGv?mjgb(p}QyZ{KD#yCVmYf zMY|UZvYNFjx+%+{nA;bN(<{ylO(JiPjLjzc2(DSTINNEA`x^ewao(W345238@>r@H zehG$4d2~RS^Tn{VdrZxCp!`D~H7l%qhwi|E7kIQH2pI-H@Gna3pzS~lnR`pu># zoRyX_~?5n59!=w5{PlgjBYgA9fYkC zC!mGkpm0uhD-+Vo0fy@(l0PPBK0H`j(tDI9EisMN{dT`lx^%!Hqy(riTd*On^ulD4 zsBS`OK)SD~W}&WTi=FQ|#Ng%ylXQ1{4(YX|$`Qi!>aovmbJ#JZoF#g1h;;Zgfatos z(VWuM#}qj!wZ$VUjmj+FW0fEdz1KYSDQtIF>J|v%P#hhR%zXXX-+Cl>|TgY=jvoW5|GU|jw&Nd^5-H7PTIPyZVs?X{QtuxCE zfuKB*tI_sqeIHHkRr8mgRfB*{Oh-0wA0!>4Ba~9nBJ5)+PV-j#6+nk}!!5k5)*DF( z{ex&Dp$f&)a^jiJB|++@lbVWEO-msHWl7O+Vz%w*`F_RGtM=LR(5UYBjRxv0yJ=&C zS8@_x-Z+xBjq5T!uZ@pb--7v1D!D%#gK{%Eh0yP{oloE5}> zD#Zh?a%Y-;65~Jz1`_779Kvykn63s52SNO5F8~C0HXZCU=!fncJcP5n{atSVVyv-? z))ee&Ur~B|=ZsVoG>?pLI}!qL-3^m23yoJK8PAZ&Z1z8(RHSbJ{N{~x z=2gI~@w2cgdRdBujErtMte3W@>3EUgb)POb^I%vg+_M_&3ayZqsR=g48OEXor85tm zJ21SK7^+=oZjU^32^R9QS#Ha?vL{xT!j4-7G*aXC@?)P{+kQ%{l#gwG+zpN9JX9d6mlAynF#2l|IoRKTYl;T~I517S5Zj%M{`P?Uo#4{sz#Mji<~&0TMa~%v z<%5_=Zc^tlJ)t3LQYY?#viChMmEK1-VbR{WIq~T{Z}st6c5bZ5;CD#SI-EhSoy!@F zEDE7ji`=JU{GGH;(-VEd&pMB0dPJoeSqvJ{8g1*oPU>_`Spv{L0u>JYkf-)RBO8r=J~;Do;oW&7Jg9LtL+ z^L=F5vJ_a(c%^rQfQ(zl53&H{2un<2!FWr+syZeb7b}JM-azq?C=ncmx zpL`)p8{AclAaRO;qK|zE$t0>C7hHRc8OO~g6E5j<8}&a+T>mFOX=7q;>g=HZH>XAXFSVqDv7@cCgQ4+1M8*Fs{RBmM z*-ypPJFOvAnKCPaFp5C&(pQCpxRLPB=2@wmonZT_QR|T8>}+NO>rRydo@l!N@-&Ju z6Aqv#T1a=emwt1{b;#9p_!338`Gx!E`Y%Jh`R-Id=Wp<^Rv31$R*4LeC5#futn%Hk zalM$x!b(y=Z8iqK(g620;J7vPXme5;2{cu5!{rw?nmN0moP)V`<}CtOe|8w*Y~%8^ zNIAk7ztz1Hb<(~*6(mvEzYdT)MCO!3;lF!&BL8fCl>|n;aROqvVa;^^nW+R9fj8Yln3x&9saz5N?Zu*iCHJV!Jl_%Fihy;#;V=bM-D=Fq?Sjrb<&m^-yh<11K#n@I8W%0K1?z4`Y_9s z?C~8@)(egpa7F!IcUCBt?it4LD!m8#d!8XlqvX$fPPTy*P+1Uys|1>a@TV~a$egAr zWLc(RWn|6I?htbm*oNDG53UV+Wbi-y+?8+tk95ZWht~WP2>WmA?0;p+BNRUuQ+b4U zxQ=pdGz#IlP>3Ty#~0|}Y?@RI+7NMb@ls;$M>`h|=CDy4N0-IyE(zFpA*B8B{!c~I zcfzFDlhLshj!mzd42~4tt}btI`Y?2;Lew$xWYy~ItrWl^lmrwI3QbYYjw=AW88xMi z5(7-B-;Rt?9W{hp%0P}}jR%zi#;iFC0EP?Cj1se`r>B%h%KL!Vi8dI`T~*Stz^aS&MzaOmV2p!Y>ogU>%> z1zMLc-eHbvi_7S4>fKU)Qk)Cn2EAkZl%AcK7=?%52V)OyRwLxDj~p^2b%!0=29IJ! zgjXB%xt=Q!9Ztx<#eUMZsi;|8rkl&wO#%B%-KNu@#*RCNW6TL zoKMbSk(xpqoKBb!Difhf4_OHFQf-A-#3)>42rd0BKRk&ZIuxwTws=3yQv+{Z8&6Fl zab?AbJxSX6)0$xRr!I7dsF*cgZdX9sP_S>IMBtE7+@7DSkux{vu8t7;86UmcYsfhf zed(Vkd2+n$HhTLn6?Vb5NuS|yfOstfeME0a-00QceF0A$v!BuD%INM&~w69ktf5Mn;o;wf6J_F*-I@=)mJ|C%~{sP*2>@9j} z{s9dpj*hlM3^K^%Pj!dq&KU=c3z>xTk9*@nnoD*#x)2}I33seivYyhXS$bG=esBHLw-?FT7bccuk_DfbjTP42mAnp~x(01sQ5=TuWNDOh& zPqKPv){#qP_o*mBOfi3HC<qtfx#9roCq+L$?h@UB=V$n*yEaN5nSOPxIMbVr}Phh!$tc_IU#>JNyPsnT=efr zrT^kq-HAVWGb3Y@&$*pO|IDZb{FPDrD~&>n+ZvCiS*#Qvr6G0vEiE>AiQi4YTcbQj z0>rj%)f}_-{1ar!d8G`2XNmnw3>4T#H66^FF|turGcrH3#(khU0&|H$eigsLGwkn3@Pvvli^uC&EvYA;_s+)+@=ynIn1gNfhV zT(7MPlriJb2wI52Im&@@rZ4S)H&VTir)z72p(EXizf(16dAGe~KId*b^Z=@E^9q_= zY|z6wsWK{AAwHci``vq%;(sSpTG_;F?L~(9o+i;A#>>I@_yZ@ZwruujAsCF$s_WgX ztArsL7x#OmSDC?SI0j5Rq9E^d1MV~u%~R<}_2r?n^`TZPfgppvp0iQ*@1jaymctxg zG##OZ6%;g>KeKKMt3kd2q!BUGGSemfyLrpl!@0jfgcFCUVj|JI0vJT@N$qRZdV0Nk zO%yeM)FvH3MA4)!F=ll^tq=NRUso}L`5-;isi$&g=?GY z>dL^v0?>8B+je2>H|U41h400u=Az?p?NgWRD(SWxN9Tk2#U!kVWnW$TslAZ9hyt8t z-#tRrtX*MO8LMjHQ7U3`=Fuu*a{5s#VsiG;D}rw7@n;0xOmo2%XGGQ9BV|O@f*b#? zKtvF;Pv_w0hXGh0<%9ThkLb+rfI)ckcG+D~=6COi5B^=gxyLBx_ryWp@~bS%*R0ep z(kPY&t)vnx@`WJ|(n=NviKHf$29cx(76zH52bKnYq!Ww%-zh)BfxZ>jo5in9lph&D z->PfA;@2q3k2v6_;?oP|M;-7Z_qwa}^;^P+^uDiZKrU1zR8A<4@?Pmr<$%IaZsnuW zVby?~P!MI5QiI==BBciLlv<_a3Ik@!;qn7&%HfIwZpz7}$=RWCN(}O-WMw-jpOEeV zp|V=3T6ri*DZ4bPq;icE>Zr1OUMOm5v9fRmrAle84r-ZFjRq=gsajrWL#bL(sEBg8 z>HwIsmD0d*=~8|umC_ZavX=^~YUvK8vX>01YS|8!vQ}B>A0_(^N*lR>h|*?pRJO7m zH08C@&{ZY-Hp+9kfsE2-NmOtp`zFeB`GJnoW?9sSvYoHW+{K|mN>|mCc=7{zrEXHF zQDwQBsItX7;>z3!;AQbxfC=uU+-hz!L*Fs~j#F881U=tTf4KVxZuX=%BKV)DS?<+Y z-pSxe)xP^Z#;2R^mzctn{#47N?w1_*S=`O3Z*1_D#`uRl&ZoZac04b{@HR$Wq>r$G zSjlXpu^-98c5nhzJ;tC9h}?1DvbC;|@6q6s)qKb}f^xt35%v(wK5q$X!zu#uGDuD* zwTZ|RxxCXw{qLT*0wsjqRu$LzaLzz?(5U0;SZ`DT^-95x+VCv_T-cgqJ!FA6IN1Is z5%tnO4FjFBaAG&LAd!KpJ`ZEk9vxg(@&eEo2-+X)*?l4fnnAM(We~igc1>_;Vhz$t zD?(@1CYw|GR4;IVTAd)1du`BxfsE%cOG5jSfGWz22J7^C#Kf#jbNuF&qYf&eL9Pqv6s zqaMBb4;3p3nnSi&LH1Vjw5s}ajO*dfPGNBY5Ju0i2wF zRGlCcBdV|K;SlegK?i=2PvyjQgzzuRq$oo{iXb>0SL>z}_610Uz@bx7K~4}a2_g`? zNd!s8Q-JPsYKho!-0eE-qk!BdQ7u(DJ(kx>1W_g2I5$hA2+}9%;q`a{zJo$Tq9Lk* zYC@($PLQ|>4Vm9*0yYs}Gq-pE50LLaFI55X2q*!+koS<&rR*qsP&u_Kq!zcaY6lx6 zYTyL3#l%6}{gTt>q!QkfpxeS1lbbhLx%5eIDmbp|y>c7s@fa6trxI45jQ+Va7* zG2oltHn~rPYl_ykG47h$W|df%u>*f8gmXn2)jmDs#L?TF*|xd2#y!{Po8Q)s-9 z3VK<7aB}-q3$M@+1c68D_Pe8^FYgo=v>;XwyJwe^yD%P63BiigjG#Gq%K$J6$(z@= zdz!@QM3wNgrNX+`$6LUOKcVOa8g(F;#2BJy%9~zwIuZmOug9yEUp^KjY2AH_4BUOX zjAu;PVg_OJOUlmJ`U)Vs-ZrOnOI4VpT!k$VeM;#;r?N%xO4{-S<@@nS&?5`sBXbE2 z=tA@oyQBkrAmIpZh4T|^fdYOc@skV_(L)R(eV#C=9%6t2U<6bV!UQ26^Od=CaZB<^ z#Gj6+Sl$D+6Of&lohgC+$O(x8k*W^J&d3t4_TQog)Im-VTq4vX z)FZkqC!q`L6>Q%S@PYX%vXAV)DOA8l^}+f`(>}G; zMCr@;Xbsc^>&lR2dc*~CfpsZ9vGzPbZm>RTzY_c7|7dZI%l3@-#d_7*zX0o!eJbd= zDBAYisn)!gT|qR4d_9`it>nR9xz_T_lLlIC7!ysWEJx! z5x6BU-Ak|1uUY)@l{T0kZ{knnJM}Kf-4&~@o&E7uI?XQWD1GVOx~>)dyM`CBUnPMT zFz?2^JzI2FAz;2KYdfejx2XF~V7>_&@1YJ$Te`|VhL?HydzPTOL@R%QNigq$*T(+7 z8IK!#c`)y=1)E!&`JLWjW}7OV=9d?8CvBjcL@XPHbE`m0&?@X#+}sX9jkn)f7MEF+ zbcB}`U>}$TJ~{1Hm%7&?{vV-xos!NUIbNe%_zL&B?ZJA?J?#D;nolu3e7X1Uf|t7c z-u}K)t*=qFS#s-!Ku=I#2GCtvL8+hE!6Rm-4Bv}VlS6l@B;r+J7^I&{31kWYnC)u_ zsAk&o0ffRV;DTd&9D!y#rWgQj&1loGisBNp10q;^Fb%Edk`ims)r2G%dr~@}FjMFTJ8E#MbnYmzJjFXJrE zfLXAJa*;pmng{^#C*75yjtP8Ng#AOC7L#bM}$6{nU}|$ELhGk8=B_uS?(kD``TlPtWK$+}V@+g?qkJ&$(mrLVHY43(4GY z7(JLd$V&}fQe;TtHM6_&1eV6Ly&~r8ULnN7aszwG@h>)o4&zx);ueGaiQq}yjtTU_ zlRSmsh)lTr2-zHKnD1QeIl+_IgSjK%Bes4E2b{eZdY7*aE$~@09+^JV$ zqb7OQ>+>Jf!MDmI6*BJ&2eT+4$P)~kVae|;o>7_>N=p^NK6>m|j*F3ecCe(MTET>J z|AvTz*Of)x#h!<>E6FtR)1s4HK3*t8F?CMr%tp3&#M7pTZ3@d)7YOg}>i)1x9hMz3 z>?cEmOU|9gJIlzplBuwX8NI;_{Zt6ki|+b|#Gjso!*qbxI;6MH}kUxNju-9;>2(3-~hbDBy5MZb!PsT9^}L?*;DM2feK$h(xIiME|8 zVR!|GLqjyWWCtV4N{A2yOR}>@&^S6jlB-kdaf??L@&=u&fTG+CAMpdLND+WhPL) zP;%QfNNIkrt7=#@opD51{e0$HL7)qMPP(Ucb9MqVQYg`wOQPxJqnl492+#QV+&Jbk zisGPPYAJHaCK=bOX1yloa8Dyi8+Q+^cG3eC*bG-%bgyZuSBFZP`@OHj(Q%hQ4okt! z&_eCpkiX+zfLXCjS(nz1+fxl6;5@Iui&h^f2VbN>cFdE#(K~h*$>dRM@g`Tc;O^?d z`4+=z8^ksh$Q4-G>Xu@4^rp{hBs-Wd@?J%{V^21bt#1&_lL*H;XHu(X^)9EW*3vb< zxqPrbcYI3sCRo8fHP{Cey(+dQ{n4~ji}Sno43!hWE`Yls%@{Pe$yF?|6o-{w#>G>C zcq-UfP`aL3U6Ube^m zj)A{K#rP-gfQjlGJ7D86husj>3N=cW3eqr#@rEJowG6VyMk)0n-AW_B#2erd9Sj^J zWW;+8@x7vd1kV%;Qs#2r9$3lQo@;a^2@$q|@EibjoN=)?LCY!WOEAc5`BsYaP|QD% zW>kT;NRQaJi^XYLU7zZ*b{W{W#EBPWK$yzNbbkB9a63gma`R*-E9tf(!=o`d=0%y6 z9zu6U&-}}Dfj_hKlsknk6AixP=P4H`-m9NgeOuKPs)) z5e4dS@I!owMqI>XE2}fp2O&#-=CWYVkXh1^O~SB7NUXQRF6}?$+g5j>^M```K$xCx zYo)@EeFQvG$*kG#a5;-_XnQVbr>VbC6d<6q2sBm@Dfo* zQX$l-zRblmRk#N{p^N`~3NTHa)awRbexSl!Q>HB}T3Kmy=rdJvTshsjMAaRy$+z(A zStYx~tJv7ydG_jW%4D(C8;Z=kT)Yp%Srvx*DFmEdl~PKfDdj|zPL-9u_AGR$X^ra{ zIV-@X?hV_MfBYtR8pP85W(bU`L`pkJk~6QW|KM z^QbUTD(jq$T31zFj*Im>$gh^NRNJg?G4Vc=z{zIOtln(Y$y*S|)E?o%sDW#O`@^-W z*`71LhXex!&ix}_r#z3(ljrQ)@s%l^>L7HeK&86l!m=QThL7Q4!s8c=Sg9(+$Uzy_ z33|?(Z;RKmpZL7q@F6;wai4ojs%LxL67UTYnivlsY&iPh6y~YS@>rOhzs#-~@p~o@%R0*iG2%kZFfoH9F-X(t5~R4MXCWhEo~w?=9?Oc@Y;)G?O|_sy zwJ`?j_B3$z!6u;uH+5kdA%>>A1+8P8#=DE)VY@NNeUde^yn!og5t=IHmc;`?ijqcx z!3M$YS8?J=QZ|DFOZluM`IuHZ!arTh8Q1qLIJdpN7V+m^&sgPPm~ z6&39jDnhORYA*wjiaT-xVY|)>z%YaTC99u3)8cc)>K=;KOmim%zXAuXLbFGiJvh^U zku{dvcJGf?2bD>9pgAn7bGNYP@a_^(a$%!a)tMrWktPe$qtl$2TbAWKp5_X92w{ix z*C*sei-m}XU&ll*buPn*H|eqpI-oE!WIg>%5a^_?Yci}o$eghInekIL+tS6LGHwPJ zw}we2oeDjq7&g#S>ajrNmL{cbmdkU2&=wl5ldL1Jbf{BMgw0Oj{xxz4Mk`*!Ztuhn z(u$1UI2xs_-R!sgMB1YXOn3xu*JeIQb)y#nJ`GjL#<{WLldxP$|BLmWayd?a-y?r5j!Oz2#dz;S73 zmI6COLG(c|d)dg!0JT{{+qV&-7~Vi~AHGa{d!#0=2biT+=wtIXMi^)@DJx*(?+yYd z^fh&|VRI(a@0`ZiHQ~|J6U^~#btn?5-y%g26=vwbYoNs%);Zu6v42v%?OA+JmPvP? zCP03oZ97w{VvDII*}^GbQDB*_p`Iu{hx*RNg>_=5=Hl5)W&IMiE^w_;!-dpy1+xFUzc)$TGygKna*?B563NnrcBn)-BTMGC8gDTqB6 zGbCcWlH+IA4%RWQ&Pb-j-Upb{HB&Z_%nwMrh`{bWR#iNE+tSQ@A}5^k;uHw>&FKZj z*NVUU*y^bovEepx6L3a7+P5j7-Wx=o&)AJ!Vi;kkHz{H$tQtmeQl#czUy`i`KN_J! z6O+_HSe$tpngFdg!yIufLw==KJ$=1!hs8MO@lzQy?{T9ZgXOGSV!7=0Yn$x({F_rG zdyhUo_PMpu6{x^88%OT1803ODsW$3sTA*^iU3w%tb3@~ua6}giS`HNc^sHK zG8Q|hx-Ap^3^>;`WCPqTXd#dcdf$h zo}@O~vr}s%d9-W;%V$eVI_PHJUe zo3w!G-NJ!XvmYZ(&}nmubRZGbl@*lmY8&58FY`bzic&r~KMC!2nAhPmFW zwoexu#}-(>%iv>(B?>KZjq?f~^tG|AQGj=jfWsAe>U(#zRCp7&0MeDxOtoU7`>Mpj zxH^4ZgEO87zkGT0`479~)0ZDiIDBnz+~&$HPMb=>&LAxo{#a*K2s+)Z zCEKf$Wqc9}r8_a=Jbe$0h9B4dTr7Ltxm;JcL$Ea11GGsMBfawaJiY4|`mN}O00yQ( zSz5Btd1>`KU-4POKnuEd#0in|^W1ER&189Kkis)q-SYiqSTg()x8BEl9yy|RdxP#y z-7-Y+VIGa3oblHYZTUOyZoFdF8DtG#Fw>=y21!s3B~Mp@u}Hez8|yoICe%fuEa}Sk zup8njK-U48yg2YggQ|OHlFi=8kw@kUy&XODtH>?t#mgi6keP&+5_!g(;oCI#di|3Q zuTWO;rt6~&NDoD5>5$}?D58u`wI2m_ z;4~RbX2)9&hp-x)`0yahl8QFvCE$Vw1Ml&0KVmkwvR~R6%-KJmca-8M{1g`;k<{_a6-Z^T>G<-; zSlfQM%x68JQ>8B_zFzJZ@0CrUD0|IE+($?Qcp2YfU{oiZ6u{m=Ax(NBs*%22_Xzt2 zhf|=x6_ER&KJO|%&*+{3FX-I!sBL#lr;^_BG<}jdHVY=2!m=VaQE$Pg;)fCeKtw$+ zjxSy4N*I0-q6Prst)jLWaYDPV?yIQad@4KN3O)$?F zyUkc5^5m9aXQhdtx;VeY1q-WaAEjPs>}mW`e%WeLYr=+l&RMs%=&z5-=z~COznj288qN31tLT@g*zm3Gu{wG# zJ1-F|mXoKM8~_ipf@@*Hv$Me?ncHMe4AI+B@*es+YJ0Wi8%yxd`|3+#eZb-Bib3M* zoSG6$PnJ}vbQjzip=|X(SX-pYHlaUUG{4V%))*ZZ3Ey zig717Q{Jgj(d{&O+7WTqxuL2m4xlPYoUi%zK+vup8wTr_L@gg?#j7&)%X81QsxHVH zG&NBx zSPcAj2BN5q(|y+qkaL5X@Z(0c>Hpw=FGc_jY%!Dr{eJwNl`EL0>MQmAQ3bP+Cj0{e8#PvUR+OE%9k)stnFG_Chh}_;7%N3dZ)CTVM^eu zXxY5kaux;M3KQx%NaV*iHN3PE4t7O-4|TpZ!0Zl~tD!nw8!41;RdBDaJxUj;D1&IE zf!AIq8a9U-MA3k#4V1-~>UqqT3U&w6?D;i%$i^O}B{-7m1QGB-z%|2IB4v^J6V?8A z(NW1C;9+0ThU%5XM=6T;8Jm=%rR`Afd*R`G{wN5rDEBPxLQM#u8bYA~BeV9&-9F`K zKq;UXhH4?ws{SJ`QNijtZ4o~N3w!W)Db_5il!IP!`xJ-a5)Z~VV_qM}>Xo$P@*Dml z-_afuW62ne;5qowTq|q7wQbr9<0c#va8&lOOw;O4a5y&AnRK`|bCJbn8W@Fzb-Uky z`QE%KvM$kVNuIIJ?C$j`T%yaqa#24bIothyF+v$&M2y1@F^dQ=B91w<%tVG-V!jPK z&k7ZFPg!W$LgFadEL(_|bV06{{wA{5NRbZ;dHgkxKC-u=47_tnN*>scF+X&`hYt^Z zCC~XC{A)Jc$u_Q8_r;&)=ryQ?RN4X!{6f}+8J`(8B7$SDd`^X=)H z0tn{YR7-eZaohoXS&a`bTqg{!m_E_x7j5Y=KanBc-)hQPTPIfyg+#4M$o26~v9u=< z5xQ!1Z1e|&@h-2JD={N=2Oi2-UL>?^pg5t@duY_*0()UmWBmx{@n#egE zPHmhI)*6PYP4DeCBw|iAP!qScTA2%%hr<|_48l$pO&WK zlME47Pbed6O^`bt=)Xw`FJsasH7rsrFkT@5>;a z$9`++y7`D+21kwfD-?!94I*nYoMZNFu~kUza%`DMOfiCQ91T{`>O>w}dgX37s8RfO zkP@hJ5-Rc(%6f~zy{pZ}zdqDO*#6K@OGM!zY!XuOoHU9EFZU&u;S<`-q8e9378iM{ zSFB>;8as<_iCcz;lq87$FpD_zvrRQDw{P6o&EH@-U@v(fbSzJIsT-@gayN}|MJX6` zjg^QKYq^q<9kW?79n6;smgFb_@6?y6CS6e=m7pja3sC?v-Vjq5`?)Lp1dgQS-*zXB z$K_e+{!FADrI}@}AmlhozDpIp{Zzv-OryY0|H_Ir(w$MN9e~wDk(F;KzvmTRS!K2} z6^nHk=f1`&TS}dSmp<2djAEHjHd(>7_CK9hkn_%idR2sOl-E}W2;@(LNX?@ z!*@?y)e5F6X(`6nI5VP4TN#C!C5!}VYG4Ll!~Yu6GNWx!QDfmjux3>i!i2k=;cYC( z$Foag={U{oD_8aNQ45>-_&j2Ynsiui)|4E$ae}eD8~l@Yf*Tb3*fUDE&+ywNMhpI6 zqo>_jZ{SU?m3BM}p5M*Owt&*p2HvAVyfw#|&Q>$nSzY1-zipkBLrcr$celJK?dhRJ z1Ctslnjv$0rII{IdC@*^%7VRa*(4vgQV#7_-4I0`i$wN)CZi`9$69JSS0L4xgZle# zWpfp=!{&LZ-#%r)a^k3#xi>_p=CJB#yHN=)ZP4v z#skh0$8vWWpy9Zy0?h(QiUq4_n&X)IftF(?$EMcQ&;;?dAZXNWEqF!BdUk4DMaqFx z*;u;JH}%jL8`I0tF$GKU-mlzd zqnvX%%2C(phstAk-Wt^fc`BUU(#ADPE-QJ_&V#s&N1K3@2T)B7S{gMQrA$eZRL>O& zGeGA93G;np8F!xNAnndl{PSLsG3Aa^R?CKv72X|I)rVT}Tq`Fgb76lOfqt!-A=+B3 zV8IE}xd93fpjB`!gfwQTwn+z*?NYLdV~IJgNtUTzCYo{O!IEF~ATI*%IhsicM_v-o zEbOjP4|CfWMa}{<-0)smvu*Qpe|Rx$Py9zCe6Ejd&kpVPn_3|GD?o)f)YUe%MgEZj z?*~l-cxKr6Yt4Ompl<&m^91sa7Z(M$9kr0Us=TbjPIx}qkT%#m8B2yqh`=4=fc-64 z%!}Q*W6R_=spL59{syRX3tLI5M4Q{#XjMmGRnI!aFL>1ho)!X#eHQ`t2gYDMSl{#U zsZ7r=AT4~q_0GGpzgcFKoggTP3aGIV|@=!v`&h4&ehi7hGFP? z6^9tXt&K)bZh&OyWMh=M$>`NAivAl z{u}B&38dt05WG5q5fT$Uy)p6}BdCCQ1k7oOx2kiX_JRC14iX8Ed3N>5hi@;TePnXA zXb=%~8`AP~?1TABEO{6^PjtkKp<7Zp3bHD@k-b0Itd&jlLfXe@fVEm6aAVCr;&;!} zm7x>E;TqQ*ZXQ)pvRP2xdAwmY`9v+2_L2P>^m?|}=~}FlzR=v2rw5;vbCRl3YA?`H zGc{##NNEEae>M{yo|2<-TK>`bwA-m^wg2W7_qyBY;J%ONyjCK24b%9~=y7pTvc*oM zU3ojcJC>9i7+UY4J`2w8yG!n)TiGs73n_4dDsj>HyS_IkeYXew`*e_F^a#E{`5h#m zQbluAl^oB+SUk6umi8!ZuP>zW%d5RZi)<p&E55QB((4>h)zR(T z*JgP2Ob5-DpyjY(Ko(JKfP!uboc)3~w? zH(+<$offY@Y;4;5bJAoQ#jK%G>k9MG*10E~&)~GPm@?a5^MS~+6>&J<7s?ATf?TUa za~CStJisKL9Ny3GSv+Gp#{9caG>Q$(FW3BWN#LrfsqNg&81SLUxtBziN~gVgC}(S; z9IBwgZ`H^pMXeRn*TJa4wU`G-7?$#<&4iPGL)rX#!YNE2zwVkwoY?++Zed9)*G=+IC4IJ@T zH%@Dxml}QCFjm|6fM^eWg2}CPK(xCazJnnrT&7fNO^L(|ncUD=q4|eyS9zsQ8cN+z zEaL-7d3S@(Y1jguOYc4MY7QuzNC0u97Vjp8AdR)gfK{Bu(KYY#5`4Jk@mw7jE(g)&1M*cjP+Gfve3KxQY6Dh>gjqy8 z<`hv^ZX>BR|DjgWSnw#+y<)?0hP_6^HUZa+9m>A<){1ohYo*e`iJ}tM!<(EhWXB+K zOFxqb(R$=fdNi#VpleA^Ib2d1{b-aqml!+!)nBO_e|cWEp?9&DB=C}RfX${KmZ7Cj zWEB;Ap^l%7@nK+iBSq?)VWdk6M1$u8PQ#KHOMPo^DeV{FmDTVSwQnQ;+s{m@L)6D`l*doY=V&ABmhEmsws_Nek(7 zw9_IG99JzwTjlpXYMYnn1eN}!eK|}K8Mg-;@_1K#@6(^*x5n%AtSUHR6^!|;NvkFO ze*!vAmSsuEzHUb5sZs^alDw5<4kisEJ3m%kcjgJgCQj!CEyK6TiKVn54k#&YrPEgN zLnfe_hjFjMVR0ctEy3B<)al`HVN0Ull*l+fa7<-~iZ0PffqPjORq{;{n)!TJAwxRK_(BMpJJ{G&i+PL0j{Ti>HE@ z5-C(o@b6v_ozGrcd@s_jq^X{);k|;-PHw2wtayn`$c;6Sc5u)Hha@vPNgscM$)jG6 zhR)<)zQmgSr@#`+zk!SY7Fhakuj;=KoBs#bnDDQ#(&~(JOd2|Wd>FKirXEd&7B$xA zxVu_8WTl;5C(M&Am!{=&q+3;r5PoI`-j~m?l7s*P)}H>P(Zt=$1V>$$kMGM@J+vt1 zJ!bByzv4=tVWl9fYpg^hz2;1QY#ZV!zcffQj5%>vNl~gmCq|efGtn-Y1%2F%W)nRS z*LZqZDWU=)jSz}&5B974VB-Fik(k2BZ3tciWuv8HAXv9_3`G^Sy;j;p&q%e0A&IU zlu?E(P;^5+9af|tSz;3vCin$ZD#}LhI2v?Bh}W@XI8Op{LW7)t2&od-6?hwoET`>b zKea#U`=@gfG1^doVOr4lLh2y`Rgu{}-gU#xH8lMQlqxrj`7x;s%c0m?N zz<0$QraMBI^~l=+3oVL}%ZA==`lWh*;0rNp9P=*vmAs6gG-R~ACIzqWZaY2ZZNm>Z z?HX#-a2K5l4}46Rhkgt*d`c4$aUEj}=o5OAC=aWNo3&R1)+Cn0$Dg!qHAjq286aa* zu5P+{kdCrGocY`iU%>;X!_@G|APxCqQT6#~=5wRlrmqU8(aLC(OQ8?=4A;VWsy6m> ztJvY0Ezv91NL2l{M@Wd?PyQg&n;yej-X39U9Q{$IOZ6e7;pwPm_?ar+ih49TC1T2% z@E)=-pP)^=;A(H|{mjL=DSeh_i~fw)`e2tZ27biDzi8@US}o=>1ql(0jA2cTQ)EEn zbW(Dwic#Wy>%}qTEAq9c!mi`;+m2lttixBH=aXqk#Cur0>xHL&W}$s20(6es46++DDw5U-zbQkl004a5 z_#_+l4+5px@Q&weQJ)m` zbDYfz0R)v$7jFvsYUBB6vOss*Q%Abq>bs~y80^XZ-eN_DGt@3qyjzUi5DII8$ovbG z%Q)nmA{a?0`3rJOO9MueBPCCn-w9eH4+)PGA2hOxDgljiG84kj#n~@!rBW z`y9P+T!Nkt$RLcSYVZ8wfq#B~Ro_C@M_0A*=qeP=Tc3mwISl4~FL)138feJ+i@%nc z*TT*C%iFeh!AoLo3O1;Vtwpk-HV0g8#IiB+zW~Q|8 z`Fg)YZP6}(tU*+x=y7yQ{7U<(Qp6CFLW$8!2^3E5whESPMlyhFKvN<1tE|9wh^>v`hLs z@rir6shBZT-VGyiR%SQy4OG$2>Uf4`4Qp8oWv{s>H=^xyn~Mk+RXDBlt6@fc@eDRi zm;Dk$H_orbDX)KK=5VxX$YP8zJ-Jw!f?`#r;{>~L)l6K@oq4ddzmfLM1o&X$X{$vM zRzloS0wZJs8ce05o{6w#*dI`r@?j}v0rA}@;|5G-2@-c;l{qp;+!;2C6;dq==U$@5 z7vBKr*jaMkq6mE;15n0#X*)PrhBMxT3FLH6K6(exOWPEMoef<0i1o4zZ=D#|=&mt@ z%MizI&T8$i*nibbN03lv=V#3<{>Pg6cRSfX=K}oW$IV(!TC$HH!8@%mL^|)Sn#b*E zKoP4aTXjHE0097ndXd%~SaD2i#X68xkPU(NSI)*>;X8byv5D!1Cs)++WYq)wmm!Q7 zjK`%rhLh~?d|&+0bHikU2|Qr(%u;C|L&-=~g&CVfN_T$epi0|f4o>@4_H-zgk^91NAShnzq6}x>HTl#8<*y6 zEaK-`4fuTh?+IwOf4dX^r@a4PYq6uT!)H?=;bJY|VCqQxPi?!ig1}!l-h1IpC3RVS z@DC6|dIlOKwg95R{A`@z1w1Ic=1<8oZf%CSi7ED#Y6&<2POzzO{FU_|zh7Gahp(@S zifikZ4jQBh!5tFZ-QC?GxI^Ragy7J)ySux)ySux)yM+9lPwpM(ALE{f%>(_m_gYhG z)~wnXecgC$f0WsHdv|a`(1Sn4N9Un-Q+v4T=`9JYmxx1p(sSK21XtpXkA&dvcJy`| zgHr33_D(Ef;4Wh0WoRoAGch3VQ9jaH3Z69DamIG}4k}~|);5X1IA@N)Iyf0*du|=b zQ#3_Z-k>aB;{pZqI1pkL5asU=wh>6Vlkdbu^&d&0x6-o?1ZbND&WI8&4x5;99Jf0k z{#Lm2VH%4}E${yJBiMH2h)tbPv3tu!#u}x$GQgj0`|~R} zAu=@5u0DScxwRDLpbRXExTHAI_ib7u*vGGlwgha%+*|t4jl^U2XWtb8GWOK1`5p3T zy(-2CN|}15zY&s4>a(d28Xd<;nnWXb?su3#63>%)Hzixx1v$$JDz>=m_VHonDdrgs zbbC0lleve{@JeEPe<+qHO&*!&mE)Iv^P53dAIqRP^Mx$4Re(o)A?+4MB*ur9iK!$O zY97_4;nNAmLm-T?m6f|q*RDQ{DoncXJ~meiBaqXWc_j>(r^T%^!Rl(2n4mW7k% ztl(%lIpOf=Oxvi-ZpE6fq49g53WMZPXja&-cYL7B3o{?Z)R4{Iml@okqdI;md?cRU zQzZ)KT0I2`#Wr*{32$8DT#&l>wpG-GLjFkYpVC1Pc}B~`C_9>hN_8kmR~=_;7c(X= z{0b_{{xxUSw%pU^QRF7n#$$?F)Q{R{>Jp?v!+ewJ3R&&{0dI*g-K*!=>p`;IDg4~Q z0Ut($VZt3i8Ah(aMLx-81DhL6%R_M?U+{*4mCgLxMp%?E(EX%wVUZJ5K$xOx)>en@ z6$qWe+#ygEv|#t$2^YPiE0cQXME~T8X&?Sb>?BW`Z5{4I-Vz?xgSa-+0Fv?Nk#Zju z0r;x~ypL`$FR7vvftMtuxu^s@#wrrscT_KU3Qzx*dZM|?_$d{^QFmrr#3a?I)z5Xa zy>kLXNn}QQ|GFC0yD2<>`oJFKR6(cR*UNcEK9drAIO z;d@E()a>lOYv0in+W_RG4L^QQTy;@cvBvjGWD){=Ii$(H38{-o`-9W8HnKUwn= zDn{n^POa82*o^HDB3P|8q#4(U&F?tHs&Y|O2;wb2g%qHUnrroNo%Fd!<<5-=S zexcjMBehAu7{YWm6l}F=yiE^l%`&{u>~ftxj@_hILvXfHI@?o9?DRUA`9PZ*irS7$ zE9+tY+O7r1ruedP)ZBg=kS-aY=CZ6lt%m1EuMSQ#&8*&}(5y=>#b$_{t2Jw@-V#Tw zAdf19TCbv{$HX0VZ^OlP-X`vkdI5{_wth1zspbpGE#u&R`&}h;yPLUU=T_H>l!>%w z<#ObUpl{of;rm?t_funMa<|4FinT-$V*LlZ6pJK9r_`EM9FtphiwJa5g%gdQ9oT(_ zW0=Xgm^fHa%%&cK zHRs2TIZbLYCKA25KCY5}##@DFwphkxQY$Sr1`KMj*1pF@KWN*e@4l3TcqR8Wym4fC zWqQU=X@-9#QZHFW=!}zO>8*=)m?@nnk1#D4l&5Ku$Y^%nAGkB}58+F^sdS@4p5tIU z%$MQaLQXmSMdXpHk*_s^$L8(-fL%!U{caBvW4CWqZdEHVldc59hTX~=h#I>K{f8fMJ7}onN|y|MFzY~TpF{E&gd);X}IsR*Vr7Z zh2N+O_!QRJDY4K;!qIDB*7RtiwM#d?q|f@bj~!UPwpei7)ugngvNCn3t+Y7Qu1d%A zEQyZP6>H-ehO6Od=`2;57>k%uSVG`ImQ%A#hHV$BlJQLDHyYeFsKjSoc8pLQpCIMM zZWbn!0L~UK++(kd3lo(;jG|%5PLhHp`RNXy7E-V}<3?WaISPv$I5_Sj&suj9zGCwc zP^Rs)?h{(b&C1;PH{oQSY1&3(u+?ZI%^#*FH{XL^V4{bio2qv?WNHfDpUG zeLh_T#-AnMjRfa=TSzhM%BO0j;lGY+p#TCQg_yXAF~2~+qdU`IHAYk|$lMXLUBOsT zY$<3HDs*W!EDMrog4sxs-j&O<%|cCX3v(?($^{0^PH+mRHFRwbuZ$$)FW}r8N1Ht+ z+|e!I8TB-gVn)C~4@AI#D!Mj%TSfq4_YiJr8e_YA2D_!Ea;!%;YaVr|3@UxkgtR0m z9-1?hrzljdD8IE(#vpu~GZA18HoCmR?h}$DPi5TFX!Qj5FqUIl)QN-mhFH4o6{`R> zvA+AlTYUYpaq2lQ-P}2Z(c3g{u?U}i475;C3!y~9($P_ce7yV-px&pESWDPYuUr}J zjNvD%m;|r}n@GRz`s#;;!-HJ6_(pd~IxA4G7q~?+#FoUt5^pgz<}^cH%Yueq+2hz7 zgFA@fYta{me=b#Xi=roYHS~#$w#=#iRB$E2j}5vJJ#tIQ4t$F})@5rGa6o(ITLAE)wsgwku3}7g z1XeZ|{HS=5`Oz9^c`VtTt33LZ2XXK{!Y|b{S0cQV7uOw&fwQnLv`E2%nSbW!u(s!U z`VZJ&y;$}0f@UA|hujC^KjlZVzvajOAG!IrLEC@Yb0-B&xjBB+cVnCU{3bSKtpGI3 zp_*2W1XY-5TelNI@S@3C&I+NCILAWK4 zdMAt@1NV~K;W;N@K_RZsXDPP0V{CGw#|BU^wWjU_J>d;PA`8im2DXS1CUD1X-ZnXZ zK^7QQ+O~c+#JlCcRFHllM_wem=&fS+;n13hCqe>BYC1c`BxqWaPREbLCBaY}J zVu2@bmsbL*!Nb3r!swCM%#e@H;4g6Q|85HL{`PqlKNNsIfH)O>ODCYif6(NQY6nI2 z>mOQZe03ELRE7o}OcqFb!uyZ~6(r3STV-0q^J;%6l~~J`PqV zW`12^26Hr;F{#`iA0=(M4$(7e!uP@a^xn0zLoI=(&$Y>D|~ zLVBVx2o6HNBV2c$?x*Ikc6=GDs`W73Q3~5x8uR8#T8@0_} z>kHP0nkE=GOdngA#67;J4pq4;LQ2Qv7C&qOw=p0ECreberR|T}7U_ce>yuD-U=aBe zOeHy{kbE~*f@fGP9B(%)l*x6HFWt4jZ)FzM6Luuk6SSYSj{Fr(1O6>`ewIwfiH`VHWX^C|t?5 zfSmg#1C8Fep^HWVK0fh7h`RIGj9Q5$D&O>O{doO zT+-#bIJTUA8q6s*A&vg(o+z`$$t~D(awQ(HtGL9N!4K*gyuDT2uY}QA`0~%jF+YIA z4=BfTWokwlDoXOmHZ70=n}PVt0a=aBVpj$_PBaU0Gi)Xh%qw-1sy5ohTMYDeQsgY1v?tFj+dTGY8^h2p7dL4sQG zKN$t)E1-IMvOJQrgf!-0zF6>>(~FWYO|JYxL_44EZK+rFEgwEwB#wKzt6D&Zzc(|+ zSe1~8uOyeMeT~K;?PoC5mqIj~lP1#eVq2H1sZV~Mbj%lG$xRtPu|TH|r~yFoj7n17 z_In59)c@vhtX!5Lh^JmY@J~nHlO-~SX13;q4dtR?v2FVio`>7$)L8E*5fvfdS`rQ8 z0TIlJgHwMF(fZOLPM&9It1M`jX&o>Z6syL{DkLcJO?#^k5QPD`+fOglM^RWGOEc6o z&Re8@ZtOrWNtZaqi)P-^DNTSs4hLC%kf4K3wQRfRkTTC=giRJ>O*0xVYagxkO|#cL zh+af=1eqfStXP^Ltvfasj0iPY9TK^q;Gq%sTOaZUa6`kGwD)2S$#e~CX}QU7#8}o0 znS9vnM|qcgtr<3H0B0>31&ew-X|{o8NHyeC?Z{2yB@(Qk!DG=`tUix$0 z?yNaTJ67$qLB`-7()zT#UDmsPw|=oQss;gPOIGU-_cY;-xAAaePz=}~z3{FZoUP<| z)+osBQMU(4p4JSN1@dg1#lC8qh!wjl;e?JdU(^l1WW}WK71F2bE{cYOkSYV)DLogZ zYI{-)mqESh!!|P4jf2xiG51i>D9nq2z3aMhN8=li5k*S2g9D zyJ}uYE(`tDcGy&J5j_-V;fAx!xSlR}nHM33ev1cH;c`Mxr-fT>_DmM@cavaogW#6L zQf$-9Pz2^vGN<|)f#L&LJia|RkX3e+P$7^eJJ8u8Az*}PZDbYU`)0I-8FA~%&U&I? zhH%K!5?Vsn>;Wc<5*4y&xn~vWeb|qvC6kjBA@(^oq-S2c{~8kg@_R)V_$W2P5&kI` zq5UlviJDpej{;BBsBDk?SI{3N+Wh>r_Dy62Qt#k!O93|WQcC#gzJ0S|S8*%YI?+u@ z1HxcpUT;KT8f%}cdQ~z9)9pAP*`L9~RW5JN2B788Qe1WZH&7iMv`7FX3m`?3M??rI znSPJC%s`}%*W~(u)Im= zcoaz)F(fVuVq%Qs&cnb81!tV2Q1}s2;LR;$88SyqiRydnpqGhGJd%u+m%4&F?58?3 z8NI{UZ&a5_n)g)REX8p~=}%@(7bN$-4X}tKq4xM!?RT<%&H0yy@7I4U4*34|LH_^R z?|+S8|NF~d0)ijqgM<+=!+-7h(v>tnrg)eiQ#|$93M<<1h7I#m4K|5YmU3Qk5%OXm z{xZa9UznaVW0^ur<6a%f`~J?nEL602zWoVW)*s4 zA~-KjmgMJ2txu||`l|F)xWwL)H1QKXN^zhBA6$@Mi0g>-Ss?fTh)o!+!iD1}U_#Sn z9g^sO34UciHL0=x(dw+?EyYS&JSzr0C;>#3C#~Tc}{ARb+ zswe9O6kce8I0kq1+4rWmKNf$!wbw8)oJQ`jAqQ<+ym z)gGjr9pv65GDi8kT{I%&(^Sd4m-ji}D`;%=UT&&`-$Xpb4HTZ&!d-AM(0|r6#FR$( z=o5zt=%Ehp!rF$j&b+LbA^urAa7R$IAQr#)|wR17A;ZIb^k zAR>o^>q~imwBFQTZm1d|1KSBlY)TUK1olSWXBmUC=S|&NEq2+SJpK0a`!|_);gXqN zv(IO+m`{=5mQdoUMTQK*XbP}wQfhDtLa6$9jfC8N=KeBkp;h5Y*~wZr3OVhHfYNg3 z%P_C4n<_$D?o~;xF3gaEWkr`g|Y7z8}W5uRSb2 zGa#>h0>;JEY491WP;YWENdI-0+(q3wY?M@~fMQWJ(>2}P?pcG2OSbiBhSwtN;%H!h z$MiaWAI(xPfYIgX5rD?oC0h6hF~4iUyfcE9;;xG%AB5G7)-&0s*CeG@7!>>y&w?qk zJXvgM`4yDq5FSA1@boyk()9cDUwxGFBJZ(g){9gL^4**u(kdR|Sx zUVPqCa;LOm_j+%Gue94{qkUTl;A^@fE7Z79E=@Q0Q*ui)&sDOrFBzAeKU3yPH&0Z$ z=bX1LJaH&_H=o-|F{kb+Evd$os~Aw!It8J~+4ctH0yu?&y7hwS!x(W*=&>pc*}K02 zgBbd-s6mju5+GEelx^l-EKngMcltIKfQ_Czc3T}VY3xeddyAPdc?Ax_!O|JJA_QSD zwfS%71LzE0IeMwqiU#hjo9x22DXgjckU$I|C4iivYXnX`_pwG8ecO!8cQ-Qn;U-y^-2PCX<7o#Hx~y%u&9j2TTVgNkw<|c#KK!j!Pb#e zjE${mw$}PJb4HoQv!qz0Nk}QwuCw6e%Ddp?+D(S0`#ePhq{&hbj%a>Dn82dv>r+UUHJ#$^a_wg z9jP5-p4fc@L~xv0zeF7+T@F}Ip3F`IkLX)fxATHzJH&OB-3kl=t1Pl%{g60TLq?6| z$)oshp$1;#B?%8=bKIanK#ozDc9R2hqqk(rY36 z5M@<@Cqs94Fg*J$WHQ3eO;QHN5DS?RCO!MH81@4phpaVCjg_cD`{KfeIH~Y$r%~;i zWY=&tmgp1moJcIcmetW6@}lU_g2ap%_IvfJn;hql=692E&l}5=xL5ga$C}6S$J$m7 zDC+%M(i!iM;+XogZ`cxx^6SE($5B9*UXN=U`*Ah*4k;Y0G?4k?_UmvH#iXZB6Zxv< znv+-(4B-daA`Hvw1q+sT=NOiN6g#2D8FLH7uZ1?^b#E*w#53lEm6;XdIt9AvRWZW4k+{gxa z=utX$#fZdfe6jfa1Q;&;z!Yn{=oQLK3s$jm8E>`BKvG!Rgj$8>ufbrFTTMbsuqXCI2^n~B8CZo ziaCL|-;I3_NIg1Z(5dcp&3rAuWjNk^*>zF+`IQ^)qE}_*3)XMaR#iKzK*4(aFD69d zD6=TAetcLoS)oT*Dg5G*kLl8<3Q#nMlAUH`N_prhFTVtD5!GP{7j2o0&g-nwFA{*4 z&iNlO^n+vAQ!7OGm3VfW6e5U=$dlk1la=r%r(^-1{;H&WU(+=t#P^dgP(vKM%rcA; z7)VT#*@Of&7_T%cAxURVOoFe`-~tt5Wu~rEL3wK3HTR}*?leESVDVIo#;s1Y7RZ$& zu=^ZQ@T*=eR|%tco3dji*w~hlWzffrbg83rp664C&EoYAi>wtJN5?!$a)9L0ER5oA zLw>eQz$(TBJ62}{bA&a!UF8ib)wPhrfmO@pT&thHZEP!J)l6|%4b#p;S;r}c1M|*z zQPFidhRIdLPaLr0-xhlmkj{-$mV zV@HV2fp4Ny1XqyY=j$C2_#+rrFHBTksXV@vWz^~O+z*y?v~U)OLvBB-=_+C5)nW%E zQaoaG9LQ5+6_|w2Kdfoyr13U!8fE)kfP9hY;x0JTsF!jAC&x!8$6=Uw-!q(zxmR?N zZkZ9{W@JVx(TaDCYuGN+3aRpNc-DyoR?I0vRlc*-bDStD;e4CAKzwv`#$+-=@=EL! zI~s=c>(Vax1WCg{%~R2B{wW|^9x!fzsz#ZTnRm0g$K`*2N$-%IJ3s*pgc5R;NASJAtvU_hzBQC)Zv%YXxAiF}=1 zNU6%ikaV_K;ohVC089K)Xx?syLdN16>d4qi@(-lbDj3-*$r)WdFV4?Xew^GPjZu4# zj*H^uISY2^qrCj(Jp{0&j5XLZ>OsS}4hWP-Vjdd_u*{nzACp4y9qSIIEtTOlC*($) z#)BpchYim5#~pL1;cd>fG@{ChXbe}ft@t7uSJe$`lHW@gB?GXI5_?c?t!Gsu>}N^* zv3*b%BX+4Ewr_T)*U;aqXprl*$1cy2odU$I;)^hXzgT|!J#-o8l(hO{UB9SMNl7|I zG_5IRSz{f1)+?8nbRN}Y)g)r;mMVO|a}QpxG*Ub8i&wVr1&R{CTa4HQCx6@(kYhT44OXF5ItTjti_gzu;q?eM zxVcf16UngLqMZiobpF7x4zU!C*0K1RG8v7%!0S<5Cm!@EMB7>vgu5g)lH?9l6Nfh) z16769opgWqru=){pN$-#M>h5b8um~$vBr(?eS`?Rvy8585Y+Aw&fvSCU;SR!>D5MB zH%(m&cO@s?sk=nxfs4GnrnDz@4<1K7zqJ@!%>eO9pytH()Kv$pD`~aS6YiHtL)Gj6 z&YnM8>m2Z0L*R&K9HF6YG`sBd!5WHfH5qFgQjhMZR4@Y!>=D_5Qc^>Sz-N=+Agv~g zr9?YGn4w&D52K=5%x0Ag2b8KxcBkzmEtxZ-b?h=kK!fVnj6tDi#l&!vdt!7u`3JNa zoFP||i_uCiR=5j^IRy7$uOHvN&`|&I2}N`UKP8J8nVgiK;N8dXHs#2hobQw>f8NlD zi$LV3lBCPA?oMTRBvQNPOWs?bxPX>ib+1};|ANpTAh4Zj3G4Mv@K-epmAV-i4EO1i zF!4V@8-FL@ND5dxnEi)<6Rvz}KP!s*&gx94J|to5jZ7PEs&ukeCMe}YtRO`=dRI69pY4=Z$>-&k-zqs)iHBb- z9@7L6acpx03|J3eFV5ql&SXprZ5Ff7OGpyNyQ9Ymwh{wP#RSmUSD=u82xxfTZ-`o( z#dNF1B6OZn!|&5Kqu<=ba#y;51FnCrUsD2LLv}^K3^!2z2H(uz6&{COy+@&J`$mk6 z1$$ZiOK`9Sre@N@y@#j(CZwlysNK8f!i>s;-{W(?-u)I77Hw$-4G$HU(>z{D`Qs`F zWIfq1{R5cB=EJ^DYs)M_Dyc|ZzOAtk-0 zCpWJM#$WjU6Bm9i={RPmI2*4zC)vMLg>tUJ@Xym3d#|>!c9GW^IoB-#yC)ji>)(){ zkR1HAh^)d??oBHh+9GSm#W^B2^;}?3FtZY2il4^7CRm7c;fVtYN52!&J$@g2iDtn( zVLXlcBODe3rlR|Oz9*5+mTx7^D_C=uDd3m+H(nR->g6RqOv`P^+#fdE%GDeMUIEiP z7Q_UXyN)6C^t||F(>=)$zow~KDg2b;6f$~>=L^0prw;iufFC*s!+-#W=7Wl?8G zwU#?)vWMU8^3X=Fw;g0Lvca6zyuInfXjI2sI+(6@sKNXkt8+b7L=KKBARtd%K3P2| zR%4-Bo(N(QHcnn3 z$y6nuz1euQzz<5|@8`9hq*ve*ik8ig-DWD_oq->hPRfOO`=-^`1NBJ4P>ZU1%BS!K z=WP4swpE)zuSW+b%d`?Ps&N(q^~^!qb0)u1e4T8}_cur)cz^HtLHDoV-s=Ue2J-<^ zX#XQT^>>)`|033Yh|23bSvvk3D+St1+t^q*+5Q)_vQm)xi`M+!up(;cF)t<(1$~@- zuV-nK14H+H1_LPM;n-_p&@kW z7ZtoR4mB5fCxx!EP_wZ(hR9~>I4l*M@r$U)W&%5>-OSHINGlMaV$s_W{qpO;Q+1;G zaRE@`m;9KAApIBNM<}6J;X;K?rInwGBnQMU?6SvvpKNQD7$Tb3dLS)`7&8))_5BPQ-(06v1n!HcZ3Gfd z`~t)W&lX0+h}|#{$d3xdmj2%TRt*Ylh%Ff|a0k3TO6dLOw>UixU{?3p{&GGh_{ z(M|`=9e##5J^w-*eo9j(pSL@6eDo5K4=n4-H}@&A>cIp)q6M77xr-jBQ-VlB^2795 zLhfR1exvV8>#n(hZto)NVm_uk{~`n`TqKO?e*jOXAJ6|CAvpgQA^tUm_#cOy|AoB& z{eM$^2YGuNbD*K)e?O$tf1-zygdMX1|4E>p6pnRof^n)LB;fO{%p24EuLCXghf@JNQ%MBjLB|n{_B?!Hz!$;3l%rz1&*2kx=&j@p3nHnEwJmgP{;QMVJ5L&q z%cB#w-X+${+%iVdz4R0j5qB0X8u+DSz_|#Ws*$Zom4b`6{e*1ZD>SQNW!Ul!01XYG zo0&}(Z{)}>t@#7LNKfsfuqa}@gijx}j@*8?sqN?TyQ5l zY+GX=qgJ+BHXaNGtL-(jSM-fkoxA=ik?Rwd$>wh%ul6R4L@)5aZhXu$9;of(d^G(d zIQb8x(f0q8H`Ln)JX7k=E1lgg$_<+|qt8np}7*8Di`u*?>@4c$FUBzDEK<%)$ zRmD+)%M#HNK{#>39#8`YGK0V_0L0N7%3Ck=L5=NiYvH4R)NVP!a0fD4y=WlV)|asD zit~%m#2$s{S-2cwIJzhqwri1ZY4_B3N@xM$#!jE$gqNHe+B}X6WkAbF_r<7ISZuex zqYs%&D93249`UZlyCMoFbvaeJE$Y-ao^rxzfYSJjQu%yD%sQ31ggFO5(@$=)-ndf1 z=43oq#*JYpdg}K=R-tE%KI@#G3K5Bp>U+~g4@(BjRR#1{d+q)Ckeo$$9i}5jv;~?{ zeHP3EFH^51Cr-wM^X@O0q=Siq>Cmfd3Dw9xKbdfCqrz(Ge|(-%zl~u=_k3~K2R!Ja zswKc2rw6FeEyhgNyvS7(^;82+zx3`Sb)(JQ^kas%-Mv~*eXBIvtAXX*U*k6#@3{j! zA9Dj~XDq_sF^(%1PQ=J9e|dM<%dP%G+pC53t?*I6Bn-m@K~kExi0U8+ z)gUoEy+798f-k6#4mu9f+!0N9c{ew}a1F|X<4ILki?YpxN+Cd?KsQJ3yQ;nHNFeCE zo1WSr8V3KAIJ#PabC*&^iYQ0;Mjq`Ts2dAlQ^Ibrg!LwYsqVO|1{_JFZ!&pNVRX$V zuYI!aBxHKZ9@!K55U1^IkOq}|!5nKHGe+(=*FcJzYeh%GgAynO@jke<{lQHb+(^qw zx}qo=eO2%B!XPmXoOc?*gTm(~X`t~3p@t)jx-{FuBfG?+Jo`+3Q7Bahqi8kH((a?~ zoj^fz3^B;Xet|qUw1%GZc!L7o!EPWrCSGtlW1q=vL*ghkbyJ;8GKsHC=KX>C>)oSx zQPR?Uy!)Mx=l>4ve}DHvHdeMa)*mnZBZNDf83FD8JzWU@VWEoi19hE^n-mKKGAT`H zFoU&%i9@XmfhzS6ROuCR6VBo#jrI8=ZMr+yH1>Xb&*v@96wY)0Z6cjSJD1~Piff{ zGwEuvY=7E)4C;qfLqoLbbT+ z%+8jGjHnX+sB3$uc;_o>(>kB`h97*S8dZ&>(1O|rv#XiyDnimmYJ@{^>n#@WN;YX! z20%nFIyc2|qh3?0tQl9qZ~#q2siihjCJv6sPk}IZAeA6;7pgvazA~&7k5nTuU@!f7 zRDQuc+PJ(>Nb+zB$b88odn%nFq8yK9meMaS>Uibx1(yS)=R2kJEG=1o(1hKKRk@jE zVKwj_@wQsV@<7p<8kpj2Sx3%nn2dux{^c`sB3O+k_o8wP5oHE4Y2n}j%C0`N-NKk= z$Osx1wuzOxdS+a01(B!|f=XOhJ10Nxgp`F(4Bp(oTtj(I(Z=ydWp$0RKBI&h8M>!;(pyV$ z*&Hr?cx($Nci)PW>GfIJP_HJIXev{pdvl^?kA&HHDH0dVer%M1@Yw-w0Mb?ye50$bir;nmgMCKhV}ck=ssh_)5cb@%Md}ydbO#9QC@` z(yB4~^JyKh_&6Q^OW=)nuv=Xb!aeNg17!Fa#m*U4!ZOiN-6G(DZf7mbTf=AUCN6|x z@kpk{1;In7h;%oJtXP-F<##Z-{nJ!FW}e(gPCDiKJ8m~#n#@hkWv)Z26+tpJ?(b!W zAD5e$idP!;X zg8U}Gak=8puz}k9=RoYavx(`cQ7yU%rp8{1hm9InRI!B!%5omG=%EY-RGq#{aA=2~ zWtDnNbS^@;2T=$*aa{*GV!`{JE&#ljoZ;P$!pY~GG{9|p*eij~wULJ4?#JpZ77Kht zjQkVf>Mq1FUyu?$2;gAwq?qTE*?&xCFus9}KyNXV2}RL6bD7<;@f|oL1aK7`uJxPN z8aXTQDDAKMxja7As)e31!FwtG;wf-;Yx z=73#D&SjG1&@hpG9@{`a@J1n-Nim{KA@l&m9+4)SpX@qer) zaQ;?JNLZUWn(14b{RI98T#Q$PSHo3BdpFU4oS;R4N#(DRfFb@qT*J>Al|W3a5)i{0 z&rc~qUcK;T^egG|diE$atitL`*@DS0rSeGgP#csL>N?YJ;RPDuktO9e=e?@+<{on| z9`&ajwTMeVx27tS3!cX{hd=HUUKe9$y>z^`U-)@4675RE*oItl!`OzOn!@CUp7O$& z7&~Zoxfnf(cR8^BpeG@-(_LkCLHSg|{}S5D=@J6xLw8l)V(`g?c~jmp0q4Vb3h1JP z@+7&k@G*gWC%jVi>4fE`x^nUHf_YQg(v@vOayH=5+3%Coy6n?vTM33+JL=&i*( z{}f6dx+`lrV4+3syWOLxHJGy)V9>S@fDCA^0@tQ>wVet0(bONnh^ucGCo&{*0YMvg zTW4QHt#e3NxL6HSb7Z1lgK1>f6L8Od)&tF2Y*ZcHPl}#8-J28HFGEd8ZSC9*A0BWa zh^C|K&B9)=MY}e!gBlu1+4MP6$rf8z?^*<1*Z3L@>Tz2NZeph$&fuxem!VCj`}dQ# z?`PXoU)VOe?!_wxsMx0{Uz;5oxQ_nyz-A{{7s@BvjqEM=r!wEmT|YQa??-Z1$V=5L zp*E#%n=3b{$lY?djID*(wVxrHo%;)ySNc$$gR5w7U<3+RV$Ltjp?vyk-I>?Muu)N} z3f1bq?LAz3HnH+;&4iXoXw$Zl=ved!xhA3h`x_+-xhGjCh$Y)Z9}}Tmjjpx$@(X&R zpHDEwiP1m5eUOCzWSFw+orpF?)Je^*Ja{fdmr$0&VRmO3C%;6R>eJ+Wej7J$qA2<7 z{vaIob=i6dSQ;p(pmhGl{9VPv8)@II^o#mUmA}jeB>Vc+<+R^Q1@S^r@#o*KM53MO z3{FBb?)7szE#)`LpxXe^cmq)EH&))WeR7VsHG*w*dVMMZs7w?1| zjwtF*+iMM(sajcXF-3Cpl@QI|S$ofjJb39Sk{qF2?1v?@u+h%BZcSbW3p=P*v+-YT{ zRfFc(ZyGBV=Ig6=_5L|3JZ1GFLhBlPp91}1q9fit;W8xz=9GEAuhqs%7OF?Yno1}x z8vC<|ws>Zz7!t`X$fky&XiN&Md=4*9Ldt5-o0-q@oUw0bCUQ)WJhSM$(X)`s_n%?S zHIPRTtq*w*VkmkvG3;!su z*XqQS_n>N>{|(xhI93{5MS3_-YvIK)D+HCgT*>Ryi?-kT7h-tC8>}{RscJ_@aZ@;1m~l}UVvbfOfld`4kY6bA&3HW z$>v(+&o6`n*L@F(#$?IXLd}#u)}{jWnz`Zo0j^5+M{4FY6w+w`9ON|p-LHe4H4y{7 zqU_D(+(eS!MEEWVF&i{Q`EkA_GFlY}!ct^Mr7|VP9%;sju==J>ohgm;>q-$D2os}@ z=@InX9u!Ly%QeT;}FCmn_hQMO=G zuBdA&_N0|8qkaFFHSlUr-a8_mJhBOy$emg?LE42e3=a9lBKPMO=KOS+PJ{YO5n)fN zsKFrA9DRgEw6T+SXfLy3UzO&(xEVM8v~W5uSCb z!^WW?bdEckfwd59ELFMzb|mf^+A;1b;QEg6DDRhJWlAEV%7PzFW~p5vZ}gC#E8#V-8OEx z-_^xCK0!guM;!g`QslIG$N>n7}Vp&fNF?)DXCYbO~?mz$2-jyGRjFN zFALw{VIkOJ_;f&0{9J#w9`buuj{U9o~C?wCx6A?;|D-b;R!5`44 z)>pMQ!#$71e|0OvPPpNDpePH`e?$!tQ^4zu5ZzYc?qPH}U>hPLccL3U;<~}!Lw3XL zFqiA7ES-)wknkB~0hNk=bN^5^i{QacM4D@ckC|w5)P|@tOie&ngIGcQC4P|*jn+}~kej#9m zq7Xa)ygzPOk=4yI`eZ&KfJVbHB9XxU*2791OkKojY`G z@4G-)U@i6NjxfTPLGf-k2_l(m#5M3#1vLY}NWZ2*7eA9n_s zI^)*MiwrEkc3pv*_mB%N^HcVVVL7ZHYA#nX6mk7mGy14C3gwYla}42KRvhG?|8yxv zz*+Wdz=z|_+|JqU;9iKAk;+6Bhk`OHj?K9uOY~jM3+3mNCijthvoW1XpS0caH}unT z1?UznTl#pEM?WW5IP!e$t$?-sc2A|qUzyaC@;Xi7GfZ7Dy3q~1G(`=r!BAPoD5UH& zBoG_T8|)^Lqhg;S*(ARm!FR)b4XhqBzpY6hhPBU))#Fl1qFGaEXK&JJ`jMJzOD?h` ztiKY5t=#=>BSVnoB6~w*ZU@PnPtq`QdTG~aRKE>@P!{C|N-{|lyfiri>>w$BOUu2M zlBA#JLngGjgi%^uDp=y1LFkMG!?skSyt?_gklx(N6qo!|yGtU%@I^5;YuIp3FW^EJ+Y@6(5j*Wh{e z$+n$Kv-OX+ALfwC$h{w$8+H#PrF!CTjI~^^+_z&BMxMXluTl76Yq2FiMoyI2_@+s7 z^7B{nLNdK!ATf!$Y`$RGHRXeZh>u+f)Yj}=yhSI+ELAaN4^GEBC!g&UAAvQ60cQEW zARh(*W>pjmeZ(s?X%riMfUl3)8qjofUzZ-Re}?cLp^5*ft4Vc_usx*lZW)xFREzWF zv8jIQ4)6FwoqSs(I$nN#oJF-m-Ij-f(AR{{7p1@!eOA$0aW z7O|GY=P)?QiYA7+(!Ixd6HZchbeRC#$!y+_Un8`GO8cUjAi&I??@E`&BX%zW%t7BZ zs|oGYX{<>&8EBe`xB%tHKmbeIuU0k^Rimv-&Bk_0__Kf+>t&)c{kz9OVu)vn6Y6w= z3O-+BW)k4Vd@2lApggZE(G~NjCUdzAzM`rhrY`4OvHE`eiDR=OE7`MhejD$(eD-#9 z6NiALS1`&}9<$J6u_Wfp9|mQR^eOO%upOD^CcY_DZUNo8jQ;pM4&zP)clP4)S98G< z%-_F*9or9CGZ}U0SwPqDUk3-9-2VtsOt(1B#7sk)-ZGlom|D!5|3Av!F**|c=@y=e z(XnmYb~3ST+jeFq=!qw`ZQHhOPi#;4=A84#bMAVdv+n)SYxU~>Sol@#+O;cWh{9{e zs`bz{*pA(BWmp}Cr!8=9DinmU-;nV8xc|A%s2uKX`~n79gEbt<%SG4MXL20G?$ngKBF)F@QW zIIqlgyyK{|b2FY*g|D%YWT7FDyUPToKKY`wstjIB4>g+)`{NHCZNKkdZ!kW|S6dpa ze>2W9`eSZt(AyQuYsQo*4j;q|SYwVBnifUpknP#0jdNV>;iJ{~AdUs8(rQ*I)>Hu@ z0kCrUuCx&Tki@6g4RR_=>t;!g)RmYEY5Gl|4;o%xt;}#tWjwR81<{J51+$?s^cdBb zdeRjTBIjZud+kH=pu@h)56EaIl-qEgq%(pXCbS~@z>TV_SRVU0?*ifcOcs-qXa+1P z3`jS~UN2PqV!ITm@|-BcihSYb%zK?=To{h5m8Y*$T?%%0d`M6kdD&`w!}_8WGZ#^m zI|*78zUs~#p{(1{8Br$6->coH4(s$*0A@T=DDv#{5}FOQo!&zRu15M6&7OXrdviX{ zsUxWmq@p?E0;ryu8yYcT)P2pDejhkuh3*=zGAj}**bjW6m*=k9y>}(%U6&EDCykto zd)q?leeLAxQ)tc`G+SzbDkg_JgXTD;vzgv77m}9NcdT2u)nX3v2hMnC$A!XeVE zQtk`B`uynz^*77f5@+w7vW~YnDs)a#E}$)6V_-e3xAZoLId0tQj3h|DYU91&ZTnD( z_vudp|83Nx=)*^wTKOag&{!ft&w!bise05EAoME&#Y?FFYE!qh1IE+i;RS;Se z^xf>B#w+&WDvvJ>AxWfi(pJgr5n6Wjy!R%Vq%6x1;A*9g}! zi7$a|gUzZ|CRMCmjJ#_VCC}>@ey%5Kvrh-_Z`bahL>_%VUBGV5e)b{zRta%=_o)M^ ziv#pg?b9aw7SHDw(E)K2AH4w#6CcHaL=zv)0e922*ehW8#qeWCIKj|kb~wTCV|Dm5 zvme>+BC{XKZX>fFhA2-4Tn_AkzX2yfpI#ckh=4wDD%_f=Zc7EKJP9DwpN#n3TsU}( zmII@?xo_N=$i*NfRVBD+1E+@`zX7LEWGUdFIeE)0G<9^Xt!o_3t^!E8>fPk2D1^;G zL%9RGrgQM+fV^~zUdL`^ZEb%)(*LKCTKC|k5IDg>u6&omrQ{x3TPQeqrhoAk%Mhgp z90yw41W{&xoBw3D(j{A)ez*KpjDIFk;nqP@_7(;uu-&v03Jr+LOZiZR@!_ok7|)iHQlPmfmWubk}_GOyAqaBLjEoPM#=ypBI*w14WC9oP}w7Jr5Tzjl%?BqmJ{c_II zbca$oMj|Jw6I}~UICGe*>k08Wi!n~pNPlFkV!QZist4&&VbB>DDg7$1Efc6LOscSr zwk)kLL;h(O;n6@|MZ(_NP@P$CJ30lML!?*Y4rvuvew^ZXYcxHS?uz?LAM`vp?thdK zv&Cb60Dvx=u!M(VhNeR?>rSF(FaaFE9PH4<^CsevnD6Dbv9OmyL!NCyv6^b(&elr) zkhG|5Cg7FJNaO$Nx+AP2Olg{!Ue@g`Cd~Qkc-gb0p|Gu8Zl07?hae8IjUQ8YXyS{s zSO+7iA*AY93r)5rZ!U?av4z9fkis}+quASNT6B70SK1Nc&ojgY`!(tlwGc+n=uM8q zFl6_D4asD6^SBj(=M>h;rlT!3so-7$`c1KHp>4>>TA3TYE%{O`(frbr zn0d42%Q^*>1{jptN-j3|#4wcErQCF>YIfhu#CjcN1LA;|H3}}fnuAlip*Gk1bu313 z{I!w`0fTdNH}NA!l-8}88v{6X(s62M z!u?8%HZ#7+_qGd}II)-KWyEPt-E0`)eakY#uD500)jEhC+!kLYT}V||>mR$Hbz7Y# zoJ<8jR6kGDmXjuC-Si8jf9FJFB*u8%Na`!oG*R`^Dbt*@NLn)v^-PhO4hkX&l2&M*_5yOSG+e2JBQ3YDP!WGU<)8W5hYoY!SQnh%Fixe zgc^Epe(ccZeSjm>5j8WH`UM_Uj0DgcwOP=$WnV9F7}lf5N8vhIG27i&wgeO4@SQFSmrjS6&vu)Wl!lqMhg;{CEIXn;qWMZdhO}l1I zGb?NYvXpwt)qZv}L&B!uqmsay1`-I#)^`6m^kpe8_HPrnHZKwHP)6-YKSK?&zj;Ag z*#FJ5Qzin(MsGw~?piM4xKJPbNV$u!z1hD@VgvnSnOq}xH`bPJJ( zwgav+Oo71nB890*G+Z>)_B8_YcC^lwroi2wRXpSV z(VW(XNN%JxJT>n`z;{CG)j=AXFVn|Gt=z{Yk#WaElj1MPIid^liHIKX1O>31;R%C} z9FepBGM!w68QZRtjjy|(!D5cwBg5^m(xXhO23fI|;nRN{L*UO=7H7K`2TNoOKDLrw zp$=R0uDn{VuIi)Jj7SortZq`Z`F4DpK0CT{@b9B=X{!}rV3iOEI5{Zv62q6CJ=}yi z$aW7rUnY*U4TW0Njb9u)dO~GV|1A$haVf?=T%LYMPr=V7dqouAI6Zl1$#IeW@fOUT z2pHt`p+ZL_NH3KH_ur&+{*>MQ=vFjY1+bTYR1*FaOn+Idw4k#DgfXG!&R zMRvBu4$4yaSR5N^gVfbBrp4g6kDmT4jzO%U(ilyfxn@rCS^gR{X^iprCx9;?o$3-C z!VkF@@qWY#KXRa)b>(E8Z^QfkH2wMEYiHZ1OL)7;9TBX|p(bi$v4KKn{#eDhtLE^U za4F1OPMj}b2SJ`B{+}j95&Ke+0vU;9q>_fAwqttIP|T#hhjJO#!0>l@!z4s0SA~0m zdzzq@?dXDYI0y+rU(jd_l7Zti4I#>yhXQtwWR5!S1(jD*s*BZZr8(FWvB;^`O08{W z%Tv$TD_2`Ai^Nj3Q*5>3GOtEY(j$$n>)F+%y(Y=$P_rAn*tE-&3(C;}g(NVDF82)! zZjUw6JM6upEgnf?(^bF8F;>&~Qz#eDXVvUpJXmk$1=p;<*hEQq+t4H|JAgG&dSL>d zsF=RuvdJXH-%ds9Q>7U2IB9aYJi~}sjN3Q>O(dPey{yMsQ=STDD;u}aje_G{0_-`{ zu^a6|r~$K;Oi7Xm#$3R1_a-g04U9;~ury5$0Uw)duiq<)`8~9bsN;dfU`+0F-6O#qscTTYlklxij*_jcG_!WT=e4EeA`EhkLURYTf+ zQP9_(5T0do&a=f~Qxo`N5=^P)O{6R9{Sm*}fOz@e-S($pTu=lLBr%&5*BH~7+4afe zzCS9#Q*Fb6*E~`Fzi(EH_WVRlL?oO;I`XI11+v#9fp3B?kg&s3D1D7g6nnXKHKcd5 zF{zPcWE^xq+z?^xL}1P!+#l{DW`e;mf;N;hCOk!V-uJO92jR4hfaF-C!Rl#aSmIO*(WZKbSD(HZQ!W>B{@!Jx!{m43qI>%jOW0ow z$W6IkBoLybv;wF%z~&=(2Q|t3Rd=`g*SIE2hd^R(xk7I< z_s-XQo>ZHT_s46rE=qiwrb+|h?mT};m;$sh$|O}r3?a-!VudLzA(9qelpC4K&`%Xb zP&+xHh+Zl^cg2Ax_hY7$eQbFSvb|6)lK|_cfHGj~kVVsvJRoj`6{d)AFDHJ{)o81oCTcV2h7<(fq{2HQzVFSq}P{R;ZFzDi+AGFrvOyzKMEC z4%rf&97-yB*U0^n23|HrA8)qfLFy^3UC~w91Xk#V_{`zURz2# z$lrwx;zT=TX2oVx3_$`GDMl+TorhB$(K+l@VAwE^hWX>1_{F<9xv1E14HQgn5`*je zQm4m6RZxwvj?^fU-05Q>TRM&U?9LVq3YmwcHC+sao~39DXHqBV4#T-el5c;sysUA$ z^t$gEw+?#JvuG6B)p)>-PWV(JxAP*bmI)kIk>fTM|JAuHBMMpuLS&MT{f^rQhXaLUqK=&#@K zS#h_?$xxNTNn%iPC_V85E5dOGpO^!oXO^S!dA}u0;AT;!^9edXp=b%rRZ$dUIeMLa z`HP%)iuKj1`2)ZJ{YOQ|zq5Q4Yz!^!gd7}fEI-1)(%$ahYZt1v$|&lne)M2=H8zN# zIY!Z7h%uk+>MLRDsTt^Ok!^CPBsSSoVKvuI3zj1S1cLOZ7+$m7$fwM-?!wZK7>-@I z>ksS_MgpfSnZ3`O4>A%D(wrYoFTTFN(0nVc>hYim|JgIe#lQO)vr7|M$%UHhPbIjusaR}O&wK~45NP) z<|$uKz>L&Wnux&}27<9Xs#H2gN3Gi5I%X}-L{bbrQ{uXq6bmhLR|L)7QM#XRO|3c- z91TZ9S!PaV6;y50z%|MxXWSl;*QhRaGDAlsEjp>-t3i6Y2|K;hne&FFwGb?#D#LH^`}mG z2@b-otv>JZgAfgOWZ`;s5y~9mD65O6iswYFwe$n?Xe^-jY%tOH2S940co9%-^kjI0 z&oy~G|7l>$tk8R|RK2u^p6M3Rn2-4TV?~8FJ+x@~{v760qf9HQ{g+Z?K(y^P-gU`d zdQ7uvDE6|bjJsr1M^Z7^$))$qqUA#gq z4>{?G?`Oy>v8$5~+XCkh6;n$)ta4Zrqex;p2;-fmIwZq`bvSvdZyqRx4@_5mo1!0T zxgbHDn{!VYp8=7TY4FVOMqU1l4h`F zqnh0_U5ER-{AL7Fkq`>Gq;-)(U4lidf}KaPMoj4kI~P@2ME>CP3J&tY6yGE`^c6$b z?f7&nl;wz*x2@o$TQ^Z)yq>K;rF<9}bPbnAWrEG={_VzFAvIWv(0)h;-(F%T(KzL; z{c{b{&NotD@d7HJP>1}G)P6vLj|-B^YeXgem3FKT<9c~MBqH$? z>6>y`1F53Dsk;tnB{vdW)fZkp_3;^#}A;9{vV0if7dww zM>St_Qx_RiH&dH`Gqi1^hvj-15rJRbVd?1Tq`%h$OD`cQHv2~@d{-XH0dc2D8d7zL zIw9Or^?=^`gbdl2{6koV5?Px8f3onancsL@yFlkBjo!E*CfHqcD|B*Nnf++7>~E~s z;7@lLieIXOkpLJ>p`U@bJv-37YyPYIiEGf!FloM(bUw8mj|5C(@2Cyo3Dkt!s)H<% z7-9pCxr7p!B6cFwmh4!}5r-;_bJ)?`u|j3d(b3#nEhM*Ag9k+Bl&wl3?T>3HPr=?q z#igCzEjvnq?5_1G&#Qyyk>d&$V%7Kk6*7C8evU|bEGlm)#+XlO+jW5-%7h&2_bj?a z#-@AC4h?OQ(`y{9)z>KQuE3{<7yJR+RGOUX8c@TQ{UWobALet>*=HAoMA`5_|3m zMi_bRV~wfp;o*wU{?ZMxW>|OX#Rxlx;n#Zs0#fa(<)G9IlIO#G2OFtr1OftI!KeC= z2W2C<;4^p82YClU!~={S!EjYEOv9TU7|)HM&=RYl5fT+f6T|Qe9OrvobuJYKQANr( zpdR|Op^&rP1KjEujJ-3nb?kYKlPcf8x|==iX@-zG*nYFN3@9($Ca94LaE+HBhUZhz zTK)kzz|UtO>(wZ#TN{EWwqzHJLsWv(e;~#YwX_PL`0^yWQ+~f&?l5kibS&XaId0XM zTBM;7f6UXaLF(UqmX{o2ss}WzGZuR(A!`90p9v!mAPK$$!n~|<1-Q3ZjvQJ;&>8Nw zvl%P(GAatH=!J;3)0wn`tc|sVBy=6pi zZ$K-AW5VQ|N$x%i7kW|5zOBa^e?{&LK?BTY&Kk$aOU*m3b&-lCQdz~)IJwvzVNST6 z@K}u!IKE-#M<8H&`aPq3+4L3i^!^QX-k26nQ@5{VFq@R`41S97-83o_H&oNE`1>Q^ zEDqZI3Tor|?UyC@aux&E&7Y?t1Pio8#y)(e=O2BB_iy9Wf62uB*F*lhu=Ah4oJ?)) z-Tu=L*~ZA)f9z!dH(mJ~*yv?Nw6*w^EM!kezs2J>eM3|zF0A9q+nIvZYF*u0xWe|v zQ+$K&{6-_;qk@ZQ{)U9n#~FgCK+v|(#&W?@Tl)LznXs)cizvyH>r_R`+cjsvGw z?N(8;fd->uPg@8KeQJ@>L=Q}bhK9&`k1efsm1D0kiaHj0y4izhcpy%w#nycr%F)RW z09oPhG{f2EBpO{Q6GUjF-pv1F_aYFc(RT)xPizH^fxNa0ZfYgO`| zIK8VLPC)(p^sP!EdovC-RueJ$oAePxF3X~Nh)b*0RG4cPanAhUY_o4N7%^Z&23s5D ziZD#1dAy1pI+k{(TcJh1U4^1&u~l>L0M)8joOsOSB5Aw(WNx;iFwU*;q0ttp_>eQ~ zl-Klt>RFyTT7)ge4H8c)hWbn*xx=dis!rjH?dQfSi4kfs9!5%7R;{y(qSK4@=WuCv z0i%7~eeKH`p19G2B;U+uhlq(Z2x7LwJw`vHJ7^M1aB1Z&ZZzy$4(&7fBun4#ci)Nc ze+GzdMkjtDUjMfBokp!~D;4H2--k6o=(Ngb;nl z**PO?ikgt=b3aR_ZK)dPkf#NcTJd_EVBy#@yTm5r6TgMMXHg^uT%C$zd4CIr)+;$~ z*V4;BK6!ox?cyJSP=hPO(%`7FG#CZzZVMsZ-;V}b5+RHVL&Ku8N0CUVS*w$H%T_7d zx*mPw=0|IkTRmfz$D1>w@>jZhV)#m&KUp*tGDd`N zx_YwOdjasCRTPxUSKmL-Pmox@3kIEy+M;pF&J+Y|L_X{i1i`jD!TcgM$a#J&K~iiCW6UiRemJxfDHr$PGG-J< zWYzQ?8DGSmS=DC}p$Uu9GS#{*!5lN6XLE<0gKn`QC_nEcsT6fWx`A@USBNRvLmJu`=Y9Uel#Lbl7@r1*L$OGfzQ2NwpDIK}Pd2fx>#^Zj4NRSl5oqzm2xr zD(OH7C!}Q(qvssh-%Jx`KG5sz2N@l~Z@y)t4&SclwZ-+T9d0rz^dJd!h z+g^&?MtnsHfe+NEPPhCND1#4jeilt(4ZF_OO5 zPXLtP7e#=>34tkih*9=lJ?^$8d74YRGoH(Jn83=HueW0FD`>#%(hgz~b?Z?~2(3y$ z08fRn2^LIZs~hMb6yKLarU^dpHU07h8%s&ms^*IybhqKOaE9Ug7zVn0+j%p^aO>MD zGd`OX*0BRz!p|;GEVt=nD-@(|bo?=3p+ShUHa#vTxJ3lGhc`m&rS1^#__ zk7s$^`S-+f?9J{$;pC@*>Z0uwpd_3!ww*;&vEi5AG9VXRBP%Lf9P601#$gXWu9AaH zcb5Myj2tDS(nn;#K;y~@^-iO<%rOVW!pP-MMUZgI?fS#Rf`b?Mvv5}sR_;tRA=W;5th@*mKwIW@JK(5Ym?@+287#WV1l98TKv zD?i&`_a{bJ8L09xRE6@ooSCE8ru^zkJfC@YTPT;(qjUK~&PNwZV$#-9dNL(8GKrGi z=G*hmmni_H+rHy~6Fj^}ZRyr6V3OM2@qi&=7p|zmta&vI{A7 znF#3BgWd(PnDgFEN21o#0KHq0-SOK6VP=?1^a4PYWmsSBqA9@ouWzdiQf(VK3s)v- z*|Cc!6Erhr*TAG`zg|vh&3b1u&YB4iewQ>c`Ifn=S_WGr)kvaRCbH|3p%SKns#T1( z?PiFekv^^j4+DYJJKvvLdiI^4rI_>*f*6`f^aok*?&Sh-`D1j4@*^So3BzftJ(&OUjTER~e7X7nFd>5slr9^^T*A zO()IrB8y8OXbsqV-ANJkOLw__EK%bx1b?O!639}k2?CDQqB|<2}M+tdHD3U4S z(RtgVH6*iP(0jvPacVTId&s00N%1@0c?59n;t3lYKRFeW-+|lF2ulxW>0~R?mD#N^ z3NXE3e5ITcMhxxA2t)0GV-V6w)DTwfkh5~01j{E!gPBy%BMObmzGhYRB~)Rz$E8nU(v zKO18FB&{3}l6vC!D|cKb+K%2P9ZT0V%Epti@*F9{{0|9I^+QpS=7Q_ixH(25k~!7j zQ3bk$M1rn{#)@dZlC`cUr?54$Z9wj=c`=Bdy6K^%^LKV-m9y(>dyQWLhn-%=el^ZW z{qKKd+2L9Msn17gq4JOQ;NSU~BK8iR|BE?oqh;lBm=J;bg+%0|$WT+Cg4oGuSp$2h z+x?@o_~pLiJ{HBLx-$(~ROd!uAo-yYA{PB9G513?66T!h>Snp#O}^ssBEWxt0lx&@ zI(Y!Te3_EYLi^Hw@G?`T=#qy z>CoXpGVf6z*i?$vIjS;3I4QPZ9P3mO6()GT+Y$x3nQqe=QO4m^{$?;HreftYQHxh| ziy&&ZgD2}%khN@%O@mr%es!I-@$P$c^EK{lIAY>#B@Yo< zE;hwnvZwNGTZ0iJ#e5R2#j3|zTLiV-z|jSeOwzqogvSTZiK$Cg!|sY|yHdYq3XKjB z*J+2qmovAFFj|ZQLoH2F8u?moSdhqS{6xL)ED9|7`cPI5b49%;soSYb_KdMj5YwY0 z4fnjPIOX3Wkgy6<<;e@FeyXFte?PO)4|vym@(XzL2CR{IOUeUKP+8)>=}``^W- zZPcn1=!e52(xrNp;yNT}R8H76FQHCqWS!m-E9R6@UiK^;jo{Aft9Klg2+$1(`O@vUz(k_%!( znkX0{Y$Qmsp+;cc`OqDxAA57UH$WhwReZ+MU^!Gi->LA#=)Hs(pV_)uvDH6yB7r<( zyg8a^sysPiO44W;l!g@GTZ5Q%j9ii4?V8st??deZB;@e1=OdB)gk$f~DU4!(al45NOID20Q-8+Mn=m2K(!dow$v? zk)h2W-~U2q>(x&FnC!gMkv0#vTtiWQfi+59LB#)DKL=4l3?fEENQqg#LZ)e8xEwM3 zC2OV8cjY1VNT$>EQ3?-S+bq#QcKqzdh7;_MYm z3v0 zAaCq$3k(DY%5&~1+r0uXNPWHweMYMmBzzME+EZb1SFG=|_1h5Y;SjtKSA6^t8`xY-TdXI0nVwNxqbeWn5zx|L%xn$&lfu zsy;5FGA2h-3`KdS>`Io9j-@{Dh1|hkzO&8Y0<8c?~#f)HvbL{DoLGUo1T~ z6;G)73LcU%f%tKb_X#pG{rm(51`0rhMUpBa zMZxp0xLCbkMP(_d6>e)1i&}m_JsQERC(k_iLiCx zfEhzSjV%qeSqQiqsw0rbnh3lqIaBvTuX&#V6|;q6IoTPbVILUJcqZ#w&DWn1bOs0e z0_>g#<0}z|3$z;5(fNIvq4v7Dy47_#R8GK8rf#FRqh>}mH0kU!b1LAvq=Gf(pzs7a ze0c0;L!K9%&v;pOLl&Vi$Xjd?yryF^ldIlK+@&e86K(-?U^~vMq+`$)`?9!uvbbv= zEB@Uei$p=hG+6kC2m6pJT^-{Q-C1QgPabNGJNugZNbdG7$G?1YR_&_0>4}|oHAP_C zDcf)T=>0Q0cgm33e;po?uO2i(eKuxgQIyd)uzw`CLML2!4>t#=4bp8?K&j9da&Aic zBK^9MYZ%c+eVDFo+%Fwf_L*c^uqIk>8>xmpwjF#vuQa>x%K|fM{N5oM9ivoeplCYPNpZ2u^OFcgG#Y3$PY~Xkh{{z3sehRJEtpW>0suB%tCKKr|5jf=E{iJWJ*J)TO= zOZN^UyD(Y_ITgABUGB0&bo+0Brqc+PCp9jdhuL57!O><>!WnroPvvlSxt!}@yIee@ zYMKr$3|wNHB8YmoF0ahOS4(_#8(kHuDg)|MhYWgOQ0k4@_+CIlOhpr}P>C`_T5!tV z_VSChYX&2HFkQ=YMyRLX>Wc=ky~~(tvpzSV-#Hm{wSJLoR5n%7YR4*{1Gf3gVm4qe z2jR=5)wip)B!Q6B1u2f)0kU`Y920I2D0@6Z9Y3#&>LC~chuxxYsgF3p@f;4I_IA7r zkrNyiJQEcxm!O{x<~-735;e^I)%#T$DRK^(PgDvrl&EveEt}rZOL}Rbv<|K)@TW&q z>&A#e6~$95OF0JL{}csk_}H41|7Zs8VgK)esej)@_~WiF>Ay(r!hj6 zHW1g}z(D{;6GF@ySJ3K z)@Nc%B@`>FtFOP%dh+Yk|0>)$<7`Hr>I;6h=%;_(>O9GJFxGtVGO^yw@bR;k_Ngn;nExW zYt*GU^w*e6bLg+pm+H`8V=vvI-;0Fd;8n8mMN~C_0jd^V+0?8?484Xx62KrP5bF!3 z3lO!JPgUc4H#O_F8$p`~UBCdMIU}ar<5sV=PtgEBfe8?Mt^d-Cz^Xdau;&N2UQw?i zaPc=~0uw=k$d#fX5SRH<58~0xq-x!1DX+F&=++j9pJ9MYZb0; z%qD|n->1xsKe&puF?m@OPH{<&rTj?RYYCiW^hwyhVb16-G%>m4!m9n)J?Cl@AHZr0 z+h)R=+(~Di*y&~N2zMXi{2@Q;0go`k`7@T8zUlz>F)+OHa`wg}Gd%3^TX^LT5|)0i zx2X@Fee@N`#>gc&)@E0$sZZ>-7%=hD3`k-65}Zk2ohWT{g~Cg?YyOxOzPV)xWEgp* zV|allK>pShC9p@pQI{l;ZQ;del$^rvwFlp66j`9x! zng7ya_!}+#C689@XNjM2AKR%M`SWnuc={I=XB{?8Ve$e2vmz=1PkiM8)y^FP^&Qk* zCcjChufp{M;To-7L+>ZxK)Y21UX z+i3@T9m#xd@RZPeUm&uwtd@30V}tu+6Lx)QpWLy84_pY>+GUlY#Ex(ICRGs$wOj;2nS(hIP|o2R^Nq$2LR-T)(n+L6pWpLfY=$ZrveLTVi9;v;dE zm%;8!>(OrrR#6(}D>XI5X>Ic-CMs~#PAQVQ)pmNQHb8`h&CDe3&<5sy;gxfiGUSYI zh}&*DUMQp}@5j5Dc%Sf@Qi+jdTd++TA=PXI50e(Kw=Hm$mM+@<+>1@(`E3qIs#w{z zMQRA4b4G?Uo;LJRP4#DyyL-ak{=E8Bp~HYbm&~E^jF(GUEh&xj+la#C z6YAjskPf^Wu6WlpRhoj)g{|oKe3^J{Hr?6==%ET}9!HQI@vNN1;NrTS)7o`6Duvm? zqhmpCrm_^srQEKWp8H}N#bC6snYsZux6I;!IV5rs<q{jaO)0-C?)$08xZ+K7Rn?2iyHe6r4_N6L?KSpLtu^-IVxAuC z1eO{}2o-EIPZZ@c^t&*aX3SK6#lg~sXvBs2k$qq%J6m1npmit6YlxSKpddJ4B^GO@ zq;p+fJD+QXe{zLgNjg>L5g%ey_O+H zlqkE^5P>^8WeRn+o76H6*IpGBSVq;b8YO{B`Ezi`L=M`%k)m14WVxx#t15>v zm)pw4UKd$$jhJHF7O6?Q7Amb2O(ZW{Vnb86CQ#GT{*sbLn-9yG$Y*z~|JH}p|n4ib9d|@7?b-fZ!ifTN`N2Niio=K>iy|YFimzx;<$ctLTadNWL_j{1uXp%h8$N#WOAI}j>QG4lE+|Qg{!L1Tv zO~L76OAf_J{cVY?8>?y1?|upuF1STh;Gg>=cLsB=9uw{if-(>XLO1l=Z zo{$T?mHEEP&T0%0Ox{cAZ-0Ca1#S8C!EE@RY8u6#nhL9bbpEo8lZ@?X8a@%Xa3dJ8 zgm(b~pE3~m0#2yU`F>+A01vNJvjJ`y-QNhsTg|7CYZ5O6xB^ZJ9LEh##8Mh%>h*Z# zE2yiGIF^Nc!X2o%f;7Y%bFFCeucS$>MVC9P)~62~Th_rhj2kAh=yFG}Yn}200UNM% z?8VQjbNM5_TRuWg=Ga-FSMrCgWLuVD-KO=%WM6wBVU-kB2I~m^!eyV_`Jz|C3F%qX zM#ua^7Hci8JK>4hAHaQ3b?3tlxoGZu8K+A~0f|F7jfiIl&#gehzNJ--bhZQG5+b`K zRzp;c^)nRb3M@i?Chf+BhngW5?^hh*^W>gGz8i||ug=vd<0*Uwq^Sz0yLY!qw zd$Z}+F1lyBo8$z}*-|b|>mz|Ep+!K-*_vcC8U{<99AZ}d{%ObRw5r0ho%`lGY(2ut zHr-@IQKY?xooK0L-YOpN9v3Tk$3b!;F#N6}&eUErm86s7=QX zx~_XIEpe{%r!87id!44(i)K}H0j@zj%fjpe{UR0PT>GFA2+Ax~$?FKrvJfK9O}3#t zz<#&cIodJ1bf*^{@;UvN2|TQ_&TsL`=G!>b7T5?JvYy2Sj=X{E3(5V!n`K#!9+4|M zvD`!9A4@>KTc6aX<&8GTUlFo2)+ZLA_RftnvSu>#UviFl72C^en>S6{O7BCci(dS)IZFmW+O!X>Lvso&Kdrvd^;j#-tu>yr% zfMA*Qlp2VBIVLQQ<8+vr)3XgT;_rW=pl~ce>SCx*pEi*Hsp$DT3i>~b9y3=v(vR_} zu%WZ5sHKzhzwr}aMOpb(K}6rws3u1^((POVOBi9Kz;9JOXe!0B@v*S+{$Bb#u(VEY zxp&xTerOVO5+9TH57capl`cmWo+sD$#%u#;{Iz$S?wf6!`{h8lHY6M51@4?Q3CLm3scS4I2*JOJZTMQl)8vd?&e z5l4dTenyCL>@$Kf@f<~@iGw#cTl5evBkH3AW+szL4in21Qlk=$(o+Cg#E10ZZ*IW6 zKKV-qW6a>SPhKZ0h-^t2F4Vhml14AdD%s+)PKrvZL6qT{y=$FSZg;%ek7BE+WqDqt z%zPqoi`njLgQ4kCGmf0v{Sh=;qmLoxt6FhFA@A%;6X^3bX#Y4(TN7cRVWd$svqppw z*2fm9S`mf#vaRilXtgWc!L`6kkPPg+_#ZIKZL~gw*9XKR`;WL7%imy@e`HaaS=#)E zOA(~B`NzikTR|LtTyKK^i^69zj6n3}ISc|u^1vY!LP@y5!FFRy^q=cyt>WLr--%@i zF=Ycekl!hHaOtkhFHOmB#yMCvPfs6G88$z)^@aZ!#Lw_Zx@G*eUHrrRmC_&!%3oiU zpQRo1_Ue-{KTz?SDS}9th_N^}VTy&LQ!FW6dllmcu}q*H@FK?-t2O_3CsEH>WgoCs zisK@9B(Rh^8gE9C$H~wn99BJiLwS4-rZ9X1wSn$TiM{4h%ktL-;x&z#Ix_j2kdDnD zF;d`6%8U~UUvQo;Lr&oG?0d2xCA*B3ak3;afITf(pe#(Su}!^BYnaLK_<$M5GR6iw z|F`M}et%6o8%f;zT7p6|8X9|^DzPGkE+5`h!AdbG7QMKwx!9?iGM;(Faa$z|9$oAM zHAPz!2LYHcOUkjq&sK2Px!-G^9c~CkI>QrV@5Ji(JtT2hc-x0uPHEs$^DAghC+3$8 z82q^&BZ!7^w%_?Kqt6(r*K^c$;*15fS_it+rzXIk=u7LJJ?3F=zm5Q3)OflR$R2Sc zH^~Y98nb78l|jk<@PWL4^a0wx`GAbQ;fFxt|Cnsp{OgDR9D0k0+LY>LL>=gK`Fh-P zw4b&Sk|ovvz2L=zBzjpw$$Z6smSd7PETOEJDJ6u|cKO+dlF8QnC5Q=T>N+W;p2t7H;94|-Dg6EVlbFYgwnt@AogEOxqshjR$7%qQAE`(#N%|q zJp^w?Eu)NFf@NIbC}*OsmPbXFX1#VZ^9l`cE_No`$$SKFv&xx&H1L_}`-%KeN2*30 zJC@;lzhPd^M{}}xQMvhPt=l$|Em9lTG!jNU-Z3c%AY*ToJWxsjHs(@pcVauIp) z9VB`DI?>=Hg2Np;EDqo4TID&P{#1CwIQsKUx-?BWipX6L0T^`kE2Y7O8xV$3KF;_H z9=Ux&0rRDCI^jDV?(><$RlFfnaQl#3{?!m5S5QNd<-Samre)w~Aj;@EeeXI)HWpUF zh4?^lS_?hz=E*?D`H1MKLghOhoXPY_QS2b6rcKO-@D6^B33cKiHF}X#kQ}|z9v8D! z{XO1@oOLdO?JEhhugs>h-b>(pThU9`7<7$T{!i(Pu6Pv6BL;;{3XLWyCLze{Cb?>` zUvV*DF-gh>sB%=f6t7X!Tg;*6bER`~)9U35{6xyH;mHUtM(={mIyh5tf2D*jG0uJX zKhnWJZt?%!_5Sf$_~0H0IXHYU4;)OLTr5qU|1GYcuDY)FA+Guck0{41qb+D58~?`D zK%_v7Duis4&#GekIIY0gpheM3gqJi1h)wp_^E!ilM32vxRM#9Gz|kFrymXxNMGlfn@Rud@vFBCnq2h4!*m3SaHi^%+Sfk z(Z;z6mj@SLJf!ykiZesh0U`uSua^f2K#1SX6;1RX--7C)o zh;1@COvqrw9VJdaS_<*63NptI@m%k~OIjH4KR}gyU_LsJzxE@(;Pf-vsOA_K-a48w z5qps8AxWQDy2S3C*KjUN*siQoDWj-6m72_BU6KmP(Xoy*_noYRE#8J4X9jB1vFb<8 zoRb=x8vUpV>Zs`RA1&zyI|w1zNA?lYt_eTobV#HP8JZa;eE&^+bD^>q$Tv| zT+zD2RdMN~99I{j0-Lm8wbXb~ppp_=|EaEjD5>9Q>%>!f&Z!`9nG$ZZ%rf;&7FgkF zF2X3PEs9mzY{VJMWLu4jdefsF??>4gn`42xKLcGAHPVmr!z~NT$#MweLuPC;j}3zC z1T2FHT_$<4xU3RqHP1Q8d@$Ehk{naGGsXrPY-!sTX-V0MS2dsHEvMO0DCo04bnmJA!5*S z()o}hi(QUgM@dJQ4h3 zM_}hg*~?IactscGO{gSuJ3wUqz~xme_$vlMv9PXITZXjTYv{U#I#y!I)^LCUHXU0G|+vE~>*sgb4ik)fVH$Msdd zBUm4P?Iw<29mM}U%lH3~3&QcYnOXH*Y2yo*lM=at=2QpDWABK4B>qV=-%!iI`Tlot?2 zqat~G9RUbrs0u}>A`ArreW6e)QBqFI2~{Nc6k$d(g0=UNdm#ZF!=&kP2Pq`2sP}}` z`*269Jc4owZwWkc2l)~5xA7662dX|HKdAUdk|IpUct@%tRAnlkf$%FS48(@G@G2P? z#Sx@9yGL{bKJO*`w0D*oQ|#=`&?7fCZ%QC)pj?+`X?d+VQb=7&jLoN<)}HPQh8jH^ zsk*oph${hgiQ7{6!WWRZ=EfC!b?o!)FJG!?XWZuKO0kotU)&UqpBHD7v8oVpXH zd#cd7Y~~z0FPe-~CuMh*iXA20D>q@k+p%+x!A+efAEiy?Sg&V}>{9y;?IQ#6Y#s3- z8a4B-FpIkrg^oRbUy-wsDOt%VeP4B?shLO#zDjR7PrV3(&w3OjT^5(2 zW3pUxsZs}RQ+IZUbe!#iYofQj`;!vU#B(@yfa7iRHK-uGgiIp759bIwCEO*FAtU7!G6>-Y3H|Wa1wa)Ej2S7>AfKo}>&g4)Zr0n>mbf$Nlfzch3`=x9jn3WkLTe-!%$Xt`Bp2x zFT`*@q(Mu{vEj{tz=jVCJ4gql%szx{0uKCsFY^ex72}!nEnO|3ZEli8L(JV(^xfS@ za0%m&!+FEy4b3Y^=~FK>?jhjph<)<2NjE1CqZ%CQeZI&4qr*KjtVP28nxl;Q*Nx}@ z|19O-wREt;##d550`G>l4VGq zC+;HD6&8{2juxd+_PE|d2-JF5N*G;+K29-@klO##F^dvQWb&71h3k54!wjf8I7qQE zrvXTeJTv09jchZBYc8}~#t@)1U38qmrM@=M5Bf$D6J9$IQ!YGlcPto-aXF8KjvpXX zn?*B7a>RbQ+Ok zr!eVf*aN>rrV5aB<%)Bke_{14gb z@3r~Ur>BVU8Do{&Tzux&V}FeQ>H# zaVB?5KNPo?EOo{kvORDj#sJf<6y8{L71`GRQ8(qX(jsY`l@d$0nIOw}o#*81p~pp) ztt$7Z8{2nZKms>dZzEY9ISjjE)TR5Ka0)y6#@|aV80{>EaD{dDi?hom{HV?x;sfqY z^yT!5&5lRoGRdN#J?~$8%H%ro=aW;JO|WH{++_E?d{!*Vo`z|eQZ2GWHnlfG#$4z1 zO{ZH*#4Z_vN5mp+63U66>F#YCZ)Csk%Kse>5yIGz?&gq5qkAdN^h7LK+tu&$N$y&) z1iJ9&7*DfI^uGPq%3=Mhwm|%k)%(ln^^bPque07)Wy5Jh1x0rxrFxY@?x#*fW{^?_ z+K3y`IBuzBW(gcd0>`4ijOXcS%RWUiDY0};5_-;am=8cACK-HhT#K?O^<#7ZK&NDX91@xhQVSww9jDWY-4!MF&* z7*0FApGQVGhMicU-`V%Wc6rzvH#_mj??CO$+S*C>u;w>uq@EG*CE8+0A*tg?H$~)x z0I#Jrj*pBx$qlj9Nqk~jNj8UoggLqtOgK{YRyh>bBdXHxI!Y=hU=npD)T26dT4<-r z=)EV^ROe9J7n@iY2rRB(Dl$51)HrF-ZI_g67PQ=yea0BF=^nvouv)a#+r2Ax6f1BP zAo|f4K1d%*FL!Q1lu!34p!H#Q{qVDLW!@NDXd90lm>?ZJAz+XQ*(bfy0p`v#^4Hig zf>;|%n2Oj?M#`>g(}#1^+`~lQN!bAgRzu9nwB-s5w78{2$=!?`n8Nb`pT8esv@9ze zGVN@+M&YhcChBj}Y`qv2GJFxX)T&#oz6=s!sj?##6+Aox7Heu1@NCH?xqNQl^er#F zgGa75O2~l*Bvcup^tmJCKFWP;5g)&h>u9nak8<55cquqPn%p;BqO>XTzgKFe{FK=e{2bKe;}efIE1Qc-rf#Fnp0PlX8>_s-ug`lu^kZUMJO*oe3)E9T8O zHLNt6KwW4${GL9yG0hK37>1EQBa+awexEIQm%639vZdP3;d#_|0u3@bH*OJa;wQK- z-eKsGYO@H+EJC|$1)vI5Py?R!wGRl07#aw zi2x$?Nx6NWsDBq3^%14+#w^c(ySM;*;N`OKD%$Xk2ybz@y73X5SRQPyb;bm@NFEf` zsZkpOx_t!fnJ)1IZ|BY`hJmOGhZspOoi`x;1NfG z`Cq2pzjlcKQk4eljdi4Ov?MGoEOav3;^F}0t350ORmOUS2~lG+7g@z>blcs&@9Zl1 z1x83CBtHZ2K{1|dB|{+I4sbU9WNtLSczJjmkq5xtRCkc>vjvp}wZ+86{0jP`CWuY= zJ^^$DGFcg?ph79N8}Yr8%H#yALIc4;cYsNabrA#u2&>H6;>!ZTUB*DOguQ%T=(l(V zF1UF1dh3sSHwx=hIMTJMXFj@&%^TvjrCz$I;r}xXN7y{T z$y@8q>Z1o}Piqasqvl!+9^($%GHwIr(FV+N;%WrWksT&5Zhqs_uUDbP;{smK2JEx@ ze9#;=QylopF+_9X;d10wa0f2;u3KD|N||Q*gSXlg-gYLh^ZjIOnYq%Mp^RG)2ks)Y z#)ABIK=>N=R_|;|uB$zqof9fy_}<84xLuv+{xl<;4nqpr&Ya@&7x-++RJ_HD6GbFw zz682=VqLrw?H&)5Mn4dS-E6rC4c-gN3!<4^f^fXlM0uVN4N1Za=r}nPh8>x%$Q`a^ zbhwhM&TCFlbNGCJ+kIM*wT4VFHH&hH#}Ue%v-2f3U5tZe=K--~#j)^1oNR-vGsx6? z6m-dS!w%ycYDBrYvt-d3XLx|F^2|Y^$lgKcvCkdlj!-_Jt5bIv{J}@sc1F^S!Qr0< zW3p$B0MM^9=j>kvtbba^{!E?}^)NPd_#+qO@AmR<>(~OC+m9-OdPHKlg} zZC`*smh$BwDmq^{kGGoX*GF?atHN?O*1Y zFV+T>N6~&ogeYny6%DE<=sDCCRa@z9cR(mq1eBCyS=s_aM3mUFDXiqAgoJe3N}i!w zU`#qAMO(p7v<4&?TxyNw`Ved`M%t6MKx-~G5}saHuo6a@^!9PWYln$Ly@7RD;1O#S z%zd*f)PX3?tJst-1r`y3y#~#iCfh3~Yx28`+1oB3M)#|F-xXBd+$Dq@2%HTsFSZq+ zZA8J$6*#6| zOfl)0^Zpr5c)$@Q9z`$BK<~0U0Vobqr;zmX9pz zS#B0_YKNK+V-w>oeR-F!iUyIm;zteaERPnQq^O-sNZWjYe!FcohW02ThP*QjoDQ%s zR=+;hs z!W*W&A)hYS(>LAM#u+-q(h(2|Z$}zdDH6vjR!+oPiNg9N&K50!dtnl@{3=!^(a+sA zqVkIxdl~#OgF#M=N<8|mM+z~Iq!eY)Ztl|`JW1&ymCH3e=uV-}Fhx*E3^F2-y*ocb z_kgs=v2Q|UATyMc<(S#Yc<@)*nl_d(#rpLEuKnu^_)op#|5LnovHx=b{l7J=(x3ih z-%O6R#Wq>2^?8Ln1sO-AUMlej1pIogLjw@Mh%+V+9Ei|9>TP+iQ7 zr6bQULpL19Cm24Inc(BjEye6otq~;(Br5(W6%#y0jhTCD$!s2VO?k3@5v?t0VMOSV zGq0ipvt?+L2HJo$jR!~CRoraSe4R+AVUv-tn}?7ScxI`Bo!FiDT%W{n4$?yBC5FZ6 zM;9n8jJZ;&EmW$#B90#7M2W$59Iynx?bCal_Lm+_elt`n%2 z5h5tJMyc)RDv_-b7gh%;bdcfpiipKTJZ5CS*DbgQs&=dV08L(2*LfBpcautpH?VVl z`LkWM0r1$~{3@6q|FvMo`NtvnSG)7&IQqhg{xJH4-MI|ogW@vUDh&ghQQlU zfb~-Cgvf~i<@Y+VO{7aeN{q7^#6J}5Bl5Tbe=ZzcqYz?E^*sKWp1t<+?BfmY5R~Ij z3(W1RQdc0Oi@5+2XqXQ<5fI%HT*I`nYGUyKUO4&1^BD<;**{4WJclO>&StuJD4jSw zl<;p(B!D`g4D>^r0TFga5!ol|`6c2t)fLAS-Ge+?$szCSh*wtOnmE~p0uDORYf&+l zXhJj}WwGusecg_Ice0G@?Yt*#MY*cqKkBrDt~qSLZ<6EE#gTILRGCUNAsjB;0aOTV zX;EpPxT3~Ru}G&f!lk+#W~JC9UGZG;E8vWnGiqF*T7v7qy+g**Cx~Qwv67*cja!Y)> z3x6YyX`WBckeQwC`|IfP;N#uT7x<*my3n@J*tj+VsDhPYl}rU^agSrQFCboH>&h4#f+B znC5u1!kq`!Z4fg{h|uuLnleUdE00Xr)~kK2R$c>3L~W2Ei#jArQ{1mW4pbh*8R_B( zwTWm2{Hd-oKjLOYVlmagqlA@X`joI?jb~ZduH62lh!F+4Y&?0mSJ7U% zN<@D59TTFM6KSp*a47Yx$@+LZ#H*ndZ0rTY!d$;oYW{6p2ph!DRj?0|b|y!nDAJ`z zdOtz3)QtY8_Lv8j1wSg3O98Q@9Z_`94unGRg9_HgI>`q#SMen0q={_#1ARrWk_@y$ z4Xp-hl+yz1$3CsXRX^d|LwqaOTR3kWbf>(_I1hp zRXM4;Slaw2RZacH9eD-yQ*0wj%ouVa<+5H@Sb(v`W^FZ}4gwsxWq1*K2+T@K+SJ=7 z#6lRFu*f!pF*BjWb>ATYorO7tv@bkn zy34-XeWz>Z$8|UN{Lbgo^bdg7I-A956%8mQk{ewWk_|>cb)!gl6S?y zTG4m0L3`0nBuDK*TtxfGHfkfm!GbVS#3zwW^xK#){>bM@^k$5^0=Upli#~DgQiVJJ@D8Li=kpNbzg`l^>~AL<6E4_ zV@)5uzSdhUHFT8-7LkM@8sLUf_=GhYBzi? zfYXmLgn$p1xC7?<$NRkD9#%ExHfK?6**mImvB!;GRx!KLAjc&?!#(5)YstM5Zr|@M zGfp`RrYw)$G5KOK?}7l--w?np2vqm2D3ZxHXx2iO*Qok-YCmN}Cxm4Mmd8 zuLsWAxH=Ze&U2K!OL&(tH(L3bP*t@*+%o|z4Y=}4@89hJa{!%p*Hd%m$~YcP7uq)& zX)_CLaf>4^*&Zng7^+H}=7gN2Cgg4eAtBTzRHM^V=Uk+qulG%djt4Yt#WGEc4&zr0 zf8M-os&t!EWlujPDeo=qkFI_e4uJOqy@&o>>%@~!0|Pp^bs^-g#$8O;neyP{`?!$c zF;1{e8y!~W%!aql8rZ1BDfI&NkbekCToB&V3GwFxd$s9Bo-t`!gxP|}BFQ1&x$W0c zl0J;gBm*9I;*mz~+c)q982U0cTZ+wmS-Ou~Lt2P0Z3uHl6^k^L2G`9ktk+q!B}goC zt7s-UOAp7&b2hA<;vP2rxC6?L;YgT019-iz^6EQU3hE(N75w;@1=Y$jf>JUja<^0E z%=R^jj-}=>l?BvNq6M~rhJNvCNMN#O&Yx44LrO`UP7$`ALj}E~=8h~#PPJrHm>^-{ zO8cG?<15?-wWzot(=B_r>2OadCcJ{miM91h$=6(S8Doz7ZiHIPx?tjKji|xpOrN-6 zwmAovGaUA3%PVi6j^0_m>HJy)B2P#T9t%!Vv~2?A8g{-F5yZ|ceoweg5#K}r8D)778lct`g4qXb)GGr(+Uw$>g+hOd=wV)9O~kvaQOV7Xqzs37Q01p;b1HN4sko| zm1@Bi${Z1qfIbAtC-4ka@R0#plhg!^Tk~`A)Cr!aOCn#t{{UNI7h54N&zu?aSzdS< z<1L1~O?bP77-vm~+HS6R^qCjM#`nHQ%&=GkZ4uky-)3#oyXp8pF_{*D>q_@r*cErYy4Ced00TxK9YWLcfXl z*ldjHu(9hIOQWO3jB4bjr_CS+ej-i}F{Jx9b@;!1oKq{ksp~Ac1Dh>rE zjRonj3!JKQb;JgWP-Px8`rKCIEQF<>Pmnr{=Q|lzP3LbiSUSmI?K%JWtOYDvJX2YW zCbRe?vpCRO%`3oQ*;hme{%pvXw&*kiv}UoQv4{@Y3D{&qsT%;lxKU?N=xZ%WsO^E$ zKZRo(CQWA*%>#LMVD$tneknW3^Z_vVoSR9T~8TqS55#aa&QUN33O6eGx5`NyaEm!(>u zmLJU2SJ){GE$W(W&7_^Qr)ZHHOCbUIuL@=Qm-c7& zSr_&Yth(a1VT-EI!qJ2eSRyMtI&x-WEo98{Nfkei)HF-iEf>dk2y2b{>`vt%UGGu% zj{8?F3gBNH+LP$g^7xmIfa0|XlB0XcNUYlXsH~|${i$cnUA9=?R9_dg)k#UTxt|~^ z@9i2q5~>G01XCLzF>1wLudUc_$j62SGoU1j8@ARu!!+W&w&|!++>|0QD84mb<%8C) z+n=UV;uVGxNuPvuMvo8)ngVJQ3tj{D$LDo^Cnplz0a_;#v;@>95_AO2Bo>?j!i!zK z)T-Nf0Akx}`Pq!QGA(Kk$Zk6*jGkjh#%oK|8Qn4`dU{%z1^Ha#=%D}cXH7D{wo@Vc zb%Q5?_-{3$;6G}T|N9o`eXuQ*wueaKETD&=SI@aM1FfC zP{nzPcUs0u(h^aqcv7++SzGDLgd1y97c}{w(7(g66n+NYe&K7fnj2MlSMr(QB*F|p=pf7U?AIKLdA_D$c0>J}Dbv2e%ZDWEm(w&yw7 ziv&tQb47VzL5+(dSzu;LEVv><4+TSs(b}5if|`9m*q9ONEq`W|&&qM7dXNqW74A zGpaXYmvSXwGa=(!+3@L<-F-Bgj%xc|9g#QA!C#(e5HaM)(U2aws!g0$5A-chFEH=@ zZH2(%Xsp8jh!jkjKP>RK! zz0Gre6x}JVpRekF)VywE>JNX!Okw?N;?6(y!vDTSe>nR7M)SJ3l;)i0)R0$xvuVemUZ@%4$ zIaRx(-vjIs1Pk7ZyXm;g-FBXLN#HPBUJw(3~VRj z4!3r=rjT~UPve_NYPH%7GjohN9h(d7OD)#kV>XY_IqV$Ux zdBoA{J_TC2WRp?Zl-v^xOY-<^mO#6t35GT5u_jp(WNZ0glLslXPf>D@q&joFb*=11 zxIOo6>AsKt*|P#n%e;zTkx*l{(oWv;=V_$+22l1yAC!lOZ?QIa8@ON)*hm-(8b&~b;Lv3h!YF{iV9x2X5S1EP zHe`m5jo9Yq#&uS>hvg&$rb?fI6Ta2P1!EbME?~aP6!U_x*4=p0RN4Y>x)yo})oq zIi8{L3}o$W3Ki`)Cukpm-6s$~{atvFM@Sz9gPz_{5I&k4M1MzrU?6Z%KB^l=e_^0pwCAYa zRM1b2U1}hG)MsRWe$;1X|5`{N-Q5|`PoZ6GpdFOwlwN(PcZFT?FaGaycrQQXdt93`7=(lJBllQBC<4c3ryRJ1;RobGm`!-V zjhS*{#TjrYe}Z~+MxpN;VbUAv(h228p&K&_H%6uI&N1nQoSSyy9FMg{YStLECFs|R z)JE+LOJVi|5@Oc&8=G`uNn@Uwbb_&ud(h&M|8y-I_u!?Ab7ZGWx&_Ok{~7HO7sbxF zIoYI*VK)TJv>gII;lYhI<^hgJ`ZJs6+EIt>Cvuy_FnPV;a2JgWz1qYx3Kr96T=XxR z<%wq;X-_SXrh|Ky(6teiwOy`L*)k4W4`hjVFRpQGW>vq!vU$sxzpe0yF}X zWSVlZQCdxJ$@KEr^m1j-vGO>ytD72l z%Q*@~^XUBL)~i`yviM&kaoAB30yDjG4S{-;hQ*e?u$wvfu|e#Mi>P!hYYBNfp`&e^y>JLn(xr0nYPd|VP&}p*108`|Km|5)6Zb|rt`}u_W{J1{J<#t-t z<5*)kWrG(Gm_$rVvsbM7voaPfS^1`=`xY^xl2){#ezPEyQ5 z&dc6%<)6lJ>A%F%6WPeEG{@TNo6}CyIc{S zx#uG1;Zye)qFlH(4KYuUbBvmtQsla;EPo(!X4~3d>gptTQhk&Snk%y@)G^zxokfYd z;_Tc_`am<^7GG*I=Ntn}7!K1?OAXg5ujMSLzw=YHHmZHqD4$;*sWNU?!P*$%*sdeh4J zidY8P2M;Wv4z#7O*(cO`wbo&-lmtO!L{)Ui^GzDFsPd5VB(Q{vR_0=dZ<2#D(C`Gf z?isg@!_KF_cj z<(hAvWbztj>8btujg!{QXJ9jQ=^o2|wrl}7kdxF_=drdm&GbLR7y*3C5{CY%}P6`2SP znVD=VIFhAyaLn0(1-uga5=920qw`jjrl7%sdLc%MVz`zHYbq8(+^2yPUTC<>HMJVu zQuf6!>tY|c`P-DDiSAJ=xFP~rCaE9wY1urlFq!3x+PO&Z3R&r%WI8e64~#% z6!e#nXRD#&rDP9PnG_PSrzNMO(FP9|t7P<7DZPspB^z|tN5m6m3W_W^oHtj<+SzzF zN9ss3jFpyPV37^4PE-nYzbUm$%Fblg7iB9}92+mZgg zc4SEWy&eHqY_kt24oE8MG@3s2)2zXAv^5c>q&Q?6P|ilF$)b1}O=EjoY64fWqj_G5 z%KUL|bllCec4@Nrk&64Rw8Vvj$=vE&VLxZdR&*zM2JW?a*Ben*n`b!sMByTuOjqds zFmp|K@n(s2KRb`9th?1j{e3)Nh-m$wkZd{<4<4_id{je;gSZIr0xv+PB5q-k9_PVV)L^1>L) z3kY!e^=C>{vL1{m#9e8|*)Rap{z$-nLgZ;wM_{X1%~yPYl?dSdf&}+%J3eYTZwQQs(D8R+lL-qHA){{Z=89&>OLB*Wi0q z^`Ya*LuE{9o|C&;{{AX-vg2wOdd2xBc>6JoP-w=_Y5btbCx7L!@PS7^j^bjSG0Y(E zajL~rKi3QlmEVw%L*d?BEGXJm%Fo;Np! zne(0Lo7xp);OtNAAnTtF#Eiw&&N6LwWJ2RYnT7~<>gG@V3oo8OzEC^)x!(@6upbMv z?@^m4UZ~blAA}arKxEze72 zJE!A2>~i+i%*coj%%C7m^|E9*QuEMDVEYjLNWTHptQJDG7IdKGw^L!EjeR5nV zl$Km_Cq50yyg8xdVuecYJAB!{)8(p19ov-C$1|ls$}7sIm4-Cg0CvueCpZ^vHk{T{ z4)2m}CY*|XD)o3T1L(Ak%rEz?I~DAg2Z;~B^g9z`PX}_|tLzF<1V!ed(1FqG09EeJ zQ~El*BoVGAs{*8&qxPF>iaLm@ac1Q%K^*V11Iz|l4h+(O*dXm9)+q|Gl5%(egb;$* zW!jN;eMrE8RvD%MQjCrQp#ejcp%j5LZu3wQ7Y?O>J3q~s5svXWrHQ4h1{DRsr7@TH zA`VmM12BC^QXEOGQ&5IJ>NCTb$3G}H&q#I_TR}X&DvI6hB(55)$oQmKAkBU9f}noV zKs5nAm-N(B;kR^jIX>Ir*lj@n`srXN4T`$-OPgxn0Z?_B!-p8IL};dQ(Z(;U`Upm_ z@f!r%><&>+0xv*3pv8IU6!1-&KB}XKj|tsK4t5a!vudg;z%f;gNwi*mSQQ71qe$qR z#$9^Xu7x5=zcbJug)7n5$2VU1I>|KR6L=~-8G&}!l2kPn$ zTq6hY>MmTjK41sbkD8Dll&@F*+6BlX zWL|b0@4Ll6+Xq7Pfouor98iE+yJy47=d8K}(l$lE8aRYZo*@pQ@xl}226GpdWKs(; z$nS9h5V{HgJSj*k&#zEpgV>=rez4e7pD1)vn*)wCTnmeEu2xwUH_9zdSGTxQRkFu- z?6fM+09Wq*erLG8IL~f5IO3RIJ(tujl0wOkR(vFCTYyB3@#E17{kYO zc+_O#^sC;9T!xJ4VddwntPIPU%z-kF5sJ32$rbZhWNf^pwE=WALA=t%hVDJ^8T8gN z@-FNvYK&y{%Gk1f9QJlmm}ex%4w-!?uz3Ful1tuIuxnv%9{}DP#>+cMNuO8}UlG<{ z#_Na3S}&BW^^GjAZvcPW-GG07zR>;%M8Io z5`8$NfU(e&a&#k};iIsLG&vf3+X@3$qzhSc+qeN*9j_0GO(3r34+?a-UUFKB))Ld!A4oYkq-H;c;i`a++w*yuY=pHP0YC>ls{Q&i_d95 ze^&b*x)!4lu;cDExKi)Z66p zk6hlp1bbJ^T)UXRyti}OF?!W{gJ|L7tjRP)`M@Q2(}y{=xYjiO#qlE_5srNL(y$lF z|4qmKC&%yq)EWP9GuYUh|9499zcCuFN`GbCD??dN7)dMgi3pku`2i4H7!eH*(jz4( zWj0xOWYHPxZeH1xG{QbtE>e(Wl*wm2bH5{ZV_dh0S@)%KGaqj|J6)&0T^_&10xUZ& z2=xig361rC@zkL=`(2^W(d>#042gnKh8GMta>an!SrRx>hb4Tt10}JQ+l-9i&~0>v z0-|}$Y^n^Fhp|d9#c)tDzuJVm;JfY2t5_s#Urc=v+0T>SflcHp%O(I+?3;SHRTdNoE9S2j9G~{(Km?R zt5Y`1ZI&=E^d79#GYmC?^1n$emawQ+mo*GAI7X1eIDeayI(SnR4UyPVkHgK%F;qMy zL(g}}mA-O?Yp-lN&}vEEiT9~=);NvE8$NP-tG3=lN;sF*OI%0perjOvd+%}Ev~Ge` z&77ie_zAXr!vEVCW5C8H6?xxJjpVj-fWHx-pm;y!+jyMRF2O22j?aZRBd2|b{ zsI;VBfT{DX(!3ja!_T&m$e`(kbHeIthHU7ed2RCLNwp#Ikm6bU!*Iz$FF*v3QJDk*u+4fm*p_;IVe!>gr6r?_H~Bcq*ss%8+A zCC5hW?f1}sNZugW5AA=zlS2Ph%%uD`G4sEpH~&HId}X=x3!r3)=4n-Y$KOT21z^kz zht**vvp5hr_(0an6j}D>&dl6xdwODHrnc1`05%B* z1=a>@m8s6wUgQ(y6@UY0_pF&pBu@teG%ogJ zVDa-#Bo`&fXWxHxpi;8u-#@XNB#3=;=hOzPZCKt&%Y#RUo~y#1vP|El&>iqDnfSf zO#}*3lo7%-=~~Eq0f-@leHMR(d9;er2wEd)u@qsMeD&Aukt16A>`9jsn9IeueCDn% z_e^IKefDQ^%YtPGHRZHy-sH<;*49$amVIo@ujd|degKYu8K788tYK}W2qYNDlAwej zY$O^;RLEA6PWru;AbAoWsy#aprdJ}1!9TJB>6tpmXJ;Iu_FO~qDS$9yga-X70yuC* zA-Z7b8MDHO0#F7fU;-5*eB$;@lD2sV9g_A?Lsl@rFk+aq5*WjsVeH4-BHT@7jD*n=FbO8V6`VKFHkltMrHI%1&ZLw<}KyOQ&z2f z3;e7Swug2Xo93h(wHoQD7C^m9 zm+1RSXlWminy5mm(VG`RYKN(ZMYUWKbvxoXeJxGev;|!Ht28Ry#Ey5Pky{F;^%SJG z&45^moTk{eqEA?d%5qf4J*8yoZG6QPFBykQ#$f^XEEk%X#r&zDDkB(u3JXvuwyMQE&54@IF^5bVg=ZWfpirFs0&+`Nqbp8A zVXZBuFy%r^Nx@=twj0thALHd9r`;m+%ji6G5Tv#Ov+bHvuEHZa3o6BIY{J|wa2x<6 z(c5|mGCP&Y+o+i?Wyz)sx~tMFP*z)>XfZ2W%I_?hMmn{=v~8^jC>FwnN@N6F(T&D(BIVX~-{}um@U`XDDoRjd&$u0vb9hP=U&o z6(C}cio{z$p>+03!n^$X)+)*;@@Qhr^x~n=1kCu{;z%m6i@$I#HtF2;@zbSs{kL!e0X-4-$*?G|1Q5j0bEa9T}uhD_Dj19N8Um#I0zUS>a3 z=|~d`gwPRXCF*NKxr{itnnFDO;>^6+YtB<4eUDVGLu5dZ}*e|K0 zj}O8X9H&X;+c;x9^Mx_(sR<+OO2R8Ok5YzyG{()Y08%Z}_6yLc{#q z?*Gpqmj63+?SIcb{O6ayBkUzjz9NDCb95`{OAj#Mbbh5ZwVWMo4x%HHz&i)H{*ol? zR{#qv7#)0Sip-cRE|d*@Ek{A=1Lyy~tB#Z?w1eK~vD~f)O>*CdAW~f%q6#nJizwHFr_a>80(6FSHNqO)p2XluY}^6EbR}0IW+VBQFUDn`8~X=(z5Cgc2FC zXQq@Dh84!^%pgyAKPP^SWvJWS9H}P3T6t!OOSsx4)Vao7SfTy~COFD^?I{0wuz~E1 z5t!e(rr?WwM`bmQZ|cUI{b=isqK9TdZ92r?VNF!yr?)&`M|8vg(=qLAZ{ueAH(=ne zV>(z_*Lhw5g*OvOTBwL($j_qEU>0C~^_3QPBwZ`lLUE4kfp##goY0WK<-YWpew|Ah zC;fH_^IMV^JA+)A+pClN(-*3W_vz|tMt`RpSVJ_~IM_J(3tMO#!;SOAiRZwR#2EaG z2`Mc(elQCtqzVWU9c7TQFwR&IP+Ts}GgzpBDnbM4D1kv;MI{ef8L6bIzqcSFbF{K8 zG7^T_tGvbfDm}#7i?$Ha1-Dk!bQJl$eg!6~cV^Z&2`87xan1w*}*5)sN{a0 zO;|4WU?uZM3#tmJ-t6x~+9j`)(+sgL=91A5`V-gzy&d3xtlP; zdbaJs<&Rdj0hg)Ui)z0gmhSo)vZ-;*#t+)v=GH2N$dor#*h0{VC&634ws^faT;AN| zIbO^@#wyoC#MC+VwBalQUGl==n#SOo;`w>kSOzX2X_+UpSzxMWrA_be zFpq{Ri*#X_SZENPlI7NBp>GHifU~$E;F-F%`Z)v&zYz*8567r6s|a-AIHT}BIdJ&Z8-n%=QxxABIs|TuiE|TUu$=if7I?m_V#}} z6u2MkSNOodz=**J-N5+VzyRIA1m(cYz}V;r`lbhZ2YROldS`n3MhQv~FB6kBQp$1@ z(^6~jb(E6QQ|+oThevx4lXCS7)U&eE5=TdS_p@>n#zzTGlohj73UWb`=D;N@HJF*| z-d=wMy|z^VxPly~qLKbDzRoGIvaD;@6>BG{*tTs|Y}>YNt76->QK{Ij*j5DgydJ1Ct!-;k5`U! z8178)5!zD!F}(ec+1x+5V;{o0e-$7fE^{A7pL`!8I$f}WU1UnCZ!_GxMtwBph$S^4 z$?S+TQovR>bPcP{1Y_}ox-SwUpcMo&aYB>aNLHV2f1XWcvzVJP_!C^beOehV1sVCW z*qa&X3Wh`8II3-c*~A#~4V#?Kgwf6r69pRnT#4J%`bNt5D>+!h0HbilL@Gu{u2REu#?F^P$)xnXPfgnw3t0^+hEe6w%8w~~ zOu_pAE&Lw6Vz}09|2AxSp?4ekP>SEI5p8PcH`wF+6vS3+ji*q|$yXF%8+QIIon_cT zeHUZw+Wd%|10J$rd*V*J5y$b~ATXtR%_xc)yfK%z39=biN@ME}|V{|!4IF$dobQjZ=|3=$n5(hZVb3=#%DuX?aivk1Hy%%hiN6){M^yva_Lp1FYj+M?4 z^l@H&>VKgUj=>jaV7C1kjC3V(UFSYR4Z=U3jK5DW{m+h`sfUY`p|Q)q=4bvbM2%6_ zmS6b5@|k_>04o_4RZ!~;gt!M)VKr*RL@7*6cZjMYePUlnux@C!<`D(vJ-~Tf38D-g ze@IdNcjT``dCxD%!S4+TxXX^RoVzb4C%Jmw{N6!!*zFvXImdC)V!2{HV<8Z#2yh3u znK5S*hAbMhmlH<5gIHiPGe*EcmO9P`L{veVF&Y}6??c)dMH^OYXsx3?_x}#ruD7Cg zc^zgJ&e$Qe(I})kQ=fCXN2KF{E@{EwY!L4@t|{}E$Az6jmtF@n4JnIc-RfFa{hWLM zJmB%IHw|##zGIz0$7@(K++DNLtA947L3O8Pqh#?hz25N)B>0ZefplOd^K>G{qmF*S zyzl~Pjs-7R4{mQJwaPA$LqoaWuZup@7JuJ!9!ZM6INV?~!fc+$1XJSN>|rH}lJaP@ z)H8fM(cWlRw0SeWvS{Xa?zk^U;eOJQSlj`^;%PNy*lv@%VBL)NPXJsOxLZh4NCAs{ zALE*k6|+m|QMYxDXDLKjL)`|SWz7N!8mzW$go2_9MHcrNVI@r!KKQuRW}+pm0MOlN zI~mGyycK|A>x>k&u;)3WI?IvS%oHQ+<^e7$^Vun*lkV3$?rMe8Pl`sI)9aC$BmSaG z%-74=Iyy`7QS-EB$8#_UQ-{!{kk1b>r_Hg1*HB3mwuvUeEB5&_X8zJFi{f{7g^D8P-l*2&wwAIaK+M=X%4~MsFa2_ zG{;NoSYa^9(qc7weg6&N`?HRbzXYi)1FN>gO8NcIty?()Z=m~ewpssC^osMhJo@3{ z_%ZAFr@`o7r}~GwwmQzoh}WT)-tNeqV7Elo`e21?NoowxDueE?JYif5?wDX?3+}Gqeu=UrDzS`>HfPMUjDf&- zOXkE?P_Z%B{B&U}5h$1{VP$Ss0WguI%o&y1nGhS7rYg&lh~EXvx$))Dr=Jlr*B;3V zu_0R#37Cr=0tTW1AMJ(#Ff#h;MG|2yl01Q$QrH(MVlS=!T!rUVWaZ7T(Z7Nww`Mfh zo*SYo$k@yr*UbhFHXw|Io*c@hc#@T_9|M^;vr`S)$bN?(=Q59Y3L= z`64@6TUc}(IT2^A(&15fw2Y8Ex>LVLodZpQq0Y1g1?&kIoqJ@Ll3xz9y8Z0uOBWiQWW~%v8WLmbumOKkTBgS^chsD=OAIf5c=t~_bq*mB$D;q}07pl?P!!lWV zW{FDJZOMkaudl8Ax|5l=J?AO0NZVA$PYS`w)KnReJ6Z8OJM&AS@lXOaCg;YzuK21l zuSwv{Vwun4uuiHIW|_@$F-M}$jI^6Oj(|F}+#?pb%8-jf<2F%iXyIp(18$;my0g~m zHw{AI$`e1SUbV7+sT%JMic4CVX}jA`7hG8AN4L!_1S!rWaTR^(X=z@J>7`r*DRnM; zx@WDWlR=*x8+0s+k2n%XH_~cdfbzETbFIKX^IR>a9@$D!b9GWkQ)^-!E4M6-r_0d` z=GrW7UeKOTNKP#!R7TT?{4FV__>EP?9mY4XJ$cyaKC$Q55;3rJxH1zCLnXh(mVCFD zE;ITGn0f>)>WpJcX}FCpOIh$pPABom3V%fnTWtBY*>1rjNpc7$uMtYFdla5CBY#7O zHbqq1{o7I&C%3zPN~L4KOyRzd;6w4Td~cGv~WBqFva~Npw>!0dp;{ zY?DGlfo629QmpMfTSGba^B6E`5?&#{!3FC$g)W_fy2His@}Y9_)9|`lxdrw6xS)h3jW!b~^#21wOrJ0SCXXG!VWK?by3Q28V2XS~2AhOHDp^@A8)CCz>tI@;t5Vd z>+)BA#$*b~7TU!bY^Lyo>YMkxYBKJ()YbeaGrudox( z_}n)f;3Rw9UM`x8&d7)WYUdEYsE!D(XmI3CT2hNKO1M3A;+CsRVxY=tc{u63Ab8rt z=P~`AYSG5UwHSLW_eqy(;$O{==16t0l$T?8Z_u)G>+gzWa&FT|6c18lZ*C91aw4hY zBE>yO@B)_aTS(nOJt zneLEpoM9&2RolAE9ifjU`P%7u`_@4-?0Mu-or?1_CIY9ZGfsn^=joOJ>MxDHQu65G+gUW8=i`FNSK

    #59TW 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 z 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 z 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

    VG%S9+{qPcu~)l{^K6v+%=SXpf?M7X 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

    SO-_c*kpKZ0md*Y=kSQ1qKd**+D0ymy7RYkgsA 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

    !%tcKfxi%vJV`Lq2B++t0Whfvm`MH+ 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

    y12)gzKX&UM}meys0slCx|>? zZNPRzGpblKO%jpd%x}Hj zaQTRs^OjfY7xz;%dsnoN`nJTDWy@jpR_>pOc-_W>+yg{J!@nZp;d4avbXaMphs$}6 zhSn)m`Fb%Nq%68aTo5gCBJc`(O93}CIwAG zZD{3l<9<(=N(m-n$f@41$K^%Z-1E*!NZGxeAtKsRpN#yDrM^o38^+p2o$mtnOq4Qv zwH?$kO5>V12iR_KiowIuzil@cIrbBgKzWF2ebR=Rp0?B$^m$hs`t9ShA2octoOTRI zh`RqVA+r9Le$+oN%Kt>f)4wAkzXL6<2u$(tpvGFYvK&ejWUMSFIV}j1Swx7@B7e}L z)v@(;LLbsFE(Z0hL}}3z>SQ1z27@!AjqEP0XUYe5t3R%ru>`M32uYWeUBt*QtbIv4 zZM{jTltC@<{?m3N{B8N|rC-=`H9 zpZ%WnH9aj!*7i)=-J}bFDbG`U`7tJTJ+r%@6nR5m0T`E}8w*^fl5-?+M_bOcE1V!q zS-bGM0YpAwV)BQEbXYOF8tdql(Z}RE($PBP{@=6-c+IowvawU+7Nq;%Dv^_XDoqau zLkkBurucn-t;8B$)d}hN(x;i*SL~EfjTJbZld!Nd|zeT5rSD*nz6*G!e>Wi5+Ear-T4d$wM05oHp$M~IjYlckQIvt zym}xR#StCd6JGWaHc~Zkg2-C*Vu)=l5Pqn ztg#KX{0BSamizyN9YO-wA!RU9H~An2us2m`+KNz;oq_tCR*3zK9j839M?2rklG&{GSu|qTfJ2cc? zh6AuecmO+O0kFeR06UbF9(w?BL00Rozn?Kpw<&?)@a>R-sk(7bFf5-sGE`Gi@Ev+p zqh7zhwSL^+9-EXoaO{~q`NduBdwhvoL|R|@?LO_iT_J~}A6nVGiAUEr06UD*BbFIl zK)_}m_NtG~9zEiv4}HcCXROp;Rf4g@zbeEuReVaxgK2GYphC1!n@aj?T6*~Ih?uhh zn3g{NnU78#I|55~{pZm>B z1=3D%W;B*;_eOhib9EoTKwdW-IzzT8)>qR{5@-=~PgZ!K0ha_`wJR0;eFXNeCTX4>jt!lt%-kH#FznCaEquIzwz867KzdW@S!(8@qTw-uI|rR@j-=R zPiq`HAoe`P!yMi$yn`+v806fNPR$V9db7B)GSn;#mwAvKUp{E7Io!&4n2sEmqmZhl_*heur!#@Dv%BFsjqWsP&;r?VdJOP$7< zF{;f+KhEBzoD*$69ngYg4LF}$L?3@$`yp9o3*eT97|J>_;dPyZ*x@qqQE1z=rDdVo3wtV?%zaZ2cQnd6$hc#~&< zOB$_|+R37yvz&>J<7YcB>tDZRwOO?&j$J2#9FyG^UfYBc2{14f_M4xN z%9gc2d4#Rx{a6|iuXOqmLTBA=dDz^o$+X0^BWV=TI!5O>WjEacqPq^K`#ejLN?r0= z0fwSi+28cr-!0-J!IZYm|7a13z%AlkU!-hh<~)LcrgJr|$$M`RaVJDlR2}ON{3a*u zNFMVm*>&UhWwLwkY%JX8iZzgGtLka>b7mXW!ryqM3u%7a$yD_v0@%X>Fnj3vS$0b; z>Y^kk_Dq>AN34`T&R#-qnLjGH7bSK_FDD3l{DSXjb-k%aftA|Dx}>1VCZ$|8g}15g z6R2QzxFo#1EKU9t|E8GLhCjEf;>KQ2?c~H10se^Zd13le`4{9TwCfsPi;`#(hgP-} zL&3NtRd9=FsW0intb(U&CTWn#%B-R@mR)a__c64{xI*5{0iQ-P=0ztPT^zEPq>A?RqEP_}5nhL7xvb2luHHKI(UN$>ZY+r|qy~nI!kc9uCyU1b6Q&TrY zDd~6;lIr1Mo5z_Gtu^sY%Mw-27@EEEp%3~e!hXz*q^E&_c`2`>`nz8Z>#0d`=@UaA zh5ba4hTJ({et@EH5U0#3q)&0hZiUir1x%a_!n`@t;V;vk4 zovpirfQjkj;~3_4J~JLFeX89TgddS1vr?Vx?N;t?x^dJ!aEx0}`u%TnZoYCuupko` z5(b8{zy0DwSOEDOBGcFRn&n>PiE4}ahFB_yFdv#E{5wv2mP!WF&_V7dih+-|j~=Jq z*#YN;DMyS0=!&8hNh;G|HYHMlXrW&fj^{+@89xMw&)D0qqs|$9dU(xxycL81e(33P zX&VCIhbUnDaLIX{+Sx9t6M+E04>#sIDbY^bd0@5-H1=)@uU_sASKIq@y8<7L0q2fl zdW8$ZgElZ7e(8y)@AEt{6*eKoZCC)^(S3Uqus>`wY%AjX+lhMc+#|C7&mNH#+#@Dv zyH>sN_`Z&U3vWymDzzo3@6O*&7TZIQM|kJLCGAb~ovg*#mb(v*2XpeE?Z+5;<;}T2 zWHvh7_HFDZKR}M7#fyCEOhrhb4=7Pb0VS%+vl6wT`E>iteF3~MMfs~nOaj-4o#2Hj zP$TyKZ#CljUp3;fi$`>X0Juhs`%@!=>BE1k5n24-H|B=|HKN^fjrbKr|96eZ_;-!S z_go{2Z%cxuI3z(@?DN+=C%WFk>IQnAXX9ebQ-P$oktDfef;)~(U1+_vG4s`RFux&m zN8Yi6+H4QiokChQ%e$dhc}_-Mix#8RefWnd#i#l~>r@@+)R_QF>|*0pvA*1bUDVJ- z72LaGL_NDn{q=HOq!WXm!8^5;Q%WD6o~UfN3#Mpyv2B>6*D*JnaLeJ6JoRo{&CPkkB8sLE zj)sW?gYQ)rGSIk6U<4pOGh5k_>%Xt|64jeBsH^Ju0{cWf^x}T*_w$=FrV-xTNgyQZ z{Kt^^-;V9TZ_58f#D9#ae$+;!mF?uUn|i){7`=C8s;Puz>{W* zg7QT;twcd|N@8yBUJYLtvBtvdYRsWalKS?)E1-^r%r*mk*-=9ltM_h6#f*^NM~onM zYcXU|v$N1+AYk(tYZ&-)-r&d}L$9knxfjm)jlq97e{cs0s1fbSi+E%#e#`rsN13WH z@%Fep*NCM%VK$UBopg&5qc0C+Z9QGmAa*Z@kb|SL3$i)_02^w}+?KOtOo<3kBc{+Y z$V7apavc3|wOZKpt+Fn4Q8=11LQgw&c4w~9i^Tjw5?|CUydf>+yE|-oUZcdfk*3!j z$_aE`Nr-G*g;(v26BsV|nR{~$EV2t?!CkNS-|jE^pXF++>an5V&+6A-FI2pv%_9cr zL!L~uT zK%V3LKD}#vq@0n}MfTIcx+KZ*q&ZQ8R5-|qblad5v$%Es8=~*=;Azgfz&4++6+l5-`9PzBmZ&a0%pQ8G6FhwJlsJddW zYwBX^5UZ&9r0mk1r15LXD4Aw6mc{BcJ7i|*71gN?Xo@Ada-LuukRTmg3571qmvA1F z^LkgfL`M>)@07Vy@Vt>~9K^02drQx~v6pv)2(90~F^;d@bD}ue>*@d61HgxYWyAWg zr3di6S@K1S#Q=QxTqC~O5xjSth;UH#n~Cz=rUCcoT}Mk-;E4F2;luwnB8vLB|Bn*! zPei0|`D3PG!lGD1Xke22B17hRHehT@7Li)q6p*fY7fK_zFT;i4Q1EXpVkcOL>LsUS zjnqV)>$}bOyp<0tD=Pt4f6O#61F&&RIeZ?YC}Ng0)P%tG38(CgSDvWz^%o23?fQz zie79OB?@o0U`ZC4mz3`?Ux(0!*g)j~x>PEW=pHI*Od^`$m*xnT@HMY^M1UHRAfyyS zAa3gzxRjwU1?Xu23^8)69cGHsTc{W5FD)u&S23)ZD{C(%e6FbROKm`%+SZMoNj8J% z*j9m^-q%RA4k4Tfg6Pr#gs8QE5H(Nso(8xZ+Yd4W7N$QjNWw2LpG5oKZqWb)B54NH zR)Q-0eJU0TF!Cfo&O&A(`qb65PAq;Fhw*<7X=PqFoY(^)I&K^ zwCP2o7P|M~mT1eQY59kZ9q_J63 zPv5LFVP#!T`}KY=jV_(I>`W-T`DPx=v(?1w)kpkHCxM)W2`o>C*B*eW4@@7vDM78= z^#IN-POKkU)72=Jc^h`8SeU1kSSTP339jFVl}yml7by_f3jCoDY135GNdWqgF(p%Z zq^ZC{=1N`>IA?6tAbt~fjPGjh7l89c$wp@H*0qbmF3-Y-2F}WzvW6ky1D=2lVUMpi z7Hu7HGK0iHQMz^B?11(?LB@)=deGRKhrM!-?1iz9Bv!1Oj}&c`rdhuQI!aMA=cY=a ze@1IZt07nQ!TlY~!q1|;K3&Hik|-9+@DEa!s&`$%kc&r+T?_o78w}ulC|K2?u?gJ$ zfyR0`m*=*+(TvZy`0!qakWSewdhGsN(Veck=UO|9=7y2qDT)m(f4CF_`v_roQQ0?& ziP4ixm3cm8jd!w4niiAq1JelV*k_FL`XjuVDcyXs>a3y`nRYY z8vo=&tq->Hf8|3rARi_G`LH$6v6UA%o)JZC2prGY`Su1lp1~bFQRxEr$zP>#5-_LA zF3lKRgxE01E&v^3k>W=;@9Qk9(2Uaxm)=5;j{QrUdSd$S1mJiEDsVhwZ(UoC`rS4m zTa&z{Xx0vRWy(_vzO^`Mcka#Ce~#uC8q1II+#$Z%cQrCfxc+T0p0f$-(>ma1pIBop zIKP;?|E{EDj?Pi4?sa$|w+a6$r}V{_p%)J^GINfCC05@NwGEMfuiP=*iwc{6NejX@ z^OYselu@L!3po(gJ22|&Qz9T~8VuOJ|0OpRAqb_5n#HauA9qw-92{tEy|{DfA2e-w z)58;Wy;3ceGhCrASxQ=){-tRibcsLlk_^c${z&tc9Hk{)<#Bmz(Bk_KLcAucFSd|? z3UN!GqJSacEgK$iTrnl>Cpsh1g&c5P(Qch>4k}$`7a`Jg86U~S8A~B%b?pjmUz4*O z)4OU*TTPT36auVFNl`z5^P%+$66bt7hY7(YD|ltv#L=Ixf(hh98={;=wz-ElURb1K zRxS$kjPV6UuZ*B)Gz1%ssGT*yrc^=pYe~w3AEbW%KGdaSmg`scWqaUpdWR&1Htu5vH_EjZ{XDttP8#;Wilce3>&+ zq`wSHyP^?A$CCR{9>UBoZ#P;BT$UIuoyr~1Z%nexOPm{#=MPDLZm>SNgyuWboRqcvhA|6OxmcWz^^wvGlvOq0>Q@Ku51wq zjW(0sfHeQb4>MMdcIKBre@Kt8ZOu^D*PBR#brSdD>1QD1r#s8b>`exegN4!Ek?~^I z*AEN8#x$^35|@H4VG1deq&2TM3X3kJR2@C~X$R-L>PIHEu|YLA1H;4hA}OR{DW~k4 zj9u4=hg=mktX9)#PB4JTFRnzD1o}Z~GLE42f-(%7(d>xTUTgInU64OOWVDNq(|WRa zN8{6vw4}kp&ZOg8Ai)JZdsFXPI))Y9vR{+_++VQ~WLIY+9s9YCszmj%{MV5=UXd=V zYD*en!>ey;vyV+ya{Fq)ZAF=V=8Nasik~H^IqlMqks^N5VIUU1%{siv+dS`;HfGCx z_sV``E@~e^F#=kb46E|&;uPgha<_mi9pjAIVf|y3%#yF`=71JTf^gX&=JsZ{s??tj zaX;NO@+YRqc$&6~) zZ+>_#0(4l)O%*sbm1b)CeHiD zD5ai+pZfxt%Wu&kJ8(egn?oQ(jl-j!|(t;vt8< zb}Q#~#kRQJZ#!+(AjDU7dORnx>ApBp=k#fH)!vdSc$u9B#D(N=0Yrl8MLXfVSC03* zha0>^+3$=|KRMlFmwI!$fh$CumV@fO$z~I8 z;@$OjeWHl11!NCqDM5%@)3QUCc@I!vSx3v+dRiz+?oS~m)D3|Qy&b0Kc!)IbPzNIn z!Dv2!@2%hLQo-G?SE>FLvYN<4qg>e!@vnH;D{X;c5Q8BH#6veA9;z?5;1)Jiz5Wvq z7nyvwIZFb7mFc%`|1(ECvkHdUpPtI zTwiq6?|BF*Qr9NzZ9&igii1wFS=a~6{7+P~ZluQ81p5P%!ApyVNuk-|!s}gRQ8BC~ z70jgevIyP~fkvZ?{%tIJ7h#LvI*om9NpL}4_JmtXzn{l;PAllrqd+`V{Qo5$g5Q$= z&v^LfV1{(k^TCW@Qx?cr9B_jeltHGn<|!K^2Ox+Hwl{7d*u8MLCwFy*Lh!gt(c5HzZHOD36{ZMGCyLIv=}gt9l_Ju64QH00-VbbeQ5|j zmoW?D6WA0n9jQ2T6UGwO`;y`a-UKECuM6EF*My;vdd#Mu!}Pe&=Bg=&8OM|mdrkU- zfag*}Y_&MGlVw{8PGb~fmJ&Y_DZ4bf18476Ow}tG8oIMQ86}1y^&NaTp7R=tcX?-B zlJA*ZlTW{VL}!od)3v8UCSzLB-8dB2TZJ>nnC ztjVRi?)$n=RrmMncsxfFx)N$;h7G^eu39#jj2LYC3DP~-VxJYR*(sdZWTe}7eR}0& zZgTq?S-7Kso@RH7K0>F46mc)s^_!OCIe2CoM?IG~f@J4eV~EWJ!YwUTCe(Ct70S)Y zF5CgHOe20D;^9*qZs5H*U-!FjWjLvQgRWHz$wI;G-~c-!K&E0 zF6IDxX)CM;BNe+V|!j@cCupv4kd4d7Q!=ylF5?|{{xR-RU#mY#Y37iL}?C=fy}hUc86N_lrZp(HbB=!_)wGn9OSg=3VSJ_C*k z%X1JYEOQWXEQ|2XplHdm(4x3Hm`R$pARr@}!R*4|V(N=JXK$&7AcTy;=gK?t0##qG zY(8cf0tJi;)IUcRU((dU+lRmaep4)wOrS)h3rT?~1#;pug7_>gwS&)901^E=f+!1O z3(4Rvlw+a-4rj~(B_dVtHGGXji0?bjV7MClkP?tS!cs39l>mfO3drj|D=c!W6yq1{ z+f)$ClXf2^$O0ju?>j;uIlv)e0vuvz3B-L0a4UmS0>$4TS~rC6nL{L@J+bM5&Py+Z z45X2uT$cd(&}ME4(NX~fp#>l|)$Bom!Kc-YnnnCBTc7;8a?6)iGv$onz8K3+=9=#w zrxy(&kZRD^yAg|U%kPh#2F6Re9v>(Xk;+%WJ5_#vMZjH31-MH!5rzF_qapk?qRIRf zqF?$;L<>=TpXvGugidIX9}o+F0yi@9(4SPJe>#_9!C7yS`AY#DVhxZI9cbf$kO}4x z`<3p@k z)e{l(6knqJNhzu#p`|)CC%@-(i$aNBTI{&oK1^auBY{J_R;#+1B`qFt1`iK;e$A&{ zwo?3pcRFz>xNID;Rh;LPPxs&Mt~PB88Z&qKF;l-MOq(vbLQhJ+!ylOd^M}`uFP0_p zRt$4r*jr|gz1+9~w581%Rg?U0a=-SvyTxpdUN#8s&LOBh(8;?Xd==b2ldx&UL@L_< znRacj+?s7nthnkDxXpik!Ze*?BgTEL3pZM!*gMcLjL%u44y{P#Tj{2adtqg6sWy~U zX_$!0AltjX&RnrYqq?irRrwV<{P_$9Qz8n4wQ&`a(a2clWL%?O>4Ei?(~& zcH47zc^){c80Ob{W#eydGFw6(nv{idhQPOdKvcr@^2*Y2#uJM=c*(^pVkxL3@fT39L22KmG}oe)!!Y zfgP#)7;ROB+IXY0-E^wor#vTxJ| z6tf9hb2zug?C;ikkG18gIh~fj;=)Vzm;59b#5<3B2(WhKu%UrrD3U~aL9vXFkpC7O z53?H41}iJHXfB6)>mT2n?Chggj|dSpyIADDFE5>1QaPM`?;t6q!Jr_|U!L_UZq+*{ zPJa6+H8*qgv4iuswt(D5>TJ;wpKZe}x&o(aq&hP9E8ieXHdV1pae_EUPE`r&-5x5w z7c?xcPX2(P#uOQaJ`O1xBNRJJ()LPiF>5qae%BZLPsr(~pA$6gA_zu#&VM0p+|@ep zYMD{h+D{gHvv!uDL!CK=dv$`2EzUdE;(B-1l#;UH3-zrFxm z`>Oq<^>}5*$C8hjM`>sA%+geyW|TyP5Y%4@mQv&TT2Q_!OD?;R|9{((yu9!LQBtSCZBgG@VS&A;dJJ-+HfZQj-W6 zw1b-8I!59|A60y2j*V)mF)w~yWH-xYDQ}XYI~y)q@z}LlcZKsE*7R56XQR@c%$As& zhfqUo#QT}<3Aab9lug3LqV3I~rKD^W<_S{NM|UGK)zzOLoS-`Zp` z{WkB4W2VYns+BV9&D|80dcB1bCW;7-hu31Fzh=sna`88^HO*K-qR{h@&w?64IPk<2 z5Y*Ix1vQvpK}~lfJv20FFAe0|l8a@ibvQ=Q>-E%KM-c;$Wv279)#dbHjn>r0K_SCy zLjXdAtQ~h*t1H>0&cR6B?*$XJ5_N3d3SMjSusy}$1 z1vNqbRd1-d;fGK>@U{EPH@;ldWw@;ncw6CniM{Z2U3a{&q& z@6S5|1*hC{zY7|+fTsOgJ5-wlQ7|{_sbCN6-e)97?F7c^f{*b)Kr9HWnkokeL@q9` zwRVq14YJz9VNQ_Aq~0lyBjc1{=x$2^68<;G_%)bXOQO#3q8jsZE7$o=QG~T=$U8TB~~wF<6kxBfb* z$o_m%acZ$E4LGR?<76JopLN|&))imy?XWCzyBYbGD82(#2p%}8XwbAp(jw$jBKlW`4e| zZ^|5aiR)R46z(83Nk4Xlag;4)K{3GyoK!R#?yr&-J1i;k>U*sQoKy^7VgODm!eev0 z-_vx_OF2!=WL=*QU7NE&j~&P^@TLF-BCU4Lty=HNEe_9UNDAe@*e{t+kZJS{p&sX|B?}(SEk?!@f@eDqzo-cMHv7Q(Yz5r91c>t z`ktqrF;^=8vp~cvl>Gc6F9`2;ykmaW+l>2zo8}m1$KtK_;q~#paAFXeNQ@t3 z-zQq~VY52v7+#T>ldjRU&s~tXYO%Py9g$U;LZY`7%H>cna5fv)`H?6RHJE{e?_Xac zL7SF%H)Wc@U`Q9$Rm6KxLxC>5a?CU0o|vMuZ6{FfX2#2#VL%?t%8Q9Nbu3Y2{gujh z-6NAIr(M-&89{nP{)12NbT%!4X-h*U_F@2jy2(B156UQOkXmvqpQ;&*GhF`-?GW^@lQH z{u~q5n3_g+3(TjMht4Q#y0Pn((<0j9qaDAab{6-MQ$@qXUnEU)(#*H3!=p+Yp-#Bl z4nU8%v~Udd^Cp4{V?Fe($eP5ioK$rJI41^!Zhr5QB9IQ~;9j^TLqqw2n9eBFmEKbD z1*2wsdqpqxY4z74LgF@iaE8k!te>TiEXPDy!f8X1jT-u8$Y$IeYuv!+ z{+#QUfi)kf5o!NpPNe^TKL4T~HumNgf6Yv*Qw^NvSg<0DS`FA`C0j3>>z%YaXUQ2{ z)@(4uOtaKkZ3x#pk5ybQleak})@noO>6q>zpI#5R5K@Ul?}Z?JZb=zB>=LTKKL3$X z3z2Iw)c-M(5e-dH;Hu6bILJr@k~?Rdw|&3(#Ej&7K$UT?6v~()$$gobjIra7S*lC% zqfc+@FYn{tl)IdD`0ln5!;QYi7yqKnGOhA@s+u4hGWn~EOR2C#l zh70=49=mY`=M3K!cc}O3CHs00axsjkvDxNF-mGS-MN{@W@>~oY;^P|P2M8h<{n)(= z^q{SKsAYp6|MHj_?>_voLO2pm1;evkZ&fskSsPMf9CS;6-4-FS$eGLNM(>Q7mu)Nv z8$`{(;z={Oi0`W1-p=1=&*jH$!Of$R9~6O}A73EV%iTk z-9BGGPJU`V{Qf!c0i_3a9A>&?rvmu3G^0zVQ>FW&Az#*3uw(f$a2^i*4JF#t)igm! zNXJVDj_~{)`FHM9py_cjB($mUm*UW9R-D=WNCd0SD*Z9IyqGTE z=Bu&A6W8yDU*(LYDA=|zRNQF@dt*0Acc3>&j9}u`(RwGr_l0U+oG)4KOLT#rq@_sS zdp(R}(J7Hz^6OdE>4`K>iYc7O;=0(7 zb_z>-JJr2LoZF~aha@k`>0;|7{_{&)YL4G}G1S@oDv?wJzvXBWLccb|=_`(R(*A-L z{)yEPrQO;G5u?H`midPKREY4XSWBS*P#YpUEQ8+*v-PDC8GjQh!a2_T3+hC-ogZIs#j0{wj;}TH0xPZkqO@N#EuT*bdWWR-0 z@eeZl6U6H5ja}~)d^%P(9q3%#65{EuARpwy_G*!{yuD!4~RtXCX!{*$$+d;gW7)ql~E+@d7IbD@`jz6v#3b*Byro z&E0ikglaspJf7>lYT_x^<5OqN=Z}`4<3LIRLM3VvinjQf0ce=cG@d2H(x=!tc~(+m z!|ba;1EzIGq2V8GD>WE9f}7`G$)tCLMx(wszRmf4RFz47vGamm%%;34+~9HT4HO9D zlZ=D#1-0+uE*W0QL71nqM`8mfTumOHBIQr|%Pqd(cI^Eb;ttLKMcF&PXZmhi+Ob`+ zE4FRhb}F{bifuc2;-q5Rwkoz!aZ+*Wed@P*_wN4iuHEZzxR3ih#u#&4a|(3G@}*oF zQnmKmC>?sBrtFZ~)v7L)^!|gtJUy(ld?5N1VgDWeSb-pPF)^|=Vl;8EwKX$wwQ{g$ zv~zPc0zS`Y{O9oSf8+0;%iI47Q>!($RPof&|In}1Nw40>V}R2Sql=*70%S1YFm-y^ z0%e|22rTplFwz*-T$70(1%)l}AB7Ek3*un7eo_mZ#t5kS-5%5`S>Rtrd(K?J(SIAp z$=u8Hnfb_d@juDQ&h~vgq09$89jZc}rNUNXswA!;F4@fr!a|p$lA_IcC_+c{e|E}B zr!bg-BrYNcVI+i~JIe&|g@HUR$V+eR;+v7>3IM_NveKB3il!ksRk>x`r^yq~kD|5I zbizniL)e$b5XDwvj=87F6U~k~NmODUIY$>Y5G#W)M@_gQP2oA@B=z2cy~F5enxbl?0?Xm#5r%Xp^U zJtyhXOXRo>DOK*@XDeCuV#s_F3K40qEUq#eRlk3iQ%AuaAL5)Ie2HC(E@SzE+=fzeg^TP3|@r0ttGS8@m`mlnsEjbM(GQ8gL z#DMT7Hhw?CBny$&kBU=Cs_%GiM1{6tEy9YMgK2hZ!<@fBx|`BAZg?MTuL@D~i*{P1 zeTwG-MW`eebWbQODOr8g#ESs*OVgzss4-z2fNhc@({O}U1;YXa!$P>lw;BS|lVoRO zK-hM0JZVlxvtmh#W8Vq~BxA7;%tHT7&l{Xo&P&kcCj%JWj#xCQZ))nr&Aov^?q$j8 zCzO|)-}RH_AUL;zvgs}Z%?)DKEq&@ef#3XQv&(Y7t}=&INqT>g(oWGtp?~HCwDdm##x4^0{Vs02UvUMw*ai$y25Z%3XWi-96RrVA4Toz* zbihQ@JPZenTU(TD?YJThshsD_b9DpUK}1Ji3UBie~U)BD!)e;+ST(hvgUhRzm%m&-4H(00@|788*DE^kJHG`3f zHr{hzfiZ`)XABxhK$UZ!u8mHGgx_{*DtMjUkJ|^@>;O~bl{Qu}StCrJ-JLJ*Pn`1_ ze0ym87r*<6V(p5I;n~A=TTBLd6%fi&{cYNAAOhEPP`3)Ld`=sOWMBHBCva|%>bvEa z1jsMUI?J#A^%`wq@4>nN2tb;D3&8(*mh|5M{NLBtt2J~rf%y9jlgl8JgYk$4U@(W8 zQk=n5tJQ?6*mHo&7O;KON7_RPLz2$WTYpuYYt~za_PP!15UWIHcs(at=!tlflPE_? ztvM2%5wOVb?(Lp!ed10w{PX;R1>*O-6l?^migrv!C#99-XgJUi0*$7ErqShi(11nQ zH5l&kazjWoAP@obYT3F74ND-f&^;Z_AfR_78jozCwhnLg{ItwgrrhVj|{oVHtoCC=JZ!ljF$kPTOZ&U6JkpDu7 zF;AmNdn{y&F!qN-*k0SlX!U*@iBLshesy7kbS6?hagx*Qk~-w9FsC}D zo=5bsU_w1_LRT2s>&^CK@4A5g%&h6ClHM$9-UbG=sL5NZwl>;S>;?3(KYz|3m~@HE z65|=JdZ)-fv(#U6X_%dH4tuK=Fk)HNN-!!FR}?Oyu+FoWf!Xvm5u>&EWEmENl>Nz4 zEoJwsj%p?le-UgQsR1tElEx1 zM#+SJxy_f6g+UrSZO5&_c@)?u#Th`Si=`IW`j@FJ-PEQg52m=hGK8{H`@2VA&d?c_ zOG~EXatjHq2?B~DnF7oTk3e-2L?|^P(zsp;rsSjg=-D30`8rRP<9F+2u}XGUEcZbz z&;MX1wL#CbcR;t=JluI$$h3iDVCGUfP zRcO#CSz8W~6vi11ROqB9MSbb)wA7F;cef`*`}9j?EXPvFt_))RX0d-Vy`+Gbqk_^D zgiLVCSb^QOMMQJ1NAeqOTYqXOZkGVP=>6B_etg>y^)ccUmP30X1_O<5SEAyj%185% zkLz5wBt<=zB{44a*5%c?wp-zGhK|r7{Z*nR{OH3|2*fU#cl?C^mh?l^pm22|Vc`p% zA$ZFrG*)AY6HIX2CE{p{xH9so4!%mgzH90;3Rkyc>MN3x{~#N1Nfg1G9O-wkzv1Cj z9KT`*5R9Gw7L5OiG5&l0=>Ham`VB_OW`msS5OCsfUtwdLWH5B-ntZcB?v%wwanc<_ zOJq5=$YA1T%8_4pgNYbra-EidfCS|sAsBU9*ZlMKuP(F?z*KUQFy}hSbD4d~t@UyE z-75I~6QfUj3i2i>${&N21`X(oDb%{Ej8%qHqRxn;l!?SpEHc@cxod@me?ST5Em*cH zi-Ne}+AXngj~-f4(BrbPFD0xWt>#2 zIVtz1ImtL1CR{lw4M4O1ZkMtKBkONW=N=}x@ntQ=24}7d3mvuz^ugdESvq*9LrhzTBj;Dw7w8ViR+qMc$`1U!Qt}Q{(3;h2PGApdni9 z2w?lT_#K3f;uCIj1%w zwIzd_8Dxk`MAqAH;Oed_F(Q@8twTf2D>}tU-=0K{kU4Wqz-`teFED)PzFjA!>+ed{ zWHJ6=Y#+H_yLo=EG`l`yoD-OCx~ZpMHp9h|SBEvqx=9dZ#XJ5@MMq*rFO@Zpow~6_ zT%N*J(!3kpGO?8^@6&P(O#TlCQQ&=92QygxALqu;ao=TT7M%9ok~W7LPdA7R#B8fd zvV_KNT}GuO#n}y;MUL#kstd0te2y8V~5g1>wci-jBXRco+WE z(h~k+XDj9H(Mj4mVZO{je+(dLIReuo*O@rp=cn5DV~aqZJ2@XB#Pt*=s6i z{2~@`SG^JC6N}&oL)z4o-XBZXdvX9U_XcakLrzR`q8>IkaCoH71wT%#A8%hLzK$B_ zd^L?wRH6_NZ+u1|*muz*%QRW;Gt^)aDoO7vQ`w=;7CcQaj`)rT$BmFwh2jwBNSVs% zt~{M<25>4Y$iCPDptT~cD~}WI!&~mlszoERMe;7C8S&}%=@r7n;KU0EIfC2Gzrl1A zMbH1?EI$NkLhwUYUklm4*b3QM*|-@@M71ZSxl-ZRwWn8CA}oB&y17(FAO5QiwZ-6- zFCrReG^vqPA1Ufo4Fa9S;KIfSma<4)V9&3hDhr1S?d?I00o7FSu9z)xa?V@RWJBQ< zX&h}O#HUOhaC3(9OlteJl7%MYanN%dOq)(bI<062>o%$RH)O3D$V~8x&&CFfT|jpn z?(U*Ly!52i`aM~W=L(RszIs{vn4hEmCF(a*e=eX&ZN?b+ox1<~TV%`pN_hGy_tR(K zjOGL3guAj^li5YhONnasmy+3-Ag`PlFLbj+{j8SwE41W=LHHv?&)ln1UYIBkX`gBO z$~VTp6m;-Bhbje9ko@0L@IR4<|N9hHwT9hackIuwRyW?TmrDbs69sg~Sqn6&nt_;9 z9A8t?7nYnx$n}iQk`S{YD*iwK>4$tbjJ$+uggg4#W}n}3<8BDw`nkV4oii~`S=aqNVXQiViT_pJL=8I=tx_$ zq!?=_ZJlnKyn9YF+J6eKoB#sQ&OQ_L9K=dHlS8YFa3ZVhv=1%U+lM47Ea`Mvn2)(e z7H^ZRs-?;XKTUZ?>)_f^0`eh9*~Di?%vD-v7xNLDXt+9ep_kR~XBC_wn%w>Euh1 zdvVe?GP6VeUO0g-{=wa+hvuJ9FBA%;56MMBstnpE7*+wU>HG)Ly+K>GP~ zPy*?%>z(xVn zHriV5G>$HXdal@1neXEfxZwV?njdV1!PO7K_|IO}NAhEm$>F_o$<$n<#)LM6)+Vd= z7pnCmJjG^)qqNIQ$ZWcKSw*#gJRu^Q=QDsdq@YBNo@{*&@?47aB`G@Mwdi*VnLQEfUt|=@(rTRC_YOXu_l@A+BIzdSbZ3t@{57fi;nIw6zo|4;UY^|TE_ z7V{WbyA-Lf2-1u4n26H~A|)-+ahf7W)zW-CM+fzaU`zwWenAHDo<{LY&e`ebeD;d) z19QPs-SPDYK|gutiQ*~k=lC?~;!h-ai{c+N0Ejo;9-p(Q>YlbPgTMP5)Bi&*@$VG>@>Ei`zR z^w@da%3!NN>SyG649jw?u_T6EzVSB|RLj>ZcMg6#i$nTK6(-GlPQiZj&-1&*nIrTx zAoPg;E%g4AQ1Jga^zdJypnk(R#bns5ig-auN}4an0*$(5{TyZ#MTuQOL>w!_o=p1x zN(C!sxXwj^wwQ-xsY2l*c;*zS1pVJJ&SQ%FP-oX{Md8c2NLe>suG>%E{Z9_Ivp?=; z;{_od(HDjJei&;FXGAqc#YDl=FjTe`ll(9y2?1Fbg)t&R&gH{jDA=t7>c2TlkKQoi zb+&cOqdgF0Xs^@d0zhy$0|7r*A!*P7PoA*Is0;Rz6RtFYg`>!*Hea;~#2}AE;=(Oo zA&)^{GSAGtbCSG)SglZ9=eN}8t-CCtHkRrdzr@pwzC0;5eg`^S>dCR$k`aqzvl_NK z^;~*xbAS83$9B2`u|mdp82s26vBlvWBb84N4huHR^hnBHam2oy5%eaAyY;i$Gve>U#cWnMi5a?iwedAbG=Hn{P!gjt-}+x;RWDF45I{|1l-OcqqiH#t z^wPe7v2WPMqb9s%!RtZ|)kC6syzfR6OR8$+W8c+x^feFCdw1xxT#(UVKwD{24*CM!~iqm{YC6Sl0W3FNmxn zvw#`XMptyw)f{6_YlG%Nw-}wFctl~}WhshrE@D!B#Q-P)3B&=d-QmQfzq-{C$%Wie z4BNjxf4w4=!z)sM64znAXI=q5@1@%r+KYnWV51`)axt@dB?E-7r=90X#+1G#h+D|w*4?eG<(I8k832NiI!o~Mq&Vz5aTm5k*RM`vJ?W#L?7#LV z?)G=Z6*>K!Q&sdT(tswIxF?*#zmM>ilxJ8`EzQMqVz5gD!8B0qolt~x?n`fU!bS(g zM{h6%1#m<{p=O2?<=}|pzw>SMO;_g`et%zc;>3b8-JI&l={e=b4kdbjM1+43eV@<< zPkQ+!YskDj73qC>F2ZSuZ!l)1xn|WhtI`L2+6m2xvxPYFMkWV1BGG7Zy=%df&OlbB zwR4mfo1sHb7J^AA>ligRkK!J+Yj!t z;@rY2=%9T4m^4k&lLSw#-`N4adahJhvfTyK3>#44jzYY7g80J^(-bZsOcO|WV~r9u zyrSG2c3e%~d6`AD(aatDP`#H~HkP5QOr%?6|8~l7tJLZRph4`B_jo`O+5fVT=bU{0 z2mY@HRMGTjtoa`!Lbm^4M)H3yHvTUe=-vqQ!lDDZx^PYC)Q73)b!vcN5wD*RG>pTi zTnA{&b*|&3e`R1X&p|oSVKh-W=>S*UqW>c@H7iYCBVK`!S`IVo-|6tI*Nn?d`x8a) z`^&*B$ZO*5o;;2Ti@9mBX|Wk!%56=I7Y;lN=0~}3Hj%&H0g!araAdBuH~W<$ohum6 zbCq)v@W`B=p0VK!kZ|i>r)u-S!ky8k@-5Up&GtY<6tq-1_NYF_m$qtSG!vTA1hrcZ z5}NIPdW^;{J>%r3Im*5jb*9N-hL6aA0A79rpMgXiQ29M6YyFL-q|&rm@)M2aM_a4s zDsO-(-pgj)!M#)fdYms4!#83+LTQY~tP1kRG^?Z{qoZWJqofw&^Ks>k<-E95L>Kke z&Z_v)Y8I>THLoz{E;8(5ZeIc4kOz;-vsuZ>Gp{XhRS1}iG1cRyj2#e7Dn;j)D zL!qW9|I(<5aT^+6fu+J~yL0!%!Re#MSIB#<+921NUw_~LN>FJAe)wJRpJxYz zsd_^vdB61Ol<)`6C^MNIKCa!3MA`?(rD{unLxmQW$Czg4*Z-X2YyCiJHce)C zYO8BGyL8{afIGb7$#-68(D+L)DRAYXxc()@+T>z9B{iGY(GRAyEUUlE-kRJ*)v#gO ztJ^YD&0M7Qu~0UU!Yp~eqPCR2;9!S`Z1*^nAu(Y4kNeu^xY61&9Hie*a<6^j_V#Bj zIXth45zU6p4aHcR6V0k>@kw?GqHZk#Z&ZGi7xKc)F^6Q*bo`2i7(#Lw?9!iL-QNk5 zR&>7ud(PL&Pmo!?qL(c4;CXG~>~H1q*IXb^mQ^M$s0m`AmxU3^l9c#`xtylkg^c{3 zsUWZ_E&;EWO>W53nGXzL^UM0>v(nUD32?lT?By6{O9%7yHbiGQHE~+y)rNzkFzI(Bbt#Q8AHG!EKSZ={gO5Cz51N~z`;$<&RxP8QZ*q8&X!b9@ryjQO6zWf_eyLi z47Z7mp#;N3eRYj`l!`z+En^?G4dc|}6$Vm}R23)(9Uiht?rOzC2g$0*lX8`7iXY|) zv)UTEPBUvySx7rm_Gg^9aaTpV5X8HJDC!{z_SI=7*$isG)hI)Z$c-2b{6XNa$GYLI zj$V9{Wpf0{g6avRvlG3Gu^qg?vHJ5{4*m`0%NupRt^z}B0@9qH;SIhD`uH9Yf-J_V z<<(@`Csrs5vT*f+`%cpiA(GG1v!QwmT4 zIr^r4a!BEDX05=_oUE_O?@YMa@{|T5USVfL@KmBIIW7)~{(Tt#Kv3`Z(3Vb{u5W_k zFP30-wWsFU9*+SO7Lm_`zs5K^in3mg3c(QuP?+Oc{>5m0DFFJ1QY)6;X< z@paWL2=$!|yHLQJ!xQ@KI=4&^&qmw4#Kb(wB<=$ix&(wD>z>c_4E5I=9?doU(9FDI zPLcCHZgd_Z`vvyJ!njLYg{_1EQ~Xbtgqe&F!~TiuZ0$xSVx>3h75mf<3MChj3osSR z@Jn)P;!}iBRdWpm%oWTM*2xI4R0FM*`c#tgvx;z+ibM@iyAcK+9B%6deaq5srg_D& zIVYMU;R6nJSh-sTpSn& z9?POzsx$%PK4j0$6ezG2XzP_U#2mOGyM&S?KqlR$!`K({e}W0Oy1~BwL1V`cC5(eLQTK{}8Dhe}HJQWWU5~=w&{jlk(vP42Ts?$LMM2H|A5*~p1b+X$ljgtt z@PC(N#Nl2FQGWpCUs4*Qe8K-U3z5RlRVI1>Agddu0A(QZ-9EUh81{e9LaxB*#fEWM zlYOrEpFCz>a$T;v#~r?ZzS9O+FrN%Y!zICupckVTW9Vt#R0c^ys=}3il#4|Z`R5!1 zV#8fP$4(LYqRYUVd?-MOL_pUqkN4=174PRzg+QwLntq%PcwrdnsojF@(-;f{MEy9` za6$x@WcIu;2Bj*og++!ZBg#D?XUnlqju1o*MB^b0C1g)O}6eut2npy3WC zE;Y1Nk|WS}W@Ka-VrGi`Q3jR;7aF5KTBd=A!@2GxR74cHaHNk|vjF5QrA7$=d-E@} z!$BMl5km!LRwbs&oR}d$je2tFFvJA2UZam5Mos9Ws_Gu(jE7iu z_6pDoo=nr;==6g=zKb5Y!OpY1*0a#?)+XNEDlMk9&{Xk-XOEc=!QN{z9GqeQm4RN; zv*)3pu!!;QmtBkOu#fRpcHv9oKaRmJrCr0URlGG~ur!ZD*{2D~)jEyr29cr)iYaTR zi^F)xAWEkSQ&9(b4uVu6>0MZ>kMug>1kep)t51KKH|3nTFc9-*|b9rAIqC?4}dYxV7ZvQrAzWnl(CVTjarapoenbOXccyOMO zUE>@eZDa}Qr#_`MQaqLog^}5%OLDDLAU$8;d0u2CYx&mtDaGYc2v7F)(~x0Zu-}57 zKK%ol(bjNe)g3;+!gz21Z0Xq?yn^f{TV-G@=sw4mzAeFoyB^jdE;H_qQx_{BfgvDFV2_{iJ0Z`b zGBOnR6-nT?qc!mE2KipV#^0(;ILyMxSl|l{FcyT|c-Mu{+7>S(^=)p(ySdjh$nKQe z{-D2GAKAX1PXkUwNM`#8mUdN}4p?PQTwY__O-5~lT1%}XRBD4H0Uckm1)Hk`TxIaM~8D%X78c5$6VbiZ(Oaxfnl9zgRcDnZ?}rs%0A8s!(|nU6ZQYdGx}UP%zXfXkf%K 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μ9^fNGx*#$Eq+Y1BHX{<|CL0P&`+c3wkyV)?8- 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

    MT*k}wz9>J0Fb8`Byp4L}n!?_7(q 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
      eY4|N6dYl5mUv^_ObW*y~XG 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

      PRE5=1nmi4iI3%XLl^dj447(X+Udf(1 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%0AeUw9fq%ox4nvp{wY{fNPKMEpx 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 Ld~B^qkoo)%4QZXo literal 0 HcmV?d00001 diff --git a/examples/scalajs-play-core-react/client/.gitignore b/examples/scalajs-play-core-react/client/.gitignore new file mode 100644 index 0000000..ae3c172 --- /dev/null +++ b/examples/scalajs-play-core-react/client/.gitignore @@ -0,0 +1 @@ +/bin/ diff --git a/examples/scalajs-play-core-react/client/src/main/scala/SPA.scala b/examples/scalajs-play-core-react/client/src/main/scala/SPA.scala new file mode 100644 index 0000000..fa289f3 --- /dev/null +++ b/examples/scalajs-play-core-react/client/src/main/scala/SPA.scala @@ -0,0 +1,13 @@ +import org.scalajs.dom + +import scala.scalajs.js +import scala.scalajs.js.annotation.JSExport + + +object SPA extends js.JSApp { + @JSExport + override def main(): Unit = { + import router.ApplicationRouter._ + router() render dom.document.getElementById("container") + } +} diff --git a/examples/scalajs-play-core-react/client/src/main/scala/component/Menu.scala b/examples/scalajs-play-core-react/client/src/main/scala/component/Menu.scala new file mode 100644 index 0000000..da34659 --- /dev/null +++ b/examples/scalajs-play-core-react/client/src/main/scala/component/Menu.scala @@ -0,0 +1,13 @@ +package component + +sealed trait MenuType + +case object Link extends MenuType + +case object Route extends MenuType + +case class Menu( + `type`: MenuType = Route, + text: String = "", + path: String = "") + diff --git a/examples/scalajs-play-core-react/client/src/main/scala/core/material/MaterialComponent.scala b/examples/scalajs-play-core-react/client/src/main/scala/core/material/MaterialComponent.scala new file mode 100644 index 0000000..d65280d --- /dev/null +++ b/examples/scalajs-play-core-react/client/src/main/scala/core/material/MaterialComponent.scala @@ -0,0 +1,44 @@ +package core.material + +import japgolly.scalajs.react._ +import japgolly.scalajs.react.vdom.all._ + +import scala.scalajs.js + +/** + * Created by Janos on 12/9/2015. + */ +object MaterialComponent { + + val rc = ReactComponentB[(ReactTag, Boolean)]("MaterialComponent") + .renderP(($, p) => { + p._1 + }) + .componentDidMount(afterMount) + .componentDidUpdate(afterUpdate) + .build + + def apply(props: (ReactTag, Boolean)): ReactComponentU[(ReactTag, Boolean), Unit, Unit, TopNode] = { + rc(props) + } + + private def upgrade(scope: CompScope.DuringCallbackM[(ReactTag, Boolean), Unit, Unit, TopNode]): Callback = { + js.Dynamic.global.window.componentHandler.upgradeElement(scope.getDOMNode()) + if (scope.props._2) { + val children = scope.getDOMNode().children + (0 until children.length).foreach(i => { + js.Dynamic.global.window.componentHandler.upgradeElement(children(i)) + } + ) + } + Callback.empty + } + + def afterMount(scope: CompScope.DuringCallbackM[(ReactTag, Boolean), Unit, Unit, TopNode]): Callback = { + upgrade(scope) + } + + def afterUpdate(scope: ComponentDidUpdate[(ReactTag, Boolean), Unit, Unit, TopNode]): Callback = { + upgrade(scope.$) + } +} diff --git a/examples/scalajs-play-core-react/client/src/main/scala/core/material/package.scala b/examples/scalajs-play-core-react/client/src/main/scala/core/material/package.scala new file mode 100644 index 0000000..282565d --- /dev/null +++ b/examples/scalajs-play-core-react/client/src/main/scala/core/material/package.scala @@ -0,0 +1,16 @@ +package core + +import japgolly.scalajs.react.vdom.all._ +import japgolly.scalajs.react.{ReactComponentU, TopNode} + +package object material { + implicit class MaterialAble(val elem: ReactTag) extends AnyVal { + def material: ReactComponentU[(ReactTag, Boolean), Unit, Unit, TopNode] = { + MaterialComponent((elem, false)) + } + + def material(children: Boolean): ReactComponentU[(ReactTag, Boolean), Unit, Unit, TopNode] = { + MaterialComponent((elem, children)) + } + } +} diff --git a/examples/scalajs-play-core-react/client/src/main/scala/core/reactive/RxObserver.scala b/examples/scalajs-play-core-react/client/src/main/scala/core/reactive/RxObserver.scala new file mode 100644 index 0000000..b252b93 --- /dev/null +++ b/examples/scalajs-play-core-react/client/src/main/scala/core/reactive/RxObserver.scala @@ -0,0 +1,24 @@ +package core.reactive + +import japgolly.scalajs.react.extra.OnUnmount +import japgolly.scalajs.react.{Callback, CallbackTo, CompState, TopNode} +import rx._ +import rx.ops._ + + +trait RxObserver extends OnUnmount { + def observe[S](rx: Rx[S])($: CompState.ReadCallbackWriteCallbackOps[S]) = + CallbackTo(rx foreach (r => $.setState(r).runNow())) >>= (obs => onUnmount(Callback(obs.kill()))) + + def observeT[R, S](rx: Rx[R])(xform: (R) => S)($: CompState.ReadCallbackWriteCallbackOps[S]) = + CallbackTo(rx foreach (r => $.setState(xform(r)).runNow())) >>= (obs => onUnmount(Callback(obs.kill()))) + + def observeCB[T](rx: Rx[T])(f: (T) => Callback) = + CallbackTo(rx foreach (r => f(r).runNow())) >>= (obs => onUnmount(Callback(obs.kill()))) + + def clearAllObservations = unmount +} + +object RxObserver { + def install[P, S, B <: RxObserver, N <: TopNode] = OnUnmount.install[P, S, B, N] +} diff --git a/examples/scalajs-play-core-react/client/src/main/scala/core/service/AbstractClient.scala b/examples/scalajs-play-core-react/client/src/main/scala/core/service/AbstractClient.scala new file mode 100644 index 0000000..9066b03 --- /dev/null +++ b/examples/scalajs-play-core-react/client/src/main/scala/core/service/AbstractClient.scala @@ -0,0 +1,31 @@ +package core.service + +import java.nio.ByteBuffer + +import boopickle.Default._ +import org.scalajs.dom + +import scala.concurrent.Future +import scala.scalajs.concurrent.JSExecutionContext.Implicits.runNow +import scala.scalajs.js.typedarray.{ArrayBuffer, TypedArrayBuffer} + + +/** + * Abstract Autowire client for api call + * + * It's send the API call into /api/{name} path and process the response + * + * @param name the api name in the server side (example: sample will call /api/sample) + */ +abstract class AbstractClient(name: String) extends autowire.Client[ByteBuffer, Pickler, Pickler] { + override def doCall(req: Request): Future[ByteBuffer] = dom.ext.Ajax.post( + url = s"/api/${name}/${req.path.mkString("/")}", + data = Pickle.intoBytes(req.args), + responseType = "arraybuffer", + headers = Map("Content-Type" -> "application/octet-stream") + ).map(r => TypedArrayBuffer.wrap(r.response.asInstanceOf[ArrayBuffer])) + + override def read[Result: Pickler](p: ByteBuffer) = Unpickle[Result].fromBytes(p) + + override def write[Result: Pickler](r: Result) = Pickle.intoBytes(r) +} diff --git a/examples/scalajs-play-core-react/client/src/main/scala/layout/Layout.scala b/examples/scalajs-play-core-react/client/src/main/scala/layout/Layout.scala new file mode 100644 index 0000000..e156ab4 --- /dev/null +++ b/examples/scalajs-play-core-react/client/src/main/scala/layout/Layout.scala @@ -0,0 +1,13 @@ +package layout + + +import japgolly.scalajs.react.ReactElement +import japgolly.scalajs.react.extra.router.{Resolution, RouterCtl} +import router.ApplicationRouter.Loc + +/** + * Created by Janos on 12/9/2015. + */ +trait Layout { + def layout(c: RouterCtl[Loc], r: Resolution[Loc]): ReactElement +} diff --git a/examples/scalajs-play-core-react/client/src/main/scala/layout/MainLayout.scala b/examples/scalajs-play-core-react/client/src/main/scala/layout/MainLayout.scala new file mode 100644 index 0000000..621281f --- /dev/null +++ b/examples/scalajs-play-core-react/client/src/main/scala/layout/MainLayout.scala @@ -0,0 +1,10 @@ +package layout + +import japgolly.scalajs.react.ReactElement +import japgolly.scalajs.react.extra.router.{Resolution, RouterCtl} +import japgolly.scalajs.react.vdom.prefix_<^._ +import router.ApplicationRouter.Loc + +object MainLayout extends Layout { + override def layout(c: RouterCtl[Loc], r: Resolution[Loc]): ReactElement = <.span(ReactLayoutWithMenu(HeaderConfig(c,"Hello World Title"))(r.render())) +} diff --git a/examples/scalajs-play-core-react/client/src/main/scala/layout/ReactLayoutWithMenu.scala b/examples/scalajs-play-core-react/client/src/main/scala/layout/ReactLayoutWithMenu.scala new file mode 100644 index 0000000..67b395c --- /dev/null +++ b/examples/scalajs-play-core-react/client/src/main/scala/layout/ReactLayoutWithMenu.scala @@ -0,0 +1,58 @@ +package layout + +import component.{Menu, Route} +import core.material._ +import japgolly.scalajs.react.extra.OnUnmount +import japgolly.scalajs.react.extra.router.{Path, RouterCtl} +import japgolly.scalajs.react.vdom.TagMod +import japgolly.scalajs.react.vdom.prefix_<^._ +import japgolly.scalajs.react.{BackendScope, PropsChildren, ReactComponentB, ReactNode} +import router.ApplicationRouter.Loc + +case class HeaderConfig( + router: RouterCtl[Loc] = null, + title: String = "Sample Title", + menu: List[Menu] = List(), + topLinks: Option[TagMod] = None + ) + +object ReactLayoutWithMenu { + + class Backend($: BackendScope[HeaderConfig, Unit]) extends OnUnmount { + def render(P: HeaderConfig, C: PropsChildren) = { + <.div(^.cls := "mdl-layout__container")( + <.div(^.cls := "mdl-layout mdl-js-layout mdl-layout--fixed-drawer")( + <.header(^.cls := "mdl-layout__header--transparent")( + <.div(^.cls := "mdl-layout__header-row")( + <.span(^.cls := "mdl-layout-title")(P.title) + ), + P.topLinks.map(i => i) + ), + <.div(^.cls := "mdl-layout__drawer")( + <.span(^.cls := "mdl-layout-title")(P.title), + <.nav(^.cls := "mdl-navigation")( + P.menu.map(menu => menu.`type` match { + case Route if P.router != null => + <.a( + ^.cls := "mdl-navigation__link", + ^.onClick --> { + P.router.byPath.set(Path(menu.path)) + } + )(menu.text) + case _ => + <.a(^.cls := "mdl-navigation__link", ^.href := menu.path)(menu.text) + }) + ) + ), + <.main(^.cls := "mdl-layout__content")(C) + ) + ).material(true) + } + } + + val component = ReactComponentB[HeaderConfig]("Application-Header") + .renderBackend[Backend] + .build + + def apply(config: HeaderConfig)(nodes: ReactNode*) = component(config, nodes: _*) +} diff --git a/examples/scalajs-play-core-react/client/src/main/scala/pages/MyScreenPage.scala b/examples/scalajs-play-core-react/client/src/main/scala/pages/MyScreenPage.scala new file mode 100644 index 0000000..45f5681 --- /dev/null +++ b/examples/scalajs-play-core-react/client/src/main/scala/pages/MyScreenPage.scala @@ -0,0 +1,34 @@ +package pages + +import autowire._ +import boopickle.Default._ +import demo.SampleApi +import japgolly.scalajs.react.vdom.prefix_<^._ +import japgolly.scalajs.react.{BackendScope, Callback, ReactComponentB} +import service.SampleClient +import scala.scalajs.concurrent.JSExecutionContext.Implicits.runNow +/** + * Main Screen component + */ +object MyScreenPage { + + case class State(message: String) + + class Backend($: BackendScope[Unit, State]) { + def init() = { + Callback { + SampleClient[SampleApi].echo("Hi there") + .call() + .map(response => $.modState(_.copy(message = response)).runNow()) + } + } + + def render() = <.div($.state.runNow().message) + } + + val component = ReactComponentB[Unit]("MyScreenPage") + .initialState(State("")) + .renderBackend[Backend] + .componentDidMount(_.backend.init()) + .buildU +} diff --git a/examples/scalajs-play-core-react/client/src/main/scala/router/ApplicationRouter.scala b/examples/scalajs-play-core-react/client/src/main/scala/router/ApplicationRouter.scala new file mode 100644 index 0000000..67ed85b --- /dev/null +++ b/examples/scalajs-play-core-react/client/src/main/scala/router/ApplicationRouter.scala @@ -0,0 +1,31 @@ +package router + +import japgolly.scalajs.react.extra.router._ +import layout.MainLayout +import pages.MyScreenPage + +/** + * Created by Janos on 12/9/2015. + */ +object ApplicationRouter { + + sealed trait Loc + + case object MainScreen extends Loc + + case object SubScreen extends Loc + + lazy val routerConfig: RouterConfig[Loc] = RouterConfigDsl[Loc].buildConfig(dsl => { + import dsl._ + ( + emptyRule + | staticRoute(root, MainScreen) ~> render(MyScreenPage.component()) + ) + .notFound(redirectToPage(MainScreen)(Redirect.Replace)) + .renderWith(MainLayout.layout) + .logToConsole + }) + + val baseUrl = BaseUrl.fromWindowOrigin_/ + val router = Router(baseUrl, routerConfig) +} diff --git a/examples/scalajs-play-core-react/client/src/main/scala/service/SampleClient.scala b/examples/scalajs-play-core-react/client/src/main/scala/service/SampleClient.scala new file mode 100644 index 0000000..e7ecfc4 --- /dev/null +++ b/examples/scalajs-play-core-react/client/src/main/scala/service/SampleClient.scala @@ -0,0 +1,6 @@ +package service + +import boopickle.Default._ +import core.service.AbstractClient + +object SampleClient extends AbstractClient("sample") diff --git a/examples/scalajs-play-core-react/project/Build.scala b/examples/scalajs-play-core-react/project/Build.scala new file mode 100644 index 0000000..9decb1a --- /dev/null +++ b/examples/scalajs-play-core-react/project/Build.scala @@ -0,0 +1,58 @@ +import org.scalajs.sbtplugin.ScalaJSPlugin +import org.scalajs.sbtplugin.ScalaJSPlugin.autoImport._ +import play.sbt.{PlayLayoutPlugin, PlayScala} +import play.twirl.sbt.SbtTwirl +import playscalajs.PlayScalaJS.autoImport._ +import playscalajs.ScalaJSPlay +import sbt.Keys._ +import sbt._ + +object ScalaJSPlayCore extends Build { + + lazy val root = project.in(file(".")) + .aggregate( + sharedJVM, + sharedJS, + client, + server + ) + .settings( + publish := {}, + publishLocal := {}, + onLoad in Global := (Command.process("project server", _: State)) compose (onLoad in Global).value + ) + + lazy val shared = (crossProject.crossType(CrossType.Pure) in file("shared")) + .settings( + scalaVersion := versions.common.scala, + libraryDependencies ++= dependencies.sharedDependencies.value + ) + .jsConfigure(_ enablePlugins ScalaJSPlay) + .jvmSettings() + .jsSettings() + + lazy val sharedJVM = shared.jvm + lazy val sharedJS = shared.js + + lazy val client = project.in(file("client")) + .settings(Settings.clientSettings ++ Seq( + name := """scalajs-play-core-react""" + )) + .enablePlugins(ScalaJSPlugin, ScalaJSPlay) + .dependsOn(sharedJS) + + lazy val clients = Seq(client) + + lazy val server = project.in(file("server")) + .settings(Settings.serverSettings ++ Seq( + name := "server", + scalaJSProjects := clients + )) + .enablePlugins(SbtTwirl, PlayScala) + .disablePlugins(PlayLayoutPlugin) + .aggregate(client) + .dependsOn(sharedJVM) + + // loads the Play server project at sbt startup + +} diff --git a/examples/scalajs-play-core-react/project/Settings.scala b/examples/scalajs-play-core-react/project/Settings.scala new file mode 100644 index 0000000..bea9248 --- /dev/null +++ b/examples/scalajs-play-core-react/project/Settings.scala @@ -0,0 +1,128 @@ +import com.typesafe.sbt.less.Import.LessKeys +import com.typesafe.sbt.web.Import._ +import org.scalajs.sbtplugin.ScalaJSPlugin.autoImport._ +import play.sbt.routes.RoutesKeys._ +import playscalajs.PlayScalaJS.autoImport._ +import sbt.Keys._ +import sbt._ + +object Settings { + val applicationName = "scalajs-play-demo" + val applicationVersion = "1.0.0" + lazy val elideOptions = settingKey[Seq[String]]("Set limit for elidable functions") + + lazy val applicationSettings = Seq( + name := applicationName, + version := applicationVersion + ) + + val sharedSettings = Seq( + scalaVersion := versions.common.scala, + scalacOptions ++= Seq( + "-Xlint", + "-unchecked", + "-deprecation", + "-feature" + ), + resolvers ++= Seq(Resolver.jcenterRepo) + ) + + lazy val clientSettings = applicationSettings ++ sharedSettings ++ Seq( + libraryDependencies ++= dependencies.clientDependencies.value, + elideOptions := Seq(), + scalacOptions ++= elideOptions.value, + jsDependencies ++= dependencies.jsDependencies.value, + skip in packageJSDependencies := false, + persistLauncher := true, + persistLauncher in Test := false, + testFrameworks += new TestFramework("utest.runner.Framework") + ) + + lazy val serverSettings = applicationSettings ++ sharedSettings ++ Seq( + libraryDependencies ++= dependencies.serverDependencies.value, + commands += ReleaseCmd, + pipelineStages := Seq(scalaJSProd), + LessKeys.compress in Assets := true, + includeFilter in(Assets, LessKeys.less) := "*.less", + excludeFilter in(Assets, LessKeys.less) := "_*.less", + routesGenerator := InjectedRoutesGenerator + ) + + // Command for building a release + lazy val ReleaseCmd = Command.command("release") { + state => "set elideOptions in client := Seq(\"-Xelide-below\", \"WARNING\")" :: + "client/clean" :: + "client/test" :: + "server/clean" :: + "server/test" :: + "server/dist" :: + "set elideOptions in client := Seq()" :: + state + } +} + +object dependencies { + val sharedDependencies = Def.setting(Seq( + "com.lihaoyi" %%% "autowire" % versions.common.autowire, + "me.chrons" %%% "boopickle" % versions.common.booPickle, + "com.lihaoyi" %%% "scalarx" % versions.common.scalaRx, + "com.lihaoyi" %%% "utest" % versions.common.uTest + )) + + val serverDependencies = Def.setting(Seq( + "com.softwaremill.macwire" %% "macros" % versions.server.macwire % "provided", + "com.softwaremill.macwire" %% "util" % versions.server.macwire, + "com.softwaremill.macwire" %% "proxy" % versions.server.macwire, + + "com.mohiva" %% "play-silhouette" % versions.server.silhouette, + "com.mohiva" %% "play-silhouette-testkit" % versions.server.silhouette % "test", + + "com.vmunier" %% "play-scalajs-scripts" % versions.server.playScripts + )) + + val clientDependencies = Def.setting(Seq( + "com.github.japgolly.scalajs-react" %%% "core" % versions.client.scalajsReact, + "com.github.japgolly.scalajs-react" %%% "extra" % versions.client.scalajsReact, + "com.github.japgolly.scalajs-react" %%% "ext-scalaz71" % versions.client.scalajsReact, + "com.github.japgolly.scalajs-react" %%% "ext-monocle" % versions.client.scalajsReact, + "com.github.japgolly.scalacss" %%% "ext-react" % versions.client.scalaCSS, + "org.scala-js" %%% "scalajs-dom" % versions.client.scalaDom + )) + + val jsDependencies = Def.setting(Seq( + "org.webjars.npm" % "react" % versions.js.react / "react-with-addons.js" minified "react-with-addons.min.js" commonJSName "React", + "org.webjars.npm" % "react-dom" % versions.js.react / "react-dom.js" commonJSName "ReactDOM" minified "react-dom.min.js" dependsOn "react-with-addons.js", + "org.webjars" % "jquery" % versions.js.jQuery / "jquery.js" minified "jquery.min.js", + RuntimeDOM % "test" + )) +} + + +object versions { + + object common { + val scala = "2.11.7" + val scalaRx = "0.2.8" + val autowire = "0.2.5" + val booPickle = "1.1.0" + val uTest = "0.3.1" + } + + object client { + val scalaDom = "0.8.2" + val scalajsReact = "0.10.1" + val scalaCSS = "0.3.1" + } + + object js { + val jQuery = "2.1.4" + val react = "0.14.2" + } + + object server { + val silhouette = "3.0.4" + val macwire = "2.1.0" + val playScripts = "0.3.0" + } + +} diff --git a/examples/scalajs-play-core-react/project/build.properties b/examples/scalajs-play-core-react/project/build.properties new file mode 100644 index 0000000..443bf66 --- /dev/null +++ b/examples/scalajs-play-core-react/project/build.properties @@ -0,0 +1,4 @@ +#Activator-generated Properties +#Fri Apr 22 09:23:32 CEST 2016 +template.uuid=03132e10-5044-46d1-ac44-2f753fd1acd0 +sbt.version=0.13.8 diff --git a/examples/scalajs-play-core-react/project/plugins.sbt b/examples/scalajs-play-core-react/project/plugins.sbt new file mode 100644 index 0000000..497f287 --- /dev/null +++ b/examples/scalajs-play-core-react/project/plugins.sbt @@ -0,0 +1,13 @@ +logLevel := Level.Warn + +resolvers += "Typesafe Releases" at "http://repo.typesafe.com/typesafe/releases/" + +addSbtPlugin("com.typesafe.sbt" % "sbt-less" % "1.0.6") + +addSbtPlugin("org.scala-js" % "sbt-scalajs" % "0.6.5") + +addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % "1.0.0") + +addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.4.3") + +addSbtPlugin("com.vmunier" % "sbt-play-scalajs" % "0.2.8") \ No newline at end of file diff --git a/examples/scalajs-play-core-react/server/src/main/assets/main.less b/examples/scalajs-play-core-react/server/src/main/assets/main.less new file mode 100644 index 0000000..e69de29 diff --git a/examples/scalajs-play-core-react/server/src/main/assets/material/material.css b/examples/scalajs-play-core-react/server/src/main/assets/material/material.css new file mode 100644 index 0000000..cdd0a01 --- /dev/null +++ b/examples/scalajs-play-core-react/server/src/main/assets/material/material.css @@ -0,0 +1,9751 @@ +/** + * material-design-lite - Material Design Components in CSS, JS and HTML + * @version v1.0.6 + * @license Apache-2.0 + * @copyright 2015 Google, Inc. + * @link https://github.com/google/material-design-lite + */ +@charset "UTF-8"; +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* Material Design Lite */ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/*------------------------------------* $CONTENTS +\*------------------------------------*/ +/** + * STYLE GUIDE VARIABLES------------------Declarations of Sass variables + * -----Typography + * -----Colors + * -----Textfield + * -----Switch + * -----Spinner + * -----Radio + * -----Menu + * -----List + * -----Layout + * -----Icon toggles + * -----Footer + * -----Column + * -----Checkbox + * -----Card + * -----Button + * -----Animation + * -----Progress + * -----Badge + * -----Shadows + * -----Grid + * -----Data table + */ +/* ========== TYPOGRAPHY ========== */ +/* We're splitting fonts into "preferred" and "performance" in order to optimize + page loading. For important text, such as the body, we want it to load + immediately and not wait for the web font load, whereas for other sections, + such as headers and titles, we're OK with things taking a bit longer to load. + We do have some optional classes and parameters in the mixins, in case you + definitely want to make sure you're using the preferred font and don't mind + the performance hit. + We should be able to improve on this once CSS Font Loading L3 becomes more + widely available. +*/ +/* ========== COLORS ========== */ +/** +* +* Material design color palettes. +* @see http://www.google.com/design/spec/style/color.html +* +**/ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* ========== Color Palettes ========== */ +/* colors.scss */ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* ========== IMAGES ========== */ +/* ========== Color & Themes ========== */ +/* ========== Typography ========== */ +/* ========== Components ========== */ +/* ========== Standard Buttons ========== */ +/* ========== Icon Toggles ========== */ +/* ========== Radio Buttons ========== */ +/* ========== Ripple effect ========== */ +/* ========== Layout ========== */ +/* ========== Content Tabs ========== */ +/* ========== Checkboxes ========== */ +/* ========== Switches ========== */ +/* ========== Spinner ========== */ +/* ========== Text fields ========== */ +/* ========== Card ========== */ +/* ========== Sliders ========== */ +/* ========== Progress ========== */ +/* ========== List ========== */ +/* ========== Item ========== */ +/* ========== Dropdown menu ========== */ +/* ========== Tooltips ========== */ +/* ========== Footer ========== */ +/* TEXTFIELD */ +/* SWITCH */ +/* SPINNER */ +/* RADIO */ +/* MENU */ +/* LIST */ +/* LAYOUT */ +/* ICON TOGGLE */ +/* FOOTER */ +/*mega-footer*/ +/*mini-footer*/ +/* CHECKBOX */ +/* CARD */ +/* Card dimensions */ +/* Cover image */ +/* BUTTON */ +/** + * + * Dimensions + * + */ +/* ANIMATION */ +/* PROGRESS */ +/* BADGE */ +/* SHADOWS */ +/* GRID */ +/* DATA TABLE */ +/* TOOLTIP */ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* Typography */ +/* Shadows */ +/* Animations */ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/*------------------------------------* $CONTENTS +\*------------------------------------*/ +/** + * STYLE GUIDE VARIABLES------------------Declarations of Sass variables + * -----Typography + * -----Colors + * -----Textfield + * -----Switch + * -----Spinner + * -----Radio + * -----Menu + * -----List + * -----Layout + * -----Icon toggles + * -----Footer + * -----Column + * -----Checkbox + * -----Card + * -----Button + * -----Animation + * -----Progress + * -----Badge + * -----Shadows + * -----Grid + * -----Data table + */ +/* ========== TYPOGRAPHY ========== */ +/* We're splitting fonts into "preferred" and "performance" in order to optimize + page loading. For important text, such as the body, we want it to load + immediately and not wait for the web font load, whereas for other sections, + such as headers and titles, we're OK with things taking a bit longer to load. + We do have some optional classes and parameters in the mixins, in case you + definitely want to make sure you're using the preferred font and don't mind + the performance hit. + We should be able to improve on this once CSS Font Loading L3 becomes more + widely available. +*/ +/* ========== COLORS ========== */ +/** +* +* Material design color palettes. +* @see http://www.google.com/design/spec/style/color.html +* +**/ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* ========== Color Palettes ========== */ +/* colors.scss */ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* ========== IMAGES ========== */ +/* ========== Color & Themes ========== */ +/* ========== Typography ========== */ +/* ========== Components ========== */ +/* ========== Standard Buttons ========== */ +/* ========== Icon Toggles ========== */ +/* ========== Radio Buttons ========== */ +/* ========== Ripple effect ========== */ +/* ========== Layout ========== */ +/* ========== Content Tabs ========== */ +/* ========== Checkboxes ========== */ +/* ========== Switches ========== */ +/* ========== Spinner ========== */ +/* ========== Text fields ========== */ +/* ========== Card ========== */ +/* ========== Sliders ========== */ +/* ========== Progress ========== */ +/* ========== List ========== */ +/* ========== Item ========== */ +/* ========== Dropdown menu ========== */ +/* ========== Tooltips ========== */ +/* ========== Footer ========== */ +/* TEXTFIELD */ +/* SWITCH */ +/* SPINNER */ +/* RADIO */ +/* MENU */ +/* LIST */ +/* LAYOUT */ +/* ICON TOGGLE */ +/* FOOTER */ +/*mega-footer*/ +/*mini-footer*/ +/* CHECKBOX */ +/* CARD */ +/* Card dimensions */ +/* Cover image */ +/* BUTTON */ +/** + * + * Dimensions + * + */ +/* ANIMATION */ +/* PROGRESS */ +/* BADGE */ +/* SHADOWS */ +/* GRID */ +/* DATA TABLE */ +/* TOOLTIP */ +/* + * What follows is the result of much research on cross-browser styling. + * Credit left inline and big thanks to Nicolas Gallagher, Jonathan Neal, + * Kroc Camen, and the H5BP dev community and team. + */ +/* ========================================================================== + Base styles: opinionated defaults + ========================================================================== */ +html { + color: rgba(0,0,0, 0.87); + font-size: 1em; + line-height: 1.4; } + +/* + * Remove text-shadow in selection highlight: + * https://twitter.com/miketaylr/status/12228805301 + * + * These selection rule sets have to be separate. + * Customize the background color to match your design. + */ +::-moz-selection { + background: #b3d4fc; + text-shadow: none; } +::selection { + background: #b3d4fc; + text-shadow: none; } + +/* + * A better looking default horizontal rule + */ +hr { + display: block; + height: 1px; + border: 0; + border-top: 1px solid #ccc; + margin: 1em 0; + padding: 0; } + +/* + * Remove the gap between audio, canvas, iframes, + * images, videos and the bottom of their containers: + * https://github.com/h5bp/html5-boilerplate/issues/440 + */ +audio, +canvas, +iframe, +img, +svg, +video { + vertical-align: middle; } + +/* + * Remove default fieldset styles. + */ +fieldset { + border: 0; + margin: 0; + padding: 0; } + +/* + * Allow only vertical resizing of textareas. + */ +textarea { + resize: vertical; } + +/* ========================================================================== + Browser Upgrade Prompt + ========================================================================== */ +.browserupgrade { + margin: 0.2em 0; + background: #ccc; + color: #000; + padding: 0.2em 0; } + +/* ========================================================================== + Author's custom styles + ========================================================================== */ +/* ========================================================================== + Helper classes + ========================================================================== */ +/* + * Hide visually and from screen readers: + */ +.hidden { + display: none !important; } + +/* + * Hide only visually, but have it available for screen readers: + * http://snook.ca/archives/html_and_css/hiding-content-for-accessibility + */ +.visuallyhidden { + border: 0; + clip: rect(0 0 0 0); + height: 1px; + margin: -1px; + overflow: hidden; + padding: 0; + position: absolute; + width: 1px; } + +/* + * Extends the .visuallyhidden class to allow the element + * to be focusable when navigated to via the keyboard: + * https://www.drupal.org/node/897638 + */ +.visuallyhidden.focusable:active, +.visuallyhidden.focusable:focus { + clip: auto; + height: auto; + margin: 0; + overflow: visible; + position: static; + width: auto; } + +/* + * Hide visually and from screen readers, but maintain layout + */ +.invisible { + visibility: hidden; } + +/* + * Clearfix: contain floats + * + * For modern browsers + * 1. The space content is one way to avoid an Opera bug when the + * `contenteditable` attribute is included anywhere else in the document. + * Otherwise it causes space to appear at the top and bottom of elements + * that receive the `clearfix` class. + * 2. The use of `table` rather than `block` is only necessary if using + * `:before` to contain the top-margins of child elements. + */ +.clearfix:before, +.clearfix:after { + content: " "; + /* 1 */ + display: table; + /* 2 */ } + +.clearfix:after { + clear: both; } + +/* ========================================================================== + EXAMPLE Media Queries for Responsive Design. + These examples override the primary ('mobile first') styles. + Modify as content requires. + ========================================================================== */ +/* ========================================================================== + Print styles. + Inlined to avoid the additional HTTP request: + http://www.phpied.com/delay-loading-your-print-css/ + ========================================================================== */ +@media print { + *, + *:before, + *:after, + *:first-letter, + *:first-line { + background: transparent !important; + color: #000 !important; + /* Black prints faster: http://www.sanbeiji.com/archives/953 */ + box-shadow: none !important; + text-shadow: none !important; } + a, + a:visited { + text-decoration: underline; } + a[href]:after { + content: " (" attr(href) ")"; } + abbr[title]:after { + content: " (" attr(title) ")"; } + /* + * Don't show links that are fragment identifiers, + * or use the `javascript:` pseudo protocol + */ + a[href^="#"]:after, + a[href^="javascript:"]:after { + content: ""; } + pre, + blockquote { + border: 1px solid #999; + page-break-inside: avoid; } + /* + * Printing Tables: + * http://css-discuss.incutio.com/wiki/Printing_Tables + */ + thead { + display: table-header-group; } + tr, + img { + page-break-inside: avoid; } + img { + max-width: 100% !important; } + p, + h2, + h3 { + orphans: 3; + widows: 3; } + h2, + h3 { + page-break-after: avoid; } } + +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* Remove the unwanted box around FAB buttons */ +/* More info: http://goo.gl/IPwKi */ +a, .mdl-accordion, .mdl-button, .mdl-card, .mdl-checkbox, .mdl-dropdown-menu, +.mdl-icon-toggle, .mdl-item, .mdl-radio, .mdl-slider, .mdl-switch, .mdl-tabs__tab { + -webkit-tap-highlight-color: transparent; + -webkit-tap-highlight-color: rgba(255, 255, 255, 0); } + +/* + * Make html take up the entire screen + * Then set touch-action to avoid touch delay on mobile IE + */ +html { + width: 100%; + height: 100%; + -ms-touch-action: manipulation; + touch-action: manipulation; } + +/* +* Make body take up the entire screen +* Remove body margin so layout containers don't cause extra overflow. +*/ +body { + width: 100%; + min-height: 100%; + margin: 0; } + +/* + * Main display reset for IE support. + * Source: http://weblog.west-wind.com/posts/2015/Jan/12/main-HTML5-Tag-not-working-in-Internet-Explorer-91011 + */ +main { + display: block; } + +/* +* Apply no display to elements with the hidden attribute. +* IE 9 and 10 support. +*/ +*[hidden] { + display: none !important; } + +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/*------------------------------------* $CONTENTS +\*------------------------------------*/ +/** + * STYLE GUIDE VARIABLES------------------Declarations of Sass variables + * -----Typography + * -----Colors + * -----Textfield + * -----Switch + * -----Spinner + * -----Radio + * -----Menu + * -----List + * -----Layout + * -----Icon toggles + * -----Footer + * -----Column + * -----Checkbox + * -----Card + * -----Button + * -----Animation + * -----Progress + * -----Badge + * -----Shadows + * -----Grid + * -----Data table + */ +/* ========== TYPOGRAPHY ========== */ +/* We're splitting fonts into "preferred" and "performance" in order to optimize + page loading. For important text, such as the body, we want it to load + immediately and not wait for the web font load, whereas for other sections, + such as headers and titles, we're OK with things taking a bit longer to load. + We do have some optional classes and parameters in the mixins, in case you + definitely want to make sure you're using the preferred font and don't mind + the performance hit. + We should be able to improve on this once CSS Font Loading L3 becomes more + widely available. +*/ +/* ========== COLORS ========== */ +/** +* +* Material design color palettes. +* @see http://www.google.com/design/spec/style/color.html +* +**/ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* ========== Color Palettes ========== */ +/* colors.scss */ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* ========== IMAGES ========== */ +/* ========== Color & Themes ========== */ +/* ========== Typography ========== */ +/* ========== Components ========== */ +/* ========== Standard Buttons ========== */ +/* ========== Icon Toggles ========== */ +/* ========== Radio Buttons ========== */ +/* ========== Ripple effect ========== */ +/* ========== Layout ========== */ +/* ========== Content Tabs ========== */ +/* ========== Checkboxes ========== */ +/* ========== Switches ========== */ +/* ========== Spinner ========== */ +/* ========== Text fields ========== */ +/* ========== Card ========== */ +/* ========== Sliders ========== */ +/* ========== Progress ========== */ +/* ========== List ========== */ +/* ========== Item ========== */ +/* ========== Dropdown menu ========== */ +/* ========== Tooltips ========== */ +/* ========== Footer ========== */ +/* TEXTFIELD */ +/* SWITCH */ +/* SPINNER */ +/* RADIO */ +/* MENU */ +/* LIST */ +/* LAYOUT */ +/* ICON TOGGLE */ +/* FOOTER */ +/*mega-footer*/ +/*mini-footer*/ +/* CHECKBOX */ +/* CARD */ +/* Card dimensions */ +/* Cover image */ +/* BUTTON */ +/** + * + * Dimensions + * + */ +/* ANIMATION */ +/* PROGRESS */ +/* BADGE */ +/* SHADOWS */ +/* GRID */ +/* DATA TABLE */ +/* TOOLTIP */ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* Typography */ +/* Shadows */ +/* Animations */ +html, body { + font-family: "Helvetica", "Arial", sans-serif; + font-size: 14px; + font-weight: 400; + line-height: 20px; } + +h1, h2, h3, h4, h5, h6, p { + margin: 0; + padding: 0; } + +/** + * Styles for HTML elements + */ +h1 small, h2 small, h3 small, h4 small, h5 small, h6 small { + font-family: "Roboto", "Helvetica", "Arial", sans-serif; + font-size: 56px; + font-weight: 400; + line-height: 1.35; + letter-spacing: -0.02em; + opacity: 0.54; + font-size: 0.6em; } + +h1 { + font-family: "Roboto", "Helvetica", "Arial", sans-serif; + font-size: 56px; + font-weight: 400; + line-height: 1.35; + letter-spacing: -0.02em; + margin-top: 24px; + margin-bottom: 24px; } + +h2 { + font-family: "Roboto", "Helvetica", "Arial", sans-serif; + font-size: 45px; + font-weight: 400; + line-height: 48px; + margin-top: 24px; + margin-bottom: 24px; } + +h3 { + font-family: "Roboto", "Helvetica", "Arial", sans-serif; + font-size: 34px; + font-weight: 400; + line-height: 40px; + margin-top: 24px; + margin-bottom: 24px; } + +h4 { + font-family: "Roboto", "Helvetica", "Arial", sans-serif; + font-size: 24px; + font-weight: 400; + line-height: 32px; + -moz-osx-font-smoothing: grayscale; + margin-top: 24px; + margin-bottom: 16px; } + +h5 { + font-family: "Roboto", "Helvetica", "Arial", sans-serif; + font-size: 20px; + font-weight: 500; + line-height: 1; + letter-spacing: 0.02em; + margin-top: 24px; + margin-bottom: 16px; } + +h6 { + font-family: "Roboto", "Helvetica", "Arial", sans-serif; + font-size: 16px; + font-weight: 400; + line-height: 24px; + letter-spacing: 0.04em; + margin-top: 24px; + margin-bottom: 16px; } + +p { + font-size: 14px; + font-weight: 400; + line-height: 24px; + letter-spacing: 0; + margin-bottom: 16px; } + +a { + color: rgb(255,64,129); + font-weight: 500; } + +blockquote { + font-family: "Roboto", "Helvetica", "Arial", sans-serif; + position: relative; + font-size: 24px; + font-weight: 300; + font-style: italic; + line-height: 1.35; + letter-spacing: 0.08em; } + blockquote:before { + position: absolute; + left: -0.5em; + content: '“'; } + blockquote:after { + content: '”'; + margin-left: -0.05em; } + +mark { + background-color: #f4ff81; } + +dt { + font-weight: 700; } + +address { + font-size: 12px; + font-weight: 400; + line-height: 1; + letter-spacing: 0; + font-style: normal; } + +ul, ol { + font-size: 14px; + font-weight: 400; + line-height: 24px; + letter-spacing: 0; } + +/** + * Class Name Styles + */ +.mdl-typography--display-4 { + font-family: "Roboto", "Helvetica", "Arial", sans-serif; + font-size: 112px; + font-weight: 300; + line-height: 1; + letter-spacing: -0.04em; } + +.mdl-typography--display-4-color-contrast { + font-family: "Roboto", "Helvetica", "Arial", sans-serif; + font-size: 112px; + font-weight: 300; + line-height: 1; + letter-spacing: -0.04em; + opacity: 0.54; } + +.mdl-typography--display-3 { + font-family: "Roboto", "Helvetica", "Arial", sans-serif; + font-size: 56px; + font-weight: 400; + line-height: 1.35; + letter-spacing: -0.02em; } + +.mdl-typography--display-3-color-contrast { + font-family: "Roboto", "Helvetica", "Arial", sans-serif; + font-size: 56px; + font-weight: 400; + line-height: 1.35; + letter-spacing: -0.02em; + opacity: 0.54; } + +.mdl-typography--display-2 { + font-family: "Roboto", "Helvetica", "Arial", sans-serif; + font-size: 45px; + font-weight: 400; + line-height: 48px; } + +.mdl-typography--display-2-color-contrast { + font-family: "Roboto", "Helvetica", "Arial", sans-serif; + font-size: 45px; + font-weight: 400; + line-height: 48px; + opacity: 0.54; } + +.mdl-typography--display-1 { + font-family: "Roboto", "Helvetica", "Arial", sans-serif; + font-size: 34px; + font-weight: 400; + line-height: 40px; } + +.mdl-typography--display-1-color-contrast { + font-family: "Roboto", "Helvetica", "Arial", sans-serif; + font-size: 34px; + font-weight: 400; + line-height: 40px; + opacity: 0.54; } + +.mdl-typography--headline { + font-family: "Roboto", "Helvetica", "Arial", sans-serif; + font-size: 24px; + font-weight: 400; + line-height: 32px; + -moz-osx-font-smoothing: grayscale; } + +.mdl-typography--headline-color-contrast { + font-family: "Roboto", "Helvetica", "Arial", sans-serif; + font-size: 24px; + font-weight: 400; + line-height: 32px; + -moz-osx-font-smoothing: grayscale; + opacity: 0.87; } + +.mdl-typography--title { + font-family: "Roboto", "Helvetica", "Arial", sans-serif; + font-size: 20px; + font-weight: 500; + line-height: 1; + letter-spacing: 0.02em; } + +.mdl-typography--title-color-contrast { + font-family: "Roboto", "Helvetica", "Arial", sans-serif; + font-size: 20px; + font-weight: 500; + line-height: 1; + letter-spacing: 0.02em; + opacity: 0.87; } + +.mdl-typography--subhead { + font-family: "Roboto", "Helvetica", "Arial", sans-serif; + font-size: 16px; + font-weight: 400; + line-height: 24px; + letter-spacing: 0.04em; } + +.mdl-typography--subhead-color-contrast { + font-family: "Roboto", "Helvetica", "Arial", sans-serif; + font-size: 16px; + font-weight: 400; + line-height: 24px; + letter-spacing: 0.04em; + opacity: 0.87; } + +.mdl-typography--body-2 { + font-size: 14px; + font-weight: bold; + line-height: 24px; + letter-spacing: 0; } + +.mdl-typography--body-2-color-contrast { + font-size: 14px; + font-weight: bold; + line-height: 24px; + letter-spacing: 0; + opacity: 0.87; } + +.mdl-typography--body-1 { + font-size: 14px; + font-weight: 400; + line-height: 24px; + letter-spacing: 0; } + +.mdl-typography--body-1-color-contrast { + font-size: 14px; + font-weight: 400; + line-height: 24px; + letter-spacing: 0; + opacity: 0.87; } + +.mdl-typography--body-2-force-preferred-font { + font-family: "Roboto", "Helvetica", "Arial", sans-serif; + font-size: 14px; + font-weight: 500; + line-height: 24px; + letter-spacing: 0; } + +.mdl-typography--body-2-force-preferred-font-color-contrast { + font-family: "Roboto", "Helvetica", "Arial", sans-serif; + font-size: 14px; + font-weight: 500; + line-height: 24px; + letter-spacing: 0; + opacity: 0.87; } + +.mdl-typography--body-1-force-preferred-font { + font-family: "Roboto", "Helvetica", "Arial", sans-serif; + font-size: 14px; + font-weight: 400; + line-height: 24px; + letter-spacing: 0; } + +.mdl-typography--body-1-force-preferred-font-color-contrast { + font-family: "Roboto", "Helvetica", "Arial", sans-serif; + font-size: 14px; + font-weight: 400; + line-height: 24px; + letter-spacing: 0; + opacity: 0.87; } + +.mdl-typography--caption { + font-size: 12px; + font-weight: 400; + line-height: 1; + letter-spacing: 0; } + +.mdl-typography--caption-force-preferred-font { + font-family: "Roboto", "Helvetica", "Arial", sans-serif; + font-size: 12px; + font-weight: 400; + line-height: 1; + letter-spacing: 0; } + +.mdl-typography--caption-color-contrast { + font-size: 12px; + font-weight: 400; + line-height: 1; + letter-spacing: 0; + opacity: 0.54; } + +.mdl-typography--caption-force-preferred-font-color-contrast { + font-family: "Roboto", "Helvetica", "Arial", sans-serif; + font-size: 12px; + font-weight: 400; + line-height: 1; + letter-spacing: 0; + opacity: 0.54; } + +.mdl-typography--menu { + font-family: "Roboto", "Helvetica", "Arial", sans-serif; + font-size: 14px; + font-weight: 500; + line-height: 1; + letter-spacing: 0; } + +.mdl-typography--menu-color-contrast { + font-family: "Roboto", "Helvetica", "Arial", sans-serif; + font-size: 14px; + font-weight: 500; + line-height: 1; + letter-spacing: 0; + opacity: 0.87; } + +.mdl-typography--button { + font-family: "Roboto", "Helvetica", "Arial", sans-serif; + font-size: 14px; + font-weight: 500; + text-transform: uppercase; + line-height: 1; + letter-spacing: 0; } + +.mdl-typography--button-color-contrast { + font-family: "Roboto", "Helvetica", "Arial", sans-serif; + font-size: 14px; + font-weight: 500; + text-transform: uppercase; + line-height: 1; + letter-spacing: 0; + opacity: 0.87; } + +.mdl-typography--text-left { + text-align: left; } + +.mdl-typography--text-right { + text-align: right; } + +.mdl-typography--text-center { + text-align: center; } + +.mdl-typography--text-justify { + text-align: justify; } + +.mdl-typography--text-nowrap { + white-space: nowrap; } + +.mdl-typography--text-lowercase { + text-transform: lowercase; } + +.mdl-typography--text-uppercase { + text-transform: uppercase; } + +.mdl-typography--text-capitalize { + text-transform: capitalize; } + +.mdl-typography--font-thin { + font-weight: 200 !important; } + +.mdl-typography--font-light { + font-weight: 300 !important; } + +.mdl-typography--font-regular { + font-weight: 400 !important; } + +.mdl-typography--font-medium { + font-weight: 500 !important; } + +.mdl-typography--font-bold { + font-weight: 700 !important; } + +.mdl-typography--font-black { + font-weight: 900 !important; } + +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/*------------------------------------* $CONTENTS +\*------------------------------------*/ +/** + * STYLE GUIDE VARIABLES------------------Declarations of Sass variables + * -----Typography + * -----Colors + * -----Textfield + * -----Switch + * -----Spinner + * -----Radio + * -----Menu + * -----List + * -----Layout + * -----Icon toggles + * -----Footer + * -----Column + * -----Checkbox + * -----Card + * -----Button + * -----Animation + * -----Progress + * -----Badge + * -----Shadows + * -----Grid + * -----Data table + */ +/* ========== TYPOGRAPHY ========== */ +/* We're splitting fonts into "preferred" and "performance" in order to optimize + page loading. For important text, such as the body, we want it to load + immediately and not wait for the web font load, whereas for other sections, + such as headers and titles, we're OK with things taking a bit longer to load. + We do have some optional classes and parameters in the mixins, in case you + definitely want to make sure you're using the preferred font and don't mind + the performance hit. + We should be able to improve on this once CSS Font Loading L3 becomes more + widely available. +*/ +/* ========== COLORS ========== */ +/** +* +* Material design color palettes. +* @see http://www.google.com/design/spec/style/color.html +* +**/ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* ========== Color Palettes ========== */ +/* colors.scss */ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* ========== IMAGES ========== */ +/* ========== Color & Themes ========== */ +/* ========== Typography ========== */ +/* ========== Components ========== */ +/* ========== Standard Buttons ========== */ +/* ========== Icon Toggles ========== */ +/* ========== Radio Buttons ========== */ +/* ========== Ripple effect ========== */ +/* ========== Layout ========== */ +/* ========== Content Tabs ========== */ +/* ========== Checkboxes ========== */ +/* ========== Switches ========== */ +/* ========== Spinner ========== */ +/* ========== Text fields ========== */ +/* ========== Card ========== */ +/* ========== Sliders ========== */ +/* ========== Progress ========== */ +/* ========== List ========== */ +/* ========== Item ========== */ +/* ========== Dropdown menu ========== */ +/* ========== Tooltips ========== */ +/* ========== Footer ========== */ +/* TEXTFIELD */ +/* SWITCH */ +/* SPINNER */ +/* RADIO */ +/* MENU */ +/* LIST */ +/* LAYOUT */ +/* ICON TOGGLE */ +/* FOOTER */ +/*mega-footer*/ +/*mini-footer*/ +/* CHECKBOX */ +/* CARD */ +/* Card dimensions */ +/* Cover image */ +/* BUTTON */ +/** + * + * Dimensions + * + */ +/* ANIMATION */ +/* PROGRESS */ +/* BADGE */ +/* SHADOWS */ +/* GRID */ +/* DATA TABLE */ +/* TOOLTIP */ +.mdl-color-text--red { + color: rgb(244,67,54) !important; } + +.mdl-color--red { + background-color: rgb(244,67,54) !important; } + +.mdl-color-text--red-50 { + color: rgb(255,235,238) !important; } + +.mdl-color--red-50 { + background-color: rgb(255,235,238) !important; } + +.mdl-color-text--red-100 { + color: rgb(255,205,210) !important; } + +.mdl-color--red-100 { + background-color: rgb(255,205,210) !important; } + +.mdl-color-text--red-200 { + color: rgb(239,154,154) !important; } + +.mdl-color--red-200 { + background-color: rgb(239,154,154) !important; } + +.mdl-color-text--red-300 { + color: rgb(229,115,115) !important; } + +.mdl-color--red-300 { + background-color: rgb(229,115,115) !important; } + +.mdl-color-text--red-400 { + color: rgb(239,83,80) !important; } + +.mdl-color--red-400 { + background-color: rgb(239,83,80) !important; } + +.mdl-color-text--red-500 { + color: rgb(244,67,54) !important; } + +.mdl-color--red-500 { + background-color: rgb(244,67,54) !important; } + +.mdl-color-text--red-600 { + color: rgb(229,57,53) !important; } + +.mdl-color--red-600 { + background-color: rgb(229,57,53) !important; } + +.mdl-color-text--red-700 { + color: rgb(211,47,47) !important; } + +.mdl-color--red-700 { + background-color: rgb(211,47,47) !important; } + +.mdl-color-text--red-800 { + color: rgb(198,40,40) !important; } + +.mdl-color--red-800 { + background-color: rgb(198,40,40) !important; } + +.mdl-color-text--red-900 { + color: rgb(183,28,28) !important; } + +.mdl-color--red-900 { + background-color: rgb(183,28,28) !important; } + +.mdl-color-text--red-A100 { + color: rgb(255,138,128) !important; } + +.mdl-color--red-A100 { + background-color: rgb(255,138,128) !important; } + +.mdl-color-text--red-A200 { + color: rgb(255,82,82) !important; } + +.mdl-color--red-A200 { + background-color: rgb(255,82,82) !important; } + +.mdl-color-text--red-A400 { + color: rgb(255,23,68) !important; } + +.mdl-color--red-A400 { + background-color: rgb(255,23,68) !important; } + +.mdl-color-text--red-A700 { + color: rgb(213,0,0) !important; } + +.mdl-color--red-A700 { + background-color: rgb(213,0,0) !important; } + +.mdl-color-text--pink { + color: rgb(233,30,99) !important; } + +.mdl-color--pink { + background-color: rgb(233,30,99) !important; } + +.mdl-color-text--pink-50 { + color: rgb(252,228,236) !important; } + +.mdl-color--pink-50 { + background-color: rgb(252,228,236) !important; } + +.mdl-color-text--pink-100 { + color: rgb(248,187,208) !important; } + +.mdl-color--pink-100 { + background-color: rgb(248,187,208) !important; } + +.mdl-color-text--pink-200 { + color: rgb(244,143,177) !important; } + +.mdl-color--pink-200 { + background-color: rgb(244,143,177) !important; } + +.mdl-color-text--pink-300 { + color: rgb(240,98,146) !important; } + +.mdl-color--pink-300 { + background-color: rgb(240,98,146) !important; } + +.mdl-color-text--pink-400 { + color: rgb(236,64,122) !important; } + +.mdl-color--pink-400 { + background-color: rgb(236,64,122) !important; } + +.mdl-color-text--pink-500 { + color: rgb(233,30,99) !important; } + +.mdl-color--pink-500 { + background-color: rgb(233,30,99) !important; } + +.mdl-color-text--pink-600 { + color: rgb(216,27,96) !important; } + +.mdl-color--pink-600 { + background-color: rgb(216,27,96) !important; } + +.mdl-color-text--pink-700 { + color: rgb(194,24,91) !important; } + +.mdl-color--pink-700 { + background-color: rgb(194,24,91) !important; } + +.mdl-color-text--pink-800 { + color: rgb(173,20,87) !important; } + +.mdl-color--pink-800 { + background-color: rgb(173,20,87) !important; } + +.mdl-color-text--pink-900 { + color: rgb(136,14,79) !important; } + +.mdl-color--pink-900 { + background-color: rgb(136,14,79) !important; } + +.mdl-color-text--pink-A100 { + color: rgb(255,128,171) !important; } + +.mdl-color--pink-A100 { + background-color: rgb(255,128,171) !important; } + +.mdl-color-text--pink-A200 { + color: rgb(255,64,129) !important; } + +.mdl-color--pink-A200 { + background-color: rgb(255,64,129) !important; } + +.mdl-color-text--pink-A400 { + color: rgb(245,0,87) !important; } + +.mdl-color--pink-A400 { + background-color: rgb(245,0,87) !important; } + +.mdl-color-text--pink-A700 { + color: rgb(197,17,98) !important; } + +.mdl-color--pink-A700 { + background-color: rgb(197,17,98) !important; } + +.mdl-color-text--purple { + color: rgb(156,39,176) !important; } + +.mdl-color--purple { + background-color: rgb(156,39,176) !important; } + +.mdl-color-text--purple-50 { + color: rgb(243,229,245) !important; } + +.mdl-color--purple-50 { + background-color: rgb(243,229,245) !important; } + +.mdl-color-text--purple-100 { + color: rgb(225,190,231) !important; } + +.mdl-color--purple-100 { + background-color: rgb(225,190,231) !important; } + +.mdl-color-text--purple-200 { + color: rgb(206,147,216) !important; } + +.mdl-color--purple-200 { + background-color: rgb(206,147,216) !important; } + +.mdl-color-text--purple-300 { + color: rgb(186,104,200) !important; } + +.mdl-color--purple-300 { + background-color: rgb(186,104,200) !important; } + +.mdl-color-text--purple-400 { + color: rgb(171,71,188) !important; } + +.mdl-color--purple-400 { + background-color: rgb(171,71,188) !important; } + +.mdl-color-text--purple-500 { + color: rgb(156,39,176) !important; } + +.mdl-color--purple-500 { + background-color: rgb(156,39,176) !important; } + +.mdl-color-text--purple-600 { + color: rgb(142,36,170) !important; } + +.mdl-color--purple-600 { + background-color: rgb(142,36,170) !important; } + +.mdl-color-text--purple-700 { + color: rgb(123,31,162) !important; } + +.mdl-color--purple-700 { + background-color: rgb(123,31,162) !important; } + +.mdl-color-text--purple-800 { + color: rgb(106,27,154) !important; } + +.mdl-color--purple-800 { + background-color: rgb(106,27,154) !important; } + +.mdl-color-text--purple-900 { + color: rgb(74,20,140) !important; } + +.mdl-color--purple-900 { + background-color: rgb(74,20,140) !important; } + +.mdl-color-text--purple-A100 { + color: rgb(234,128,252) !important; } + +.mdl-color--purple-A100 { + background-color: rgb(234,128,252) !important; } + +.mdl-color-text--purple-A200 { + color: rgb(224,64,251) !important; } + +.mdl-color--purple-A200 { + background-color: rgb(224,64,251) !important; } + +.mdl-color-text--purple-A400 { + color: rgb(213,0,249) !important; } + +.mdl-color--purple-A400 { + background-color: rgb(213,0,249) !important; } + +.mdl-color-text--purple-A700 { + color: rgb(170,0,255) !important; } + +.mdl-color--purple-A700 { + background-color: rgb(170,0,255) !important; } + +.mdl-color-text--deep-purple { + color: rgb(103,58,183) !important; } + +.mdl-color--deep-purple { + background-color: rgb(103,58,183) !important; } + +.mdl-color-text--deep-purple-50 { + color: rgb(237,231,246) !important; } + +.mdl-color--deep-purple-50 { + background-color: rgb(237,231,246) !important; } + +.mdl-color-text--deep-purple-100 { + color: rgb(209,196,233) !important; } + +.mdl-color--deep-purple-100 { + background-color: rgb(209,196,233) !important; } + +.mdl-color-text--deep-purple-200 { + color: rgb(179,157,219) !important; } + +.mdl-color--deep-purple-200 { + background-color: rgb(179,157,219) !important; } + +.mdl-color-text--deep-purple-300 { + color: rgb(149,117,205) !important; } + +.mdl-color--deep-purple-300 { + background-color: rgb(149,117,205) !important; } + +.mdl-color-text--deep-purple-400 { + color: rgb(126,87,194) !important; } + +.mdl-color--deep-purple-400 { + background-color: rgb(126,87,194) !important; } + +.mdl-color-text--deep-purple-500 { + color: rgb(103,58,183) !important; } + +.mdl-color--deep-purple-500 { + background-color: rgb(103,58,183) !important; } + +.mdl-color-text--deep-purple-600 { + color: rgb(94,53,177) !important; } + +.mdl-color--deep-purple-600 { + background-color: rgb(94,53,177) !important; } + +.mdl-color-text--deep-purple-700 { + color: rgb(81,45,168) !important; } + +.mdl-color--deep-purple-700 { + background-color: rgb(81,45,168) !important; } + +.mdl-color-text--deep-purple-800 { + color: rgb(69,39,160) !important; } + +.mdl-color--deep-purple-800 { + background-color: rgb(69,39,160) !important; } + +.mdl-color-text--deep-purple-900 { + color: rgb(49,27,146) !important; } + +.mdl-color--deep-purple-900 { + background-color: rgb(49,27,146) !important; } + +.mdl-color-text--deep-purple-A100 { + color: rgb(179,136,255) !important; } + +.mdl-color--deep-purple-A100 { + background-color: rgb(179,136,255) !important; } + +.mdl-color-text--deep-purple-A200 { + color: rgb(124,77,255) !important; } + +.mdl-color--deep-purple-A200 { + background-color: rgb(124,77,255) !important; } + +.mdl-color-text--deep-purple-A400 { + color: rgb(101,31,255) !important; } + +.mdl-color--deep-purple-A400 { + background-color: rgb(101,31,255) !important; } + +.mdl-color-text--deep-purple-A700 { + color: rgb(98,0,234) !important; } + +.mdl-color--deep-purple-A700 { + background-color: rgb(98,0,234) !important; } + +.mdl-color-text--indigo { + color: rgb(63,81,181) !important; } + +.mdl-color--indigo { + background-color: rgb(63,81,181) !important; } + +.mdl-color-text--indigo-50 { + color: rgb(232,234,246) !important; } + +.mdl-color--indigo-50 { + background-color: rgb(232,234,246) !important; } + +.mdl-color-text--indigo-100 { + color: rgb(197,202,233) !important; } + +.mdl-color--indigo-100 { + background-color: rgb(197,202,233) !important; } + +.mdl-color-text--indigo-200 { + color: rgb(159,168,218) !important; } + +.mdl-color--indigo-200 { + background-color: rgb(159,168,218) !important; } + +.mdl-color-text--indigo-300 { + color: rgb(121,134,203) !important; } + +.mdl-color--indigo-300 { + background-color: rgb(121,134,203) !important; } + +.mdl-color-text--indigo-400 { + color: rgb(92,107,192) !important; } + +.mdl-color--indigo-400 { + background-color: rgb(92,107,192) !important; } + +.mdl-color-text--indigo-500 { + color: rgb(63,81,181) !important; } + +.mdl-color--indigo-500 { + background-color: rgb(63,81,181) !important; } + +.mdl-color-text--indigo-600 { + color: rgb(57,73,171) !important; } + +.mdl-color--indigo-600 { + background-color: rgb(57,73,171) !important; } + +.mdl-color-text--indigo-700 { + color: rgb(48,63,159) !important; } + +.mdl-color--indigo-700 { + background-color: rgb(48,63,159) !important; } + +.mdl-color-text--indigo-800 { + color: rgb(40,53,147) !important; } + +.mdl-color--indigo-800 { + background-color: rgb(40,53,147) !important; } + +.mdl-color-text--indigo-900 { + color: rgb(26,35,126) !important; } + +.mdl-color--indigo-900 { + background-color: rgb(26,35,126) !important; } + +.mdl-color-text--indigo-A100 { + color: rgb(140,158,255) !important; } + +.mdl-color--indigo-A100 { + background-color: rgb(140,158,255) !important; } + +.mdl-color-text--indigo-A200 { + color: rgb(83,109,254) !important; } + +.mdl-color--indigo-A200 { + background-color: rgb(83,109,254) !important; } + +.mdl-color-text--indigo-A400 { + color: rgb(61,90,254) !important; } + +.mdl-color--indigo-A400 { + background-color: rgb(61,90,254) !important; } + +.mdl-color-text--indigo-A700 { + color: rgb(48,79,254) !important; } + +.mdl-color--indigo-A700 { + background-color: rgb(48,79,254) !important; } + +.mdl-color-text--blue { + color: rgb(33,150,243) !important; } + +.mdl-color--blue { + background-color: rgb(33,150,243) !important; } + +.mdl-color-text--blue-50 { + color: rgb(227,242,253) !important; } + +.mdl-color--blue-50 { + background-color: rgb(227,242,253) !important; } + +.mdl-color-text--blue-100 { + color: rgb(187,222,251) !important; } + +.mdl-color--blue-100 { + background-color: rgb(187,222,251) !important; } + +.mdl-color-text--blue-200 { + color: rgb(144,202,249) !important; } + +.mdl-color--blue-200 { + background-color: rgb(144,202,249) !important; } + +.mdl-color-text--blue-300 { + color: rgb(100,181,246) !important; } + +.mdl-color--blue-300 { + background-color: rgb(100,181,246) !important; } + +.mdl-color-text--blue-400 { + color: rgb(66,165,245) !important; } + +.mdl-color--blue-400 { + background-color: rgb(66,165,245) !important; } + +.mdl-color-text--blue-500 { + color: rgb(33,150,243) !important; } + +.mdl-color--blue-500 { + background-color: rgb(33,150,243) !important; } + +.mdl-color-text--blue-600 { + color: rgb(30,136,229) !important; } + +.mdl-color--blue-600 { + background-color: rgb(30,136,229) !important; } + +.mdl-color-text--blue-700 { + color: rgb(25,118,210) !important; } + +.mdl-color--blue-700 { + background-color: rgb(25,118,210) !important; } + +.mdl-color-text--blue-800 { + color: rgb(21,101,192) !important; } + +.mdl-color--blue-800 { + background-color: rgb(21,101,192) !important; } + +.mdl-color-text--blue-900 { + color: rgb(13,71,161) !important; } + +.mdl-color--blue-900 { + background-color: rgb(13,71,161) !important; } + +.mdl-color-text--blue-A100 { + color: rgb(130,177,255) !important; } + +.mdl-color--blue-A100 { + background-color: rgb(130,177,255) !important; } + +.mdl-color-text--blue-A200 { + color: rgb(68,138,255) !important; } + +.mdl-color--blue-A200 { + background-color: rgb(68,138,255) !important; } + +.mdl-color-text--blue-A400 { + color: rgb(41,121,255) !important; } + +.mdl-color--blue-A400 { + background-color: rgb(41,121,255) !important; } + +.mdl-color-text--blue-A700 { + color: rgb(41,98,255) !important; } + +.mdl-color--blue-A700 { + background-color: rgb(41,98,255) !important; } + +.mdl-color-text--light-blue { + color: rgb(3,169,244) !important; } + +.mdl-color--light-blue { + background-color: rgb(3,169,244) !important; } + +.mdl-color-text--light-blue-50 { + color: rgb(225,245,254) !important; } + +.mdl-color--light-blue-50 { + background-color: rgb(225,245,254) !important; } + +.mdl-color-text--light-blue-100 { + color: rgb(179,229,252) !important; } + +.mdl-color--light-blue-100 { + background-color: rgb(179,229,252) !important; } + +.mdl-color-text--light-blue-200 { + color: rgb(129,212,250) !important; } + +.mdl-color--light-blue-200 { + background-color: rgb(129,212,250) !important; } + +.mdl-color-text--light-blue-300 { + color: rgb(79,195,247) !important; } + +.mdl-color--light-blue-300 { + background-color: rgb(79,195,247) !important; } + +.mdl-color-text--light-blue-400 { + color: rgb(41,182,246) !important; } + +.mdl-color--light-blue-400 { + background-color: rgb(41,182,246) !important; } + +.mdl-color-text--light-blue-500 { + color: rgb(3,169,244) !important; } + +.mdl-color--light-blue-500 { + background-color: rgb(3,169,244) !important; } + +.mdl-color-text--light-blue-600 { + color: rgb(3,155,229) !important; } + +.mdl-color--light-blue-600 { + background-color: rgb(3,155,229) !important; } + +.mdl-color-text--light-blue-700 { + color: rgb(2,136,209) !important; } + +.mdl-color--light-blue-700 { + background-color: rgb(2,136,209) !important; } + +.mdl-color-text--light-blue-800 { + color: rgb(2,119,189) !important; } + +.mdl-color--light-blue-800 { + background-color: rgb(2,119,189) !important; } + +.mdl-color-text--light-blue-900 { + color: rgb(1,87,155) !important; } + +.mdl-color--light-blue-900 { + background-color: rgb(1,87,155) !important; } + +.mdl-color-text--light-blue-A100 { + color: rgb(128,216,255) !important; } + +.mdl-color--light-blue-A100 { + background-color: rgb(128,216,255) !important; } + +.mdl-color-text--light-blue-A200 { + color: rgb(64,196,255) !important; } + +.mdl-color--light-blue-A200 { + background-color: rgb(64,196,255) !important; } + +.mdl-color-text--light-blue-A400 { + color: rgb(0,176,255) !important; } + +.mdl-color--light-blue-A400 { + background-color: rgb(0,176,255) !important; } + +.mdl-color-text--light-blue-A700 { + color: rgb(0,145,234) !important; } + +.mdl-color--light-blue-A700 { + background-color: rgb(0,145,234) !important; } + +.mdl-color-text--cyan { + color: rgb(0,188,212) !important; } + +.mdl-color--cyan { + background-color: rgb(0,188,212) !important; } + +.mdl-color-text--cyan-50 { + color: rgb(224,247,250) !important; } + +.mdl-color--cyan-50 { + background-color: rgb(224,247,250) !important; } + +.mdl-color-text--cyan-100 { + color: rgb(178,235,242) !important; } + +.mdl-color--cyan-100 { + background-color: rgb(178,235,242) !important; } + +.mdl-color-text--cyan-200 { + color: rgb(128,222,234) !important; } + +.mdl-color--cyan-200 { + background-color: rgb(128,222,234) !important; } + +.mdl-color-text--cyan-300 { + color: rgb(77,208,225) !important; } + +.mdl-color--cyan-300 { + background-color: rgb(77,208,225) !important; } + +.mdl-color-text--cyan-400 { + color: rgb(38,198,218) !important; } + +.mdl-color--cyan-400 { + background-color: rgb(38,198,218) !important; } + +.mdl-color-text--cyan-500 { + color: rgb(0,188,212) !important; } + +.mdl-color--cyan-500 { + background-color: rgb(0,188,212) !important; } + +.mdl-color-text--cyan-600 { + color: rgb(0,172,193) !important; } + +.mdl-color--cyan-600 { + background-color: rgb(0,172,193) !important; } + +.mdl-color-text--cyan-700 { + color: rgb(0,151,167) !important; } + +.mdl-color--cyan-700 { + background-color: rgb(0,151,167) !important; } + +.mdl-color-text--cyan-800 { + color: rgb(0,131,143) !important; } + +.mdl-color--cyan-800 { + background-color: rgb(0,131,143) !important; } + +.mdl-color-text--cyan-900 { + color: rgb(0,96,100) !important; } + +.mdl-color--cyan-900 { + background-color: rgb(0,96,100) !important; } + +.mdl-color-text--cyan-A100 { + color: rgb(132,255,255) !important; } + +.mdl-color--cyan-A100 { + background-color: rgb(132,255,255) !important; } + +.mdl-color-text--cyan-A200 { + color: rgb(24,255,255) !important; } + +.mdl-color--cyan-A200 { + background-color: rgb(24,255,255) !important; } + +.mdl-color-text--cyan-A400 { + color: rgb(0,229,255) !important; } + +.mdl-color--cyan-A400 { + background-color: rgb(0,229,255) !important; } + +.mdl-color-text--cyan-A700 { + color: rgb(0,184,212) !important; } + +.mdl-color--cyan-A700 { + background-color: rgb(0,184,212) !important; } + +.mdl-color-text--teal { + color: rgb(0,150,136) !important; } + +.mdl-color--teal { + background-color: rgb(0,150,136) !important; } + +.mdl-color-text--teal-50 { + color: rgb(224,242,241) !important; } + +.mdl-color--teal-50 { + background-color: rgb(224,242,241) !important; } + +.mdl-color-text--teal-100 { + color: rgb(178,223,219) !important; } + +.mdl-color--teal-100 { + background-color: rgb(178,223,219) !important; } + +.mdl-color-text--teal-200 { + color: rgb(128,203,196) !important; } + +.mdl-color--teal-200 { + background-color: rgb(128,203,196) !important; } + +.mdl-color-text--teal-300 { + color: rgb(77,182,172) !important; } + +.mdl-color--teal-300 { + background-color: rgb(77,182,172) !important; } + +.mdl-color-text--teal-400 { + color: rgb(38,166,154) !important; } + +.mdl-color--teal-400 { + background-color: rgb(38,166,154) !important; } + +.mdl-color-text--teal-500 { + color: rgb(0,150,136) !important; } + +.mdl-color--teal-500 { + background-color: rgb(0,150,136) !important; } + +.mdl-color-text--teal-600 { + color: rgb(0,137,123) !important; } + +.mdl-color--teal-600 { + background-color: rgb(0,137,123) !important; } + +.mdl-color-text--teal-700 { + color: rgb(0,121,107) !important; } + +.mdl-color--teal-700 { + background-color: rgb(0,121,107) !important; } + +.mdl-color-text--teal-800 { + color: rgb(0,105,92) !important; } + +.mdl-color--teal-800 { + background-color: rgb(0,105,92) !important; } + +.mdl-color-text--teal-900 { + color: rgb(0,77,64) !important; } + +.mdl-color--teal-900 { + background-color: rgb(0,77,64) !important; } + +.mdl-color-text--teal-A100 { + color: rgb(167,255,235) !important; } + +.mdl-color--teal-A100 { + background-color: rgb(167,255,235) !important; } + +.mdl-color-text--teal-A200 { + color: rgb(100,255,218) !important; } + +.mdl-color--teal-A200 { + background-color: rgb(100,255,218) !important; } + +.mdl-color-text--teal-A400 { + color: rgb(29,233,182) !important; } + +.mdl-color--teal-A400 { + background-color: rgb(29,233,182) !important; } + +.mdl-color-text--teal-A700 { + color: rgb(0,191,165) !important; } + +.mdl-color--teal-A700 { + background-color: rgb(0,191,165) !important; } + +.mdl-color-text--green { + color: rgb(76,175,80) !important; } + +.mdl-color--green { + background-color: rgb(76,175,80) !important; } + +.mdl-color-text--green-50 { + color: rgb(232,245,233) !important; } + +.mdl-color--green-50 { + background-color: rgb(232,245,233) !important; } + +.mdl-color-text--green-100 { + color: rgb(200,230,201) !important; } + +.mdl-color--green-100 { + background-color: rgb(200,230,201) !important; } + +.mdl-color-text--green-200 { + color: rgb(165,214,167) !important; } + +.mdl-color--green-200 { + background-color: rgb(165,214,167) !important; } + +.mdl-color-text--green-300 { + color: rgb(129,199,132) !important; } + +.mdl-color--green-300 { + background-color: rgb(129,199,132) !important; } + +.mdl-color-text--green-400 { + color: rgb(102,187,106) !important; } + +.mdl-color--green-400 { + background-color: rgb(102,187,106) !important; } + +.mdl-color-text--green-500 { + color: rgb(76,175,80) !important; } + +.mdl-color--green-500 { + background-color: rgb(76,175,80) !important; } + +.mdl-color-text--green-600 { + color: rgb(67,160,71) !important; } + +.mdl-color--green-600 { + background-color: rgb(67,160,71) !important; } + +.mdl-color-text--green-700 { + color: rgb(56,142,60) !important; } + +.mdl-color--green-700 { + background-color: rgb(56,142,60) !important; } + +.mdl-color-text--green-800 { + color: rgb(46,125,50) !important; } + +.mdl-color--green-800 { + background-color: rgb(46,125,50) !important; } + +.mdl-color-text--green-900 { + color: rgb(27,94,32) !important; } + +.mdl-color--green-900 { + background-color: rgb(27,94,32) !important; } + +.mdl-color-text--green-A100 { + color: rgb(185,246,202) !important; } + +.mdl-color--green-A100 { + background-color: rgb(185,246,202) !important; } + +.mdl-color-text--green-A200 { + color: rgb(105,240,174) !important; } + +.mdl-color--green-A200 { + background-color: rgb(105,240,174) !important; } + +.mdl-color-text--green-A400 { + color: rgb(0,230,118) !important; } + +.mdl-color--green-A400 { + background-color: rgb(0,230,118) !important; } + +.mdl-color-text--green-A700 { + color: rgb(0,200,83) !important; } + +.mdl-color--green-A700 { + background-color: rgb(0,200,83) !important; } + +.mdl-color-text--light-green { + color: rgb(139,195,74) !important; } + +.mdl-color--light-green { + background-color: rgb(139,195,74) !important; } + +.mdl-color-text--light-green-50 { + color: rgb(241,248,233) !important; } + +.mdl-color--light-green-50 { + background-color: rgb(241,248,233) !important; } + +.mdl-color-text--light-green-100 { + color: rgb(220,237,200) !important; } + +.mdl-color--light-green-100 { + background-color: rgb(220,237,200) !important; } + +.mdl-color-text--light-green-200 { + color: rgb(197,225,165) !important; } + +.mdl-color--light-green-200 { + background-color: rgb(197,225,165) !important; } + +.mdl-color-text--light-green-300 { + color: rgb(174,213,129) !important; } + +.mdl-color--light-green-300 { + background-color: rgb(174,213,129) !important; } + +.mdl-color-text--light-green-400 { + color: rgb(156,204,101) !important; } + +.mdl-color--light-green-400 { + background-color: rgb(156,204,101) !important; } + +.mdl-color-text--light-green-500 { + color: rgb(139,195,74) !important; } + +.mdl-color--light-green-500 { + background-color: rgb(139,195,74) !important; } + +.mdl-color-text--light-green-600 { + color: rgb(124,179,66) !important; } + +.mdl-color--light-green-600 { + background-color: rgb(124,179,66) !important; } + +.mdl-color-text--light-green-700 { + color: rgb(104,159,56) !important; } + +.mdl-color--light-green-700 { + background-color: rgb(104,159,56) !important; } + +.mdl-color-text--light-green-800 { + color: rgb(85,139,47) !important; } + +.mdl-color--light-green-800 { + background-color: rgb(85,139,47) !important; } + +.mdl-color-text--light-green-900 { + color: rgb(51,105,30) !important; } + +.mdl-color--light-green-900 { + background-color: rgb(51,105,30) !important; } + +.mdl-color-text--light-green-A100 { + color: rgb(204,255,144) !important; } + +.mdl-color--light-green-A100 { + background-color: rgb(204,255,144) !important; } + +.mdl-color-text--light-green-A200 { + color: rgb(178,255,89) !important; } + +.mdl-color--light-green-A200 { + background-color: rgb(178,255,89) !important; } + +.mdl-color-text--light-green-A400 { + color: rgb(118,255,3) !important; } + +.mdl-color--light-green-A400 { + background-color: rgb(118,255,3) !important; } + +.mdl-color-text--light-green-A700 { + color: rgb(100,221,23) !important; } + +.mdl-color--light-green-A700 { + background-color: rgb(100,221,23) !important; } + +.mdl-color-text--lime { + color: rgb(205,220,57) !important; } + +.mdl-color--lime { + background-color: rgb(205,220,57) !important; } + +.mdl-color-text--lime-50 { + color: rgb(249,251,231) !important; } + +.mdl-color--lime-50 { + background-color: rgb(249,251,231) !important; } + +.mdl-color-text--lime-100 { + color: rgb(240,244,195) !important; } + +.mdl-color--lime-100 { + background-color: rgb(240,244,195) !important; } + +.mdl-color-text--lime-200 { + color: rgb(230,238,156) !important; } + +.mdl-color--lime-200 { + background-color: rgb(230,238,156) !important; } + +.mdl-color-text--lime-300 { + color: rgb(220,231,117) !important; } + +.mdl-color--lime-300 { + background-color: rgb(220,231,117) !important; } + +.mdl-color-text--lime-400 { + color: rgb(212,225,87) !important; } + +.mdl-color--lime-400 { + background-color: rgb(212,225,87) !important; } + +.mdl-color-text--lime-500 { + color: rgb(205,220,57) !important; } + +.mdl-color--lime-500 { + background-color: rgb(205,220,57) !important; } + +.mdl-color-text--lime-600 { + color: rgb(192,202,51) !important; } + +.mdl-color--lime-600 { + background-color: rgb(192,202,51) !important; } + +.mdl-color-text--lime-700 { + color: rgb(175,180,43) !important; } + +.mdl-color--lime-700 { + background-color: rgb(175,180,43) !important; } + +.mdl-color-text--lime-800 { + color: rgb(158,157,36) !important; } + +.mdl-color--lime-800 { + background-color: rgb(158,157,36) !important; } + +.mdl-color-text--lime-900 { + color: rgb(130,119,23) !important; } + +.mdl-color--lime-900 { + background-color: rgb(130,119,23) !important; } + +.mdl-color-text--lime-A100 { + color: rgb(244,255,129) !important; } + +.mdl-color--lime-A100 { + background-color: rgb(244,255,129) !important; } + +.mdl-color-text--lime-A200 { + color: rgb(238,255,65) !important; } + +.mdl-color--lime-A200 { + background-color: rgb(238,255,65) !important; } + +.mdl-color-text--lime-A400 { + color: rgb(198,255,0) !important; } + +.mdl-color--lime-A400 { + background-color: rgb(198,255,0) !important; } + +.mdl-color-text--lime-A700 { + color: rgb(174,234,0) !important; } + +.mdl-color--lime-A700 { + background-color: rgb(174,234,0) !important; } + +.mdl-color-text--yellow { + color: rgb(255,235,59) !important; } + +.mdl-color--yellow { + background-color: rgb(255,235,59) !important; } + +.mdl-color-text--yellow-50 { + color: rgb(255,253,231) !important; } + +.mdl-color--yellow-50 { + background-color: rgb(255,253,231) !important; } + +.mdl-color-text--yellow-100 { + color: rgb(255,249,196) !important; } + +.mdl-color--yellow-100 { + background-color: rgb(255,249,196) !important; } + +.mdl-color-text--yellow-200 { + color: rgb(255,245,157) !important; } + +.mdl-color--yellow-200 { + background-color: rgb(255,245,157) !important; } + +.mdl-color-text--yellow-300 { + color: rgb(255,241,118) !important; } + +.mdl-color--yellow-300 { + background-color: rgb(255,241,118) !important; } + +.mdl-color-text--yellow-400 { + color: rgb(255,238,88) !important; } + +.mdl-color--yellow-400 { + background-color: rgb(255,238,88) !important; } + +.mdl-color-text--yellow-500 { + color: rgb(255,235,59) !important; } + +.mdl-color--yellow-500 { + background-color: rgb(255,235,59) !important; } + +.mdl-color-text--yellow-600 { + color: rgb(253,216,53) !important; } + +.mdl-color--yellow-600 { + background-color: rgb(253,216,53) !important; } + +.mdl-color-text--yellow-700 { + color: rgb(251,192,45) !important; } + +.mdl-color--yellow-700 { + background-color: rgb(251,192,45) !important; } + +.mdl-color-text--yellow-800 { + color: rgb(249,168,37) !important; } + +.mdl-color--yellow-800 { + background-color: rgb(249,168,37) !important; } + +.mdl-color-text--yellow-900 { + color: rgb(245,127,23) !important; } + +.mdl-color--yellow-900 { + background-color: rgb(245,127,23) !important; } + +.mdl-color-text--yellow-A100 { + color: rgb(255,255,141) !important; } + +.mdl-color--yellow-A100 { + background-color: rgb(255,255,141) !important; } + +.mdl-color-text--yellow-A200 { + color: rgb(255,255,0) !important; } + +.mdl-color--yellow-A200 { + background-color: rgb(255,255,0) !important; } + +.mdl-color-text--yellow-A400 { + color: rgb(255,234,0) !important; } + +.mdl-color--yellow-A400 { + background-color: rgb(255,234,0) !important; } + +.mdl-color-text--yellow-A700 { + color: rgb(255,214,0) !important; } + +.mdl-color--yellow-A700 { + background-color: rgb(255,214,0) !important; } + +.mdl-color-text--amber { + color: rgb(255,193,7) !important; } + +.mdl-color--amber { + background-color: rgb(255,193,7) !important; } + +.mdl-color-text--amber-50 { + color: rgb(255,248,225) !important; } + +.mdl-color--amber-50 { + background-color: rgb(255,248,225) !important; } + +.mdl-color-text--amber-100 { + color: rgb(255,236,179) !important; } + +.mdl-color--amber-100 { + background-color: rgb(255,236,179) !important; } + +.mdl-color-text--amber-200 { + color: rgb(255,224,130) !important; } + +.mdl-color--amber-200 { + background-color: rgb(255,224,130) !important; } + +.mdl-color-text--amber-300 { + color: rgb(255,213,79) !important; } + +.mdl-color--amber-300 { + background-color: rgb(255,213,79) !important; } + +.mdl-color-text--amber-400 { + color: rgb(255,202,40) !important; } + +.mdl-color--amber-400 { + background-color: rgb(255,202,40) !important; } + +.mdl-color-text--amber-500 { + color: rgb(255,193,7) !important; } + +.mdl-color--amber-500 { + background-color: rgb(255,193,7) !important; } + +.mdl-color-text--amber-600 { + color: rgb(255,179,0) !important; } + +.mdl-color--amber-600 { + background-color: rgb(255,179,0) !important; } + +.mdl-color-text--amber-700 { + color: rgb(255,160,0) !important; } + +.mdl-color--amber-700 { + background-color: rgb(255,160,0) !important; } + +.mdl-color-text--amber-800 { + color: rgb(255,143,0) !important; } + +.mdl-color--amber-800 { + background-color: rgb(255,143,0) !important; } + +.mdl-color-text--amber-900 { + color: rgb(255,111,0) !important; } + +.mdl-color--amber-900 { + background-color: rgb(255,111,0) !important; } + +.mdl-color-text--amber-A100 { + color: rgb(255,229,127) !important; } + +.mdl-color--amber-A100 { + background-color: rgb(255,229,127) !important; } + +.mdl-color-text--amber-A200 { + color: rgb(255,215,64) !important; } + +.mdl-color--amber-A200 { + background-color: rgb(255,215,64) !important; } + +.mdl-color-text--amber-A400 { + color: rgb(255,196,0) !important; } + +.mdl-color--amber-A400 { + background-color: rgb(255,196,0) !important; } + +.mdl-color-text--amber-A700 { + color: rgb(255,171,0) !important; } + +.mdl-color--amber-A700 { + background-color: rgb(255,171,0) !important; } + +.mdl-color-text--orange { + color: rgb(255,152,0) !important; } + +.mdl-color--orange { + background-color: rgb(255,152,0) !important; } + +.mdl-color-text--orange-50 { + color: rgb(255,243,224) !important; } + +.mdl-color--orange-50 { + background-color: rgb(255,243,224) !important; } + +.mdl-color-text--orange-100 { + color: rgb(255,224,178) !important; } + +.mdl-color--orange-100 { + background-color: rgb(255,224,178) !important; } + +.mdl-color-text--orange-200 { + color: rgb(255,204,128) !important; } + +.mdl-color--orange-200 { + background-color: rgb(255,204,128) !important; } + +.mdl-color-text--orange-300 { + color: rgb(255,183,77) !important; } + +.mdl-color--orange-300 { + background-color: rgb(255,183,77) !important; } + +.mdl-color-text--orange-400 { + color: rgb(255,167,38) !important; } + +.mdl-color--orange-400 { + background-color: rgb(255,167,38) !important; } + +.mdl-color-text--orange-500 { + color: rgb(255,152,0) !important; } + +.mdl-color--orange-500 { + background-color: rgb(255,152,0) !important; } + +.mdl-color-text--orange-600 { + color: rgb(251,140,0) !important; } + +.mdl-color--orange-600 { + background-color: rgb(251,140,0) !important; } + +.mdl-color-text--orange-700 { + color: rgb(245,124,0) !important; } + +.mdl-color--orange-700 { + background-color: rgb(245,124,0) !important; } + +.mdl-color-text--orange-800 { + color: rgb(239,108,0) !important; } + +.mdl-color--orange-800 { + background-color: rgb(239,108,0) !important; } + +.mdl-color-text--orange-900 { + color: rgb(230,81,0) !important; } + +.mdl-color--orange-900 { + background-color: rgb(230,81,0) !important; } + +.mdl-color-text--orange-A100 { + color: rgb(255,209,128) !important; } + +.mdl-color--orange-A100 { + background-color: rgb(255,209,128) !important; } + +.mdl-color-text--orange-A200 { + color: rgb(255,171,64) !important; } + +.mdl-color--orange-A200 { + background-color: rgb(255,171,64) !important; } + +.mdl-color-text--orange-A400 { + color: rgb(255,145,0) !important; } + +.mdl-color--orange-A400 { + background-color: rgb(255,145,0) !important; } + +.mdl-color-text--orange-A700 { + color: rgb(255,109,0) !important; } + +.mdl-color--orange-A700 { + background-color: rgb(255,109,0) !important; } + +.mdl-color-text--deep-orange { + color: rgb(255,87,34) !important; } + +.mdl-color--deep-orange { + background-color: rgb(255,87,34) !important; } + +.mdl-color-text--deep-orange-50 { + color: rgb(251,233,231) !important; } + +.mdl-color--deep-orange-50 { + background-color: rgb(251,233,231) !important; } + +.mdl-color-text--deep-orange-100 { + color: rgb(255,204,188) !important; } + +.mdl-color--deep-orange-100 { + background-color: rgb(255,204,188) !important; } + +.mdl-color-text--deep-orange-200 { + color: rgb(255,171,145) !important; } + +.mdl-color--deep-orange-200 { + background-color: rgb(255,171,145) !important; } + +.mdl-color-text--deep-orange-300 { + color: rgb(255,138,101) !important; } + +.mdl-color--deep-orange-300 { + background-color: rgb(255,138,101) !important; } + +.mdl-color-text--deep-orange-400 { + color: rgb(255,112,67) !important; } + +.mdl-color--deep-orange-400 { + background-color: rgb(255,112,67) !important; } + +.mdl-color-text--deep-orange-500 { + color: rgb(255,87,34) !important; } + +.mdl-color--deep-orange-500 { + background-color: rgb(255,87,34) !important; } + +.mdl-color-text--deep-orange-600 { + color: rgb(244,81,30) !important; } + +.mdl-color--deep-orange-600 { + background-color: rgb(244,81,30) !important; } + +.mdl-color-text--deep-orange-700 { + color: rgb(230,74,25) !important; } + +.mdl-color--deep-orange-700 { + background-color: rgb(230,74,25) !important; } + +.mdl-color-text--deep-orange-800 { + color: rgb(216,67,21) !important; } + +.mdl-color--deep-orange-800 { + background-color: rgb(216,67,21) !important; } + +.mdl-color-text--deep-orange-900 { + color: rgb(191,54,12) !important; } + +.mdl-color--deep-orange-900 { + background-color: rgb(191,54,12) !important; } + +.mdl-color-text--deep-orange-A100 { + color: rgb(255,158,128) !important; } + +.mdl-color--deep-orange-A100 { + background-color: rgb(255,158,128) !important; } + +.mdl-color-text--deep-orange-A200 { + color: rgb(255,110,64) !important; } + +.mdl-color--deep-orange-A200 { + background-color: rgb(255,110,64) !important; } + +.mdl-color-text--deep-orange-A400 { + color: rgb(255,61,0) !important; } + +.mdl-color--deep-orange-A400 { + background-color: rgb(255,61,0) !important; } + +.mdl-color-text--deep-orange-A700 { + color: rgb(221,44,0) !important; } + +.mdl-color--deep-orange-A700 { + background-color: rgb(221,44,0) !important; } + +.mdl-color-text--brown { + color: rgb(121,85,72) !important; } + +.mdl-color--brown { + background-color: rgb(121,85,72) !important; } + +.mdl-color-text--brown-50 { + color: rgb(239,235,233) !important; } + +.mdl-color--brown-50 { + background-color: rgb(239,235,233) !important; } + +.mdl-color-text--brown-100 { + color: rgb(215,204,200) !important; } + +.mdl-color--brown-100 { + background-color: rgb(215,204,200) !important; } + +.mdl-color-text--brown-200 { + color: rgb(188,170,164) !important; } + +.mdl-color--brown-200 { + background-color: rgb(188,170,164) !important; } + +.mdl-color-text--brown-300 { + color: rgb(161,136,127) !important; } + +.mdl-color--brown-300 { + background-color: rgb(161,136,127) !important; } + +.mdl-color-text--brown-400 { + color: rgb(141,110,99) !important; } + +.mdl-color--brown-400 { + background-color: rgb(141,110,99) !important; } + +.mdl-color-text--brown-500 { + color: rgb(121,85,72) !important; } + +.mdl-color--brown-500 { + background-color: rgb(121,85,72) !important; } + +.mdl-color-text--brown-600 { + color: rgb(109,76,65) !important; } + +.mdl-color--brown-600 { + background-color: rgb(109,76,65) !important; } + +.mdl-color-text--brown-700 { + color: rgb(93,64,55) !important; } + +.mdl-color--brown-700 { + background-color: rgb(93,64,55) !important; } + +.mdl-color-text--brown-800 { + color: rgb(78,52,46) !important; } + +.mdl-color--brown-800 { + background-color: rgb(78,52,46) !important; } + +.mdl-color-text--brown-900 { + color: rgb(62,39,35) !important; } + +.mdl-color--brown-900 { + background-color: rgb(62,39,35) !important; } + +.mdl-color-text--grey { + color: rgb(158,158,158) !important; } + +.mdl-color--grey { + background-color: rgb(158,158,158) !important; } + +.mdl-color-text--grey-50 { + color: rgb(250,250,250) !important; } + +.mdl-color--grey-50 { + background-color: rgb(250,250,250) !important; } + +.mdl-color-text--grey-100 { + color: rgb(245,245,245) !important; } + +.mdl-color--grey-100 { + background-color: rgb(245,245,245) !important; } + +.mdl-color-text--grey-200 { + color: rgb(238,238,238) !important; } + +.mdl-color--grey-200 { + background-color: rgb(238,238,238) !important; } + +.mdl-color-text--grey-300 { + color: rgb(224,224,224) !important; } + +.mdl-color--grey-300 { + background-color: rgb(224,224,224) !important; } + +.mdl-color-text--grey-400 { + color: rgb(189,189,189) !important; } + +.mdl-color--grey-400 { + background-color: rgb(189,189,189) !important; } + +.mdl-color-text--grey-500 { + color: rgb(158,158,158) !important; } + +.mdl-color--grey-500 { + background-color: rgb(158,158,158) !important; } + +.mdl-color-text--grey-600 { + color: rgb(117,117,117) !important; } + +.mdl-color--grey-600 { + background-color: rgb(117,117,117) !important; } + +.mdl-color-text--grey-700 { + color: rgb(97,97,97) !important; } + +.mdl-color--grey-700 { + background-color: rgb(97,97,97) !important; } + +.mdl-color-text--grey-800 { + color: rgb(66,66,66) !important; } + +.mdl-color--grey-800 { + background-color: rgb(66,66,66) !important; } + +.mdl-color-text--grey-900 { + color: rgb(33,33,33) !important; } + +.mdl-color--grey-900 { + background-color: rgb(33,33,33) !important; } + +.mdl-color-text--blue-grey { + color: rgb(96,125,139) !important; } + +.mdl-color--blue-grey { + background-color: rgb(96,125,139) !important; } + +.mdl-color-text--blue-grey-50 { + color: rgb(236,239,241) !important; } + +.mdl-color--blue-grey-50 { + background-color: rgb(236,239,241) !important; } + +.mdl-color-text--blue-grey-100 { + color: rgb(207,216,220) !important; } + +.mdl-color--blue-grey-100 { + background-color: rgb(207,216,220) !important; } + +.mdl-color-text--blue-grey-200 { + color: rgb(176,190,197) !important; } + +.mdl-color--blue-grey-200 { + background-color: rgb(176,190,197) !important; } + +.mdl-color-text--blue-grey-300 { + color: rgb(144,164,174) !important; } + +.mdl-color--blue-grey-300 { + background-color: rgb(144,164,174) !important; } + +.mdl-color-text--blue-grey-400 { + color: rgb(120,144,156) !important; } + +.mdl-color--blue-grey-400 { + background-color: rgb(120,144,156) !important; } + +.mdl-color-text--blue-grey-500 { + color: rgb(96,125,139) !important; } + +.mdl-color--blue-grey-500 { + background-color: rgb(96,125,139) !important; } + +.mdl-color-text--blue-grey-600 { + color: rgb(84,110,122) !important; } + +.mdl-color--blue-grey-600 { + background-color: rgb(84,110,122) !important; } + +.mdl-color-text--blue-grey-700 { + color: rgb(69,90,100) !important; } + +.mdl-color--blue-grey-700 { + background-color: rgb(69,90,100) !important; } + +.mdl-color-text--blue-grey-800 { + color: rgb(55,71,79) !important; } + +.mdl-color--blue-grey-800 { + background-color: rgb(55,71,79) !important; } + +.mdl-color-text--blue-grey-900 { + color: rgb(38,50,56) !important; } + +.mdl-color--blue-grey-900 { + background-color: rgb(38,50,56) !important; } + +.mdl-color--black { + background-color: rgb(0,0,0) !important; } + +.mdl-color-text--black { + color: rgb(0,0,0) !important; } + +.mdl-color--white { + background-color: rgb(255,255,255) !important; } + +.mdl-color-text--white { + color: rgb(255,255,255) !important; } + +.mdl-color--primary { + background-color: rgb(63,81,181) !important; } + +.mdl-color--primary-contrast { + background-color: rgb(255,255,255) !important; } + +.mdl-color--primary-dark { + background-color: rgb(48,63,159) !important; } + +.mdl-color--accent { + background-color: rgb(255,64,129) !important; } + +.mdl-color--accent-contrast { + background-color: rgb(255,255,255) !important; } + +.mdl-color-text--primary { + color: rgb(63,81,181) !important; } + +.mdl-color-text--primary-contrast { + color: rgb(255,255,255) !important; } + +.mdl-color-text--primary-dark { + color: rgb(48,63,159) !important; } + +.mdl-color-text--accent { + color: rgb(255,64,129) !important; } + +.mdl-color-text--accent-contrast { + color: rgb(255,255,255) !important; } + +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/*------------------------------------* $CONTENTS +\*------------------------------------*/ +/** + * STYLE GUIDE VARIABLES------------------Declarations of Sass variables + * -----Typography + * -----Colors + * -----Textfield + * -----Switch + * -----Spinner + * -----Radio + * -----Menu + * -----List + * -----Layout + * -----Icon toggles + * -----Footer + * -----Column + * -----Checkbox + * -----Card + * -----Button + * -----Animation + * -----Progress + * -----Badge + * -----Shadows + * -----Grid + * -----Data table + */ +/* ========== TYPOGRAPHY ========== */ +/* We're splitting fonts into "preferred" and "performance" in order to optimize + page loading. For important text, such as the body, we want it to load + immediately and not wait for the web font load, whereas for other sections, + such as headers and titles, we're OK with things taking a bit longer to load. + We do have some optional classes and parameters in the mixins, in case you + definitely want to make sure you're using the preferred font and don't mind + the performance hit. + We should be able to improve on this once CSS Font Loading L3 becomes more + widely available. +*/ +/* ========== COLORS ========== */ +/** +* +* Material design color palettes. +* @see http://www.google.com/design/spec/style/color.html +* +**/ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* ========== Color Palettes ========== */ +/* colors.scss */ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* ========== IMAGES ========== */ +/* ========== Color & Themes ========== */ +/* ========== Typography ========== */ +/* ========== Components ========== */ +/* ========== Standard Buttons ========== */ +/* ========== Icon Toggles ========== */ +/* ========== Radio Buttons ========== */ +/* ========== Ripple effect ========== */ +/* ========== Layout ========== */ +/* ========== Content Tabs ========== */ +/* ========== Checkboxes ========== */ +/* ========== Switches ========== */ +/* ========== Spinner ========== */ +/* ========== Text fields ========== */ +/* ========== Card ========== */ +/* ========== Sliders ========== */ +/* ========== Progress ========== */ +/* ========== List ========== */ +/* ========== Item ========== */ +/* ========== Dropdown menu ========== */ +/* ========== Tooltips ========== */ +/* ========== Footer ========== */ +/* TEXTFIELD */ +/* SWITCH */ +/* SPINNER */ +/* RADIO */ +/* MENU */ +/* LIST */ +/* LAYOUT */ +/* ICON TOGGLE */ +/* FOOTER */ +/*mega-footer*/ +/*mini-footer*/ +/* CHECKBOX */ +/* CARD */ +/* Card dimensions */ +/* Cover image */ +/* BUTTON */ +/** + * + * Dimensions + * + */ +/* ANIMATION */ +/* PROGRESS */ +/* BADGE */ +/* SHADOWS */ +/* GRID */ +/* DATA TABLE */ +/* TOOLTIP */ +.mdl-ripple { + background: rgb(0,0,0); + border-radius: 50%; + height: 50px; + left: 0; + opacity: 0; + pointer-events: none; + position: absolute; + top: 0; + -webkit-transform: translate(-50%, -50%); + -ms-transform: translate(-50%, -50%); + transform: translate(-50%, -50%); + width: 50px; + overflow: hidden; } + .mdl-ripple.is-animating { + -webkit-transition: -webkit-transform 0.3s cubic-bezier(0, 0, 0.2, 1), width 0.3s cubic-bezier(0, 0, 0.2, 1), height 0.3s cubic-bezier(0, 0, 0.2, 1), opacity 0.6s cubic-bezier(0, 0, 0.2, 1); + transition: transform 0.3s cubic-bezier(0, 0, 0.2, 1), width 0.3s cubic-bezier(0, 0, 0.2, 1), height 0.3s cubic-bezier(0, 0, 0.2, 1), opacity 0.6s cubic-bezier(0, 0, 0.2, 1); } + .mdl-ripple.is-visible { + opacity: 0.3; } + +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/*------------------------------------* $CONTENTS +\*------------------------------------*/ +/** + * STYLE GUIDE VARIABLES------------------Declarations of Sass variables + * -----Typography + * -----Colors + * -----Textfield + * -----Switch + * -----Spinner + * -----Radio + * -----Menu + * -----List + * -----Layout + * -----Icon toggles + * -----Footer + * -----Column + * -----Checkbox + * -----Card + * -----Button + * -----Animation + * -----Progress + * -----Badge + * -----Shadows + * -----Grid + * -----Data table + */ +/* ========== TYPOGRAPHY ========== */ +/* We're splitting fonts into "preferred" and "performance" in order to optimize + page loading. For important text, such as the body, we want it to load + immediately and not wait for the web font load, whereas for other sections, + such as headers and titles, we're OK with things taking a bit longer to load. + We do have some optional classes and parameters in the mixins, in case you + definitely want to make sure you're using the preferred font and don't mind + the performance hit. + We should be able to improve on this once CSS Font Loading L3 becomes more + widely available. +*/ +/* ========== COLORS ========== */ +/** +* +* Material design color palettes. +* @see http://www.google.com/design/spec/style/color.html +* +**/ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* ========== Color Palettes ========== */ +/* colors.scss */ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* ========== IMAGES ========== */ +/* ========== Color & Themes ========== */ +/* ========== Typography ========== */ +/* ========== Components ========== */ +/* ========== Standard Buttons ========== */ +/* ========== Icon Toggles ========== */ +/* ========== Radio Buttons ========== */ +/* ========== Ripple effect ========== */ +/* ========== Layout ========== */ +/* ========== Content Tabs ========== */ +/* ========== Checkboxes ========== */ +/* ========== Switches ========== */ +/* ========== Spinner ========== */ +/* ========== Text fields ========== */ +/* ========== Card ========== */ +/* ========== Sliders ========== */ +/* ========== Progress ========== */ +/* ========== List ========== */ +/* ========== Item ========== */ +/* ========== Dropdown menu ========== */ +/* ========== Tooltips ========== */ +/* ========== Footer ========== */ +/* TEXTFIELD */ +/* SWITCH */ +/* SPINNER */ +/* RADIO */ +/* MENU */ +/* LIST */ +/* LAYOUT */ +/* ICON TOGGLE */ +/* FOOTER */ +/*mega-footer*/ +/*mini-footer*/ +/* CHECKBOX */ +/* CARD */ +/* Card dimensions */ +/* Cover image */ +/* BUTTON */ +/** + * + * Dimensions + * + */ +/* ANIMATION */ +/* PROGRESS */ +/* BADGE */ +/* SHADOWS */ +/* GRID */ +/* DATA TABLE */ +/* TOOLTIP */ +.mdl-animation--default { + -webkit-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); } + +.mdl-animation--fast-out-slow-in { + -webkit-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); } + +.mdl-animation--linear-out-slow-in { + -webkit-transition-timing-function: cubic-bezier(0, 0, 0.2, 1); + transition-timing-function: cubic-bezier(0, 0, 0.2, 1); } + +.mdl-animation--fast-out-linear-in { + -webkit-transition-timing-function: cubic-bezier(0.4, 0, 1, 1); + transition-timing-function: cubic-bezier(0.4, 0, 1, 1); } + +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/*------------------------------------* $CONTENTS +\*------------------------------------*/ +/** + * STYLE GUIDE VARIABLES------------------Declarations of Sass variables + * -----Typography + * -----Colors + * -----Textfield + * -----Switch + * -----Spinner + * -----Radio + * -----Menu + * -----List + * -----Layout + * -----Icon toggles + * -----Footer + * -----Column + * -----Checkbox + * -----Card + * -----Button + * -----Animation + * -----Progress + * -----Badge + * -----Shadows + * -----Grid + * -----Data table + */ +/* ========== TYPOGRAPHY ========== */ +/* We're splitting fonts into "preferred" and "performance" in order to optimize + page loading. For important text, such as the body, we want it to load + immediately and not wait for the web font load, whereas for other sections, + such as headers and titles, we're OK with things taking a bit longer to load. + We do have some optional classes and parameters in the mixins, in case you + definitely want to make sure you're using the preferred font and don't mind + the performance hit. + We should be able to improve on this once CSS Font Loading L3 becomes more + widely available. +*/ +/* ========== COLORS ========== */ +/** +* +* Material design color palettes. +* @see http://www.google.com/design/spec/style/color.html +* +**/ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* ========== Color Palettes ========== */ +/* colors.scss */ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* ========== IMAGES ========== */ +/* ========== Color & Themes ========== */ +/* ========== Typography ========== */ +/* ========== Components ========== */ +/* ========== Standard Buttons ========== */ +/* ========== Icon Toggles ========== */ +/* ========== Radio Buttons ========== */ +/* ========== Ripple effect ========== */ +/* ========== Layout ========== */ +/* ========== Content Tabs ========== */ +/* ========== Checkboxes ========== */ +/* ========== Switches ========== */ +/* ========== Spinner ========== */ +/* ========== Text fields ========== */ +/* ========== Card ========== */ +/* ========== Sliders ========== */ +/* ========== Progress ========== */ +/* ========== List ========== */ +/* ========== Item ========== */ +/* ========== Dropdown menu ========== */ +/* ========== Tooltips ========== */ +/* ========== Footer ========== */ +/* TEXTFIELD */ +/* SWITCH */ +/* SPINNER */ +/* RADIO */ +/* MENU */ +/* LIST */ +/* LAYOUT */ +/* ICON TOGGLE */ +/* FOOTER */ +/*mega-footer*/ +/*mini-footer*/ +/* CHECKBOX */ +/* CARD */ +/* Card dimensions */ +/* Cover image */ +/* BUTTON */ +/** + * + * Dimensions + * + */ +/* ANIMATION */ +/* PROGRESS */ +/* BADGE */ +/* SHADOWS */ +/* GRID */ +/* DATA TABLE */ +/* TOOLTIP */ +.mdl-badge { + position: relative; + white-space: nowrap; + margin-right: 24px; } + .mdl-badge:not([data-badge]) { + margin-right: auto; } + .mdl-badge[data-badge]:after { + content: attr(data-badge); + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-box-orient: horizontal; + -webkit-box-direction: normal; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-flex-wrap: wrap; + -ms-flex-wrap: wrap; + flex-wrap: wrap; + -webkit-box-pack: center; + -webkit-justify-content: center; + -ms-flex-pack: center; + justify-content: center; + -webkit-align-content: center; + -ms-flex-line-pack: center; + align-content: center; + -webkit-box-align: center; + -webkit-align-items: center; + -ms-flex-align: center; + align-items: center; + position: absolute; + top: -11px; + right: -24px; + font-family: "Roboto", "Helvetica", "Arial", sans-serif; + font-weight: 600; + font-size: 12px; + width: 22px; + height: 22px; + border-radius: 50%; + background: rgb(255,64,129); + color: rgb(255,255,255); } + .mdl-button .mdl-badge[data-badge]:after { + top: -10px; + right: -5px; } + .mdl-badge.mdl-badge--no-background[data-badge]:after { + color: rgb(255,64,129); + background: rgb(255,255,255); + box-shadow: 0 0 1px gray; } + +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/*------------------------------------* $CONTENTS +\*------------------------------------*/ +/** + * STYLE GUIDE VARIABLES------------------Declarations of Sass variables + * -----Typography + * -----Colors + * -----Textfield + * -----Switch + * -----Spinner + * -----Radio + * -----Menu + * -----List + * -----Layout + * -----Icon toggles + * -----Footer + * -----Column + * -----Checkbox + * -----Card + * -----Button + * -----Animation + * -----Progress + * -----Badge + * -----Shadows + * -----Grid + * -----Data table + */ +/* ========== TYPOGRAPHY ========== */ +/* We're splitting fonts into "preferred" and "performance" in order to optimize + page loading. For important text, such as the body, we want it to load + immediately and not wait for the web font load, whereas for other sections, + such as headers and titles, we're OK with things taking a bit longer to load. + We do have some optional classes and parameters in the mixins, in case you + definitely want to make sure you're using the preferred font and don't mind + the performance hit. + We should be able to improve on this once CSS Font Loading L3 becomes more + widely available. +*/ +/* ========== COLORS ========== */ +/** +* +* Material design color palettes. +* @see http://www.google.com/design/spec/style/color.html +* +**/ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* ========== Color Palettes ========== */ +/* colors.scss */ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* ========== IMAGES ========== */ +/* ========== Color & Themes ========== */ +/* ========== Typography ========== */ +/* ========== Components ========== */ +/* ========== Standard Buttons ========== */ +/* ========== Icon Toggles ========== */ +/* ========== Radio Buttons ========== */ +/* ========== Ripple effect ========== */ +/* ========== Layout ========== */ +/* ========== Content Tabs ========== */ +/* ========== Checkboxes ========== */ +/* ========== Switches ========== */ +/* ========== Spinner ========== */ +/* ========== Text fields ========== */ +/* ========== Card ========== */ +/* ========== Sliders ========== */ +/* ========== Progress ========== */ +/* ========== List ========== */ +/* ========== Item ========== */ +/* ========== Dropdown menu ========== */ +/* ========== Tooltips ========== */ +/* ========== Footer ========== */ +/* TEXTFIELD */ +/* SWITCH */ +/* SPINNER */ +/* RADIO */ +/* MENU */ +/* LIST */ +/* LAYOUT */ +/* ICON TOGGLE */ +/* FOOTER */ +/*mega-footer*/ +/*mini-footer*/ +/* CHECKBOX */ +/* CARD */ +/* Card dimensions */ +/* Cover image */ +/* BUTTON */ +/** + * + * Dimensions + * + */ +/* ANIMATION */ +/* PROGRESS */ +/* BADGE */ +/* SHADOWS */ +/* GRID */ +/* DATA TABLE */ +/* TOOLTIP */ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* Typography */ +/* Shadows */ +/* Animations */ +.mdl-button { + background: transparent; + border: none; + border-radius: 2px; + color: rgb(0,0,0); + position: relative; + height: 36px; + min-width: 64px; + padding: 0 16px; + display: inline-block; + font-family: "Roboto", "Helvetica", "Arial", sans-serif; + font-size: 14px; + font-weight: 500; + text-transform: uppercase; + line-height: 1; + letter-spacing: 0; + overflow: hidden; + will-change: box-shadow, transform; + -webkit-transition: box-shadow 0.2s cubic-bezier(0.4, 0, 1, 1), background-color 0.2s cubic-bezier(0.4, 0, 0.2, 1), color 0.2s cubic-bezier(0.4, 0, 0.2, 1); + transition: box-shadow 0.2s cubic-bezier(0.4, 0, 1, 1), background-color 0.2s cubic-bezier(0.4, 0, 0.2, 1), color 0.2s cubic-bezier(0.4, 0, 0.2, 1); + outline: none; + cursor: pointer; + text-decoration: none; + text-align: center; + line-height: 36px; + vertical-align: middle; } + .mdl-button::-moz-focus-inner { + border: 0; } + .mdl-button:hover { + background-color: rgba(158,158,158, 0.20); } + .mdl-button:focus:not(:active) { + background-color: rgba(0,0,0, 0.12); } + .mdl-button:active { + background-color: rgba(158,158,158, 0.40); } + .mdl-button.mdl-button--colored { + color: rgb(63,81,181); } + .mdl-button.mdl-button--colored:focus:not(:active) { + background-color: rgba(0,0,0, 0.12); } + +input.mdl-button[type="submit"] { + -webkit-appearance: none; } + +.mdl-button--raised { + background: rgba(158,158,158, 0.20); + box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12); } + .mdl-button--raised:active { + box-shadow: 0 4px 5px 0 rgba(0, 0, 0, 0.14), 0 1px 10px 0 rgba(0, 0, 0, 0.12), 0 2px 4px -1px rgba(0, 0, 0, 0.2); + background-color: rgba(158,158,158, 0.40); } + .mdl-button--raised:focus:not(:active) { + box-shadow: 0 0 8px rgba(0, 0, 0, 0.18), 0 8px 16px rgba(0, 0, 0, 0.36); + background-color: rgba(158,158,158, 0.40); } + .mdl-button--raised.mdl-button--colored { + background: rgb(63,81,181); + color: rgb(255,255,255); } + .mdl-button--raised.mdl-button--colored:hover { + background-color: rgb(63,81,181); } + .mdl-button--raised.mdl-button--colored:active { + background-color: rgb(63,81,181); } + .mdl-button--raised.mdl-button--colored:focus:not(:active) { + background-color: rgb(63,81,181); } + .mdl-button--raised.mdl-button--colored .mdl-ripple { + background: rgb(255,255,255); } + +.mdl-button--fab { + border-radius: 50%; + font-size: 24px; + height: 56px; + margin: auto; + min-width: 56px; + width: 56px; + padding: 0; + overflow: hidden; + background: rgba(158,158,158, 0.20); + box-shadow: 0 1px 1.5px 0 rgba(0, 0, 0, 0.12), 0 1px 1px 0 rgba(0, 0, 0, 0.24); + position: relative; + line-height: normal; } + .mdl-button--fab .material-icons { + position: absolute; + top: 50%; + left: 50%; + -webkit-transform: translate(-12px, -12px); + -ms-transform: translate(-12px, -12px); + transform: translate(-12px, -12px); + line-height: 24px; + width: 24px; } + .mdl-button--fab.mdl-button--mini-fab { + height: 40px; + min-width: 40px; + width: 40px; } + .mdl-button--fab .mdl-button__ripple-container { + border-radius: 50%; + -webkit-mask-image: -webkit-radial-gradient(circle, white, black); } + .mdl-button--fab:active { + box-shadow: 0 4px 5px 0 rgba(0, 0, 0, 0.14), 0 1px 10px 0 rgba(0, 0, 0, 0.12), 0 2px 4px -1px rgba(0, 0, 0, 0.2); + background-color: rgba(158,158,158, 0.40); } + .mdl-button--fab:focus:not(:active) { + box-shadow: 0 0 8px rgba(0, 0, 0, 0.18), 0 8px 16px rgba(0, 0, 0, 0.36); + background-color: rgba(158,158,158, 0.40); } + .mdl-button--fab.mdl-button--colored { + background: rgb(255,64,129); + color: rgb(255,255,255); } + .mdl-button--fab.mdl-button--colored:hover { + background-color: rgb(255,64,129); } + .mdl-button--fab.mdl-button--colored:focus:not(:active) { + background-color: rgb(255,64,129); } + .mdl-button--fab.mdl-button--colored:active { + background-color: rgb(255,64,129); } + .mdl-button--fab.mdl-button--colored .mdl-ripple { + background: rgb(255,255,255); } + +.mdl-button--icon { + border-radius: 50%; + font-size: 24px; + height: 32px; + margin-left: 0; + margin-right: 0; + min-width: 32px; + width: 32px; + padding: 0; + overflow: hidden; + color: inherit; + line-height: normal; } + .mdl-button--icon .material-icons { + position: absolute; + top: 50%; + left: 50%; + -webkit-transform: translate(-12px, -12px); + -ms-transform: translate(-12px, -12px); + transform: translate(-12px, -12px); + line-height: 24px; + width: 24px; } + .mdl-button--icon.mdl-button--mini-icon { + height: 24px; + min-width: 24px; + width: 24px; } + .mdl-button--icon.mdl-button--mini-icon .material-icons { + top: 0px; + left: 0px; } + .mdl-button--icon .mdl-button__ripple-container { + border-radius: 50%; + -webkit-mask-image: -webkit-radial-gradient(circle, white, black); } + +.mdl-button__ripple-container { + display: block; + height: 100%; + left: 0px; + position: absolute; + top: 0px; + width: 100%; + z-index: 0; + overflow: hidden; } + .mdl-button[disabled] .mdl-button__ripple-container .mdl-ripple, + .mdl-button.mdl-button--disabled .mdl-button__ripple-container .mdl-ripple { + background-color: transparent; } + +.mdl-button--primary.mdl-button--primary { + color: rgb(63,81,181); } + .mdl-button--primary.mdl-button--primary .mdl-ripple { + background: rgb(255,255,255); } + .mdl-button--primary.mdl-button--primary.mdl-button--raised, .mdl-button--primary.mdl-button--primary.mdl-button--fab { + color: rgb(255,255,255); + background-color: rgb(63,81,181); } + +.mdl-button--accent.mdl-button--accent { + color: rgb(255,64,129); } + .mdl-button--accent.mdl-button--accent .mdl-ripple { + background: rgb(255,255,255); } + .mdl-button--accent.mdl-button--accent.mdl-button--raised, .mdl-button--accent.mdl-button--accent.mdl-button--fab { + color: rgb(255,255,255); + background-color: rgb(255,64,129); } + +.mdl-button[disabled][disabled], .mdl-button.mdl-button--disabled.mdl-button--disabled { + color: rgba(0,0,0, 0.26); + cursor: default; + background-color: transparent; } + +.mdl-button--fab[disabled][disabled], .mdl-button--fab.mdl-button--disabled.mdl-button--disabled { + background-color: rgba(0,0,0, 0.12); + color: rgba(0,0,0, 0.26); + box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12); } + +.mdl-button--raised[disabled][disabled], .mdl-button--raised.mdl-button--disabled.mdl-button--disabled { + background-color: rgba(0,0,0, 0.12); + color: rgba(0,0,0, 0.26); + box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12); } + +.mdl-button--colored[disabled][disabled], .mdl-button--colored.mdl-button--disabled.mdl-button--disabled { + color: rgba(0,0,0, 0.26); } + +.mdl-button .material-icons { + vertical-align: middle; } + +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/*------------------------------------* $CONTENTS +\*------------------------------------*/ +/** + * STYLE GUIDE VARIABLES------------------Declarations of Sass variables + * -----Typography + * -----Colors + * -----Textfield + * -----Switch + * -----Spinner + * -----Radio + * -----Menu + * -----List + * -----Layout + * -----Icon toggles + * -----Footer + * -----Column + * -----Checkbox + * -----Card + * -----Button + * -----Animation + * -----Progress + * -----Badge + * -----Shadows + * -----Grid + * -----Data table + */ +/* ========== TYPOGRAPHY ========== */ +/* We're splitting fonts into "preferred" and "performance" in order to optimize + page loading. For important text, such as the body, we want it to load + immediately and not wait for the web font load, whereas for other sections, + such as headers and titles, we're OK with things taking a bit longer to load. + We do have some optional classes and parameters in the mixins, in case you + definitely want to make sure you're using the preferred font and don't mind + the performance hit. + We should be able to improve on this once CSS Font Loading L3 becomes more + widely available. +*/ +/* ========== COLORS ========== */ +/** +* +* Material design color palettes. +* @see http://www.google.com/design/spec/style/color.html +* +**/ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* ========== Color Palettes ========== */ +/* colors.scss */ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* ========== IMAGES ========== */ +/* ========== Color & Themes ========== */ +/* ========== Typography ========== */ +/* ========== Components ========== */ +/* ========== Standard Buttons ========== */ +/* ========== Icon Toggles ========== */ +/* ========== Radio Buttons ========== */ +/* ========== Ripple effect ========== */ +/* ========== Layout ========== */ +/* ========== Content Tabs ========== */ +/* ========== Checkboxes ========== */ +/* ========== Switches ========== */ +/* ========== Spinner ========== */ +/* ========== Text fields ========== */ +/* ========== Card ========== */ +/* ========== Sliders ========== */ +/* ========== Progress ========== */ +/* ========== List ========== */ +/* ========== Item ========== */ +/* ========== Dropdown menu ========== */ +/* ========== Tooltips ========== */ +/* ========== Footer ========== */ +/* TEXTFIELD */ +/* SWITCH */ +/* SPINNER */ +/* RADIO */ +/* MENU */ +/* LIST */ +/* LAYOUT */ +/* ICON TOGGLE */ +/* FOOTER */ +/*mega-footer*/ +/*mini-footer*/ +/* CHECKBOX */ +/* CARD */ +/* Card dimensions */ +/* Cover image */ +/* BUTTON */ +/** + * + * Dimensions + * + */ +/* ANIMATION */ +/* PROGRESS */ +/* BADGE */ +/* SHADOWS */ +/* GRID */ +/* DATA TABLE */ +/* TOOLTIP */ +.mdl-card { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-box-orient: vertical; + -webkit-box-direction: normal; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + font-size: 16px; + font-weight: 400; + min-height: 200px; + overflow: hidden; + width: 330px; + z-index: 1; + position: relative; + background: rgb(255,255,255); + border-radius: 2px; + box-sizing: border-box; } + +.mdl-card__media { + background-color: rgb(255,64,129); + background-repeat: repeat; + background-position: 50% 50%; + background-size: cover; + background-origin: padding-box; + background-attachment: scroll; + box-sizing: border-box; } + +.mdl-card__title { + -webkit-box-align: center; + -webkit-align-items: center; + -ms-flex-align: center; + align-items: center; + color: rgb(0,0,0); + display: block; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-box-pack: stretch; + -webkit-justify-content: stretch; + -ms-flex-pack: stretch; + justify-content: stretch; + line-height: normal; + padding: 16px 16px; + -webkit-perspective-origin: 165px 56px; + perspective-origin: 165px 56px; + -webkit-transform-origin: 165px 56px; + -ms-transform-origin: 165px 56px; + transform-origin: 165px 56px; + box-sizing: border-box; } + .mdl-card__title.mdl-card--border { + border-bottom: 1px solid rgba(0, 0, 0, 0.1); } + +.mdl-card__title-text { + -webkit-align-self: flex-end; + -ms-flex-item-align: end; + align-self: flex-end; + color: inherit; + display: block; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + font-size: 24px; + font-weight: 300; + line-height: normal; + overflow: hidden; + -webkit-transform-origin: 149px 48px; + -ms-transform-origin: 149px 48px; + transform-origin: 149px 48px; + margin: 0; } + +.mdl-card__subtitle-text { + font-size: 14px; + color: rgba(0,0,0, 0.54); + margin: 0; } + +.mdl-card__supporting-text { + color: rgba(0,0,0, 0.54); + font-size: 13px; + line-height: 18px; + overflow: hidden; + padding: 16px 16px; + width: 90%; } + +.mdl-card__actions { + font-size: 16px; + line-height: normal; + width: 100%; + background-color: transparent; + padding: 8px; + box-sizing: border-box; } + .mdl-card__actions.mdl-card--border { + border-top: 1px solid rgba(0, 0, 0, 0.1); } + +.mdl-card--expand { + -webkit-box-flex: 1; + -webkit-flex-grow: 1; + -ms-flex-positive: 1; + flex-grow: 1; } + +.mdl-card__menu { + position: absolute; + right: 16px; + top: 16px; } + +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/*------------------------------------* $CONTENTS +\*------------------------------------*/ +/** + * STYLE GUIDE VARIABLES------------------Declarations of Sass variables + * -----Typography + * -----Colors + * -----Textfield + * -----Switch + * -----Spinner + * -----Radio + * -----Menu + * -----List + * -----Layout + * -----Icon toggles + * -----Footer + * -----Column + * -----Checkbox + * -----Card + * -----Button + * -----Animation + * -----Progress + * -----Badge + * -----Shadows + * -----Grid + * -----Data table + */ +/* ========== TYPOGRAPHY ========== */ +/* We're splitting fonts into "preferred" and "performance" in order to optimize + page loading. For important text, such as the body, we want it to load + immediately and not wait for the web font load, whereas for other sections, + such as headers and titles, we're OK with things taking a bit longer to load. + We do have some optional classes and parameters in the mixins, in case you + definitely want to make sure you're using the preferred font and don't mind + the performance hit. + We should be able to improve on this once CSS Font Loading L3 becomes more + widely available. +*/ +/* ========== COLORS ========== */ +/** +* +* Material design color palettes. +* @see http://www.google.com/design/spec/style/color.html +* +**/ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* ========== Color Palettes ========== */ +/* colors.scss */ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* ========== IMAGES ========== */ +/* ========== Color & Themes ========== */ +/* ========== Typography ========== */ +/* ========== Components ========== */ +/* ========== Standard Buttons ========== */ +/* ========== Icon Toggles ========== */ +/* ========== Radio Buttons ========== */ +/* ========== Ripple effect ========== */ +/* ========== Layout ========== */ +/* ========== Content Tabs ========== */ +/* ========== Checkboxes ========== */ +/* ========== Switches ========== */ +/* ========== Spinner ========== */ +/* ========== Text fields ========== */ +/* ========== Card ========== */ +/* ========== Sliders ========== */ +/* ========== Progress ========== */ +/* ========== List ========== */ +/* ========== Item ========== */ +/* ========== Dropdown menu ========== */ +/* ========== Tooltips ========== */ +/* ========== Footer ========== */ +/* TEXTFIELD */ +/* SWITCH */ +/* SPINNER */ +/* RADIO */ +/* MENU */ +/* LIST */ +/* LAYOUT */ +/* ICON TOGGLE */ +/* FOOTER */ +/*mega-footer*/ +/*mini-footer*/ +/* CHECKBOX */ +/* CARD */ +/* Card dimensions */ +/* Cover image */ +/* BUTTON */ +/** + * + * Dimensions + * + */ +/* ANIMATION */ +/* PROGRESS */ +/* BADGE */ +/* SHADOWS */ +/* GRID */ +/* DATA TABLE */ +/* TOOLTIP */ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* Typography */ +/* Shadows */ +/* Animations */ +.mdl-checkbox { + position: relative; + z-index: 1; + vertical-align: middle; + display: inline-block; + box-sizing: border-box; + width: 100%; + height: 24px; + margin: 0; + padding: 0; } + .mdl-checkbox.is-upgraded { + padding-left: 24px; } + +.mdl-checkbox__input { + line-height: 24px; } + .mdl-checkbox.is-upgraded .mdl-checkbox__input { + position: absolute; + width: 0; + height: 0; + margin: 0; + padding: 0; + opacity: 0; + -ms-appearance: none; + -moz-appearance: none; + -webkit-appearance: none; + appearance: none; + border: none; } + +.mdl-checkbox__box-outline { + position: absolute; + top: 3px; + left: 0; + display: inline-block; + box-sizing: border-box; + width: 16px; + height: 16px; + margin: 0; + cursor: pointer; + overflow: hidden; + border: 2px solid rgba(0,0,0, 0.54); + border-radius: 2px; + z-index: 2; } + .mdl-checkbox.is-checked .mdl-checkbox__box-outline { + border: 2px solid rgb(63,81,181); } + .mdl-checkbox.is-disabled .mdl-checkbox__box-outline { + border: 2px solid rgba(0,0,0, 0.26); + cursor: auto; } + +.mdl-checkbox__focus-helper { + position: absolute; + top: 3px; + left: 0; + display: inline-block; + box-sizing: border-box; + width: 16px; + height: 16px; + border-radius: 50%; + background-color: transparent; } + .mdl-checkbox.is-focused .mdl-checkbox__focus-helper { + box-shadow: 0 0 0px 8px rgba(0, 0, 0, 0.1); + background-color: rgba(0, 0, 0, 0.1); } + .mdl-checkbox.is-focused.is-checked .mdl-checkbox__focus-helper { + box-shadow: 0 0 0px 8px rgba(63,81,181, 0.26); + background-color: rgba(63,81,181, 0.26); } + +.mdl-checkbox__tick-outline { + position: absolute; + top: 0; + left: 0; + height: 100%; + width: 100%; + -webkit-mask: url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcKICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICB4bWxuczpjYz0iaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbnMjIgogICB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiCiAgIHhtbG5zOnN2Zz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgdmVyc2lvbj0iMS4xIgogICB2aWV3Qm94PSIwIDAgMSAxIgogICBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWluWU1pbiBtZWV0Ij4KICA8ZGVmcz4KICAgIDxjbGlwUGF0aCBpZD0iY2xpcCI+CiAgICAgIDxwYXRoCiAgICAgICAgIGQ9Ik0gMCwwIDAsMSAxLDEgMSwwIDAsMCB6IE0gMC44NTM0Mzc1LDAuMTY3MTg3NSAwLjk1OTY4NzUsMC4yNzMxMjUgMC40MjkzNzUsMC44MDM0Mzc1IDAuMzIzMTI1LDAuOTA5Njg3NSAwLjIxNzE4NzUsMC44MDM0Mzc1IDAuMDQwMzEyNSwwLjYyNjg3NSAwLjE0NjU2MjUsMC41MjA2MjUgMC4zMjMxMjUsMC42OTc1IDAuODUzNDM3NSwwLjE2NzE4NzUgeiIKICAgICAgICAgc3R5bGU9ImZpbGw6I2ZmZmZmZjtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZSIgLz4KICAgIDwvY2xpcFBhdGg+CiAgICA8bWFzayBpZD0ibWFzayIgbWFza1VuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgbWFza0NvbnRlbnRVbml0cz0ib2JqZWN0Qm91bmRpbmdCb3giPgogICAgICA8cGF0aAogICAgICAgICBkPSJNIDAsMCAwLDEgMSwxIDEsMCAwLDAgeiBNIDAuODUzNDM3NSwwLjE2NzE4NzUgMC45NTk2ODc1LDAuMjczMTI1IDAuNDI5Mzc1LDAuODAzNDM3NSAwLjMyMzEyNSwwLjkwOTY4NzUgMC4yMTcxODc1LDAuODAzNDM3NSAwLjA0MDMxMjUsMC42MjY4NzUgMC4xNDY1NjI1LDAuNTIwNjI1IDAuMzIzMTI1LDAuNjk3NSAwLjg1MzQzNzUsMC4xNjcxODc1IHoiCiAgICAgICAgIHN0eWxlPSJmaWxsOiNmZmZmZmY7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmUiIC8+CiAgICA8L21hc2s+CiAgPC9kZWZzPgogIDxyZWN0CiAgICAgd2lkdGg9IjEiCiAgICAgaGVpZ2h0PSIxIgogICAgIHg9IjAiCiAgICAgeT0iMCIKICAgICBjbGlwLXBhdGg9InVybCgjY2xpcCkiCiAgICAgc3R5bGU9ImZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZSIgLz4KPC9zdmc+Cg=="); + mask: url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcKICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICB4bWxuczpjYz0iaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbnMjIgogICB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiCiAgIHhtbG5zOnN2Zz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgdmVyc2lvbj0iMS4xIgogICB2aWV3Qm94PSIwIDAgMSAxIgogICBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWluWU1pbiBtZWV0Ij4KICA8ZGVmcz4KICAgIDxjbGlwUGF0aCBpZD0iY2xpcCI+CiAgICAgIDxwYXRoCiAgICAgICAgIGQ9Ik0gMCwwIDAsMSAxLDEgMSwwIDAsMCB6IE0gMC44NTM0Mzc1LDAuMTY3MTg3NSAwLjk1OTY4NzUsMC4yNzMxMjUgMC40MjkzNzUsMC44MDM0Mzc1IDAuMzIzMTI1LDAuOTA5Njg3NSAwLjIxNzE4NzUsMC44MDM0Mzc1IDAuMDQwMzEyNSwwLjYyNjg3NSAwLjE0NjU2MjUsMC41MjA2MjUgMC4zMjMxMjUsMC42OTc1IDAuODUzNDM3NSwwLjE2NzE4NzUgeiIKICAgICAgICAgc3R5bGU9ImZpbGw6I2ZmZmZmZjtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZSIgLz4KICAgIDwvY2xpcFBhdGg+CiAgICA8bWFzayBpZD0ibWFzayIgbWFza1VuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgbWFza0NvbnRlbnRVbml0cz0ib2JqZWN0Qm91bmRpbmdCb3giPgogICAgICA8cGF0aAogICAgICAgICBkPSJNIDAsMCAwLDEgMSwxIDEsMCAwLDAgeiBNIDAuODUzNDM3NSwwLjE2NzE4NzUgMC45NTk2ODc1LDAuMjczMTI1IDAuNDI5Mzc1LDAuODAzNDM3NSAwLjMyMzEyNSwwLjkwOTY4NzUgMC4yMTcxODc1LDAuODAzNDM3NSAwLjA0MDMxMjUsMC42MjY4NzUgMC4xNDY1NjI1LDAuNTIwNjI1IDAuMzIzMTI1LDAuNjk3NSAwLjg1MzQzNzUsMC4xNjcxODc1IHoiCiAgICAgICAgIHN0eWxlPSJmaWxsOiNmZmZmZmY7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmUiIC8+CiAgICA8L21hc2s+CiAgPC9kZWZzPgogIDxyZWN0CiAgICAgd2lkdGg9IjEiCiAgICAgaGVpZ2h0PSIxIgogICAgIHg9IjAiCiAgICAgeT0iMCIKICAgICBjbGlwLXBhdGg9InVybCgjY2xpcCkiCiAgICAgc3R5bGU9ImZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZSIgLz4KPC9zdmc+Cg=="); + background: transparent; + -webkit-transition-duration: 0.28s; + transition-duration: 0.28s; + -webkit-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + -webkit-transition-property: background; + transition-property: background; } + .mdl-checkbox.is-checked .mdl-checkbox__tick-outline { + background: rgb(63,81,181) url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcKICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICB4bWxuczpjYz0iaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbnMjIgogICB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiCiAgIHhtbG5zOnN2Zz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgdmVyc2lvbj0iMS4xIgogICB2aWV3Qm94PSIwIDAgMSAxIgogICBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWluWU1pbiBtZWV0Ij4KICA8cGF0aAogICAgIGQ9Ik0gMC4wNDAzODA1OSwwLjYyNjc3NjcgMC4xNDY0NDY2MSwwLjUyMDcxMDY4IDAuNDI5Mjg5MzIsMC44MDM1NTMzOSAwLjMyMzIyMzMsMC45MDk2MTk0MSB6IE0gMC4yMTcxNTcyOSwwLjgwMzU1MzM5IDAuODUzNTUzMzksMC4xNjcxNTcyOSAwLjk1OTYxOTQxLDAuMjczMjIzMyAwLjMyMzIyMzMsMC45MDk2MTk0MSB6IgogICAgIGlkPSJyZWN0Mzc4MCIKICAgICBzdHlsZT0iZmlsbDojZmZmZmZmO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lIiAvPgo8L3N2Zz4K"); } + .mdl-checkbox.is-checked.is-disabled .mdl-checkbox__tick-outline { + background: rgba(0,0,0, 0.26) url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcKICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICB4bWxuczpjYz0iaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbnMjIgogICB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiCiAgIHhtbG5zOnN2Zz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgdmVyc2lvbj0iMS4xIgogICB2aWV3Qm94PSIwIDAgMSAxIgogICBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWluWU1pbiBtZWV0Ij4KICA8cGF0aAogICAgIGQ9Ik0gMC4wNDAzODA1OSwwLjYyNjc3NjcgMC4xNDY0NDY2MSwwLjUyMDcxMDY4IDAuNDI5Mjg5MzIsMC44MDM1NTMzOSAwLjMyMzIyMzMsMC45MDk2MTk0MSB6IE0gMC4yMTcxNTcyOSwwLjgwMzU1MzM5IDAuODUzNTUzMzksMC4xNjcxNTcyOSAwLjk1OTYxOTQxLDAuMjczMjIzMyAwLjMyMzIyMzMsMC45MDk2MTk0MSB6IgogICAgIGlkPSJyZWN0Mzc4MCIKICAgICBzdHlsZT0iZmlsbDojZmZmZmZmO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lIiAvPgo8L3N2Zz4K"); } + +.mdl-checkbox__label { + position: relative; + cursor: pointer; + font-size: 16px; + line-height: 24px; + margin: 0; } + .mdl-checkbox.is-disabled .mdl-checkbox__label { + color: rgba(0,0,0, 0.26); + cursor: auto; } + +.mdl-checkbox__ripple-container { + position: absolute; + z-index: 2; + top: -6px; + left: -10px; + box-sizing: border-box; + width: 36px; + height: 36px; + border-radius: 50%; + cursor: pointer; + overflow: hidden; + -webkit-mask-image: -webkit-radial-gradient(circle, white, black); } + .mdl-checkbox__ripple-container .mdl-ripple { + background: rgb(63,81,181); } + .mdl-checkbox.is-disabled .mdl-checkbox__ripple-container { + cursor: auto; } + .mdl-checkbox.is-disabled .mdl-checkbox__ripple-container .mdl-ripple { + background: transparent; } + +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/*------------------------------------* $CONTENTS +\*------------------------------------*/ +/** + * STYLE GUIDE VARIABLES------------------Declarations of Sass variables + * -----Typography + * -----Colors + * -----Textfield + * -----Switch + * -----Spinner + * -----Radio + * -----Menu + * -----List + * -----Layout + * -----Icon toggles + * -----Footer + * -----Column + * -----Checkbox + * -----Card + * -----Button + * -----Animation + * -----Progress + * -----Badge + * -----Shadows + * -----Grid + * -----Data table + */ +/* ========== TYPOGRAPHY ========== */ +/* We're splitting fonts into "preferred" and "performance" in order to optimize + page loading. For important text, such as the body, we want it to load + immediately and not wait for the web font load, whereas for other sections, + such as headers and titles, we're OK with things taking a bit longer to load. + We do have some optional classes and parameters in the mixins, in case you + definitely want to make sure you're using the preferred font and don't mind + the performance hit. + We should be able to improve on this once CSS Font Loading L3 becomes more + widely available. +*/ +/* ========== COLORS ========== */ +/** +* +* Material design color palettes. +* @see http://www.google.com/design/spec/style/color.html +* +**/ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* ========== Color Palettes ========== */ +/* colors.scss */ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* ========== IMAGES ========== */ +/* ========== Color & Themes ========== */ +/* ========== Typography ========== */ +/* ========== Components ========== */ +/* ========== Standard Buttons ========== */ +/* ========== Icon Toggles ========== */ +/* ========== Radio Buttons ========== */ +/* ========== Ripple effect ========== */ +/* ========== Layout ========== */ +/* ========== Content Tabs ========== */ +/* ========== Checkboxes ========== */ +/* ========== Switches ========== */ +/* ========== Spinner ========== */ +/* ========== Text fields ========== */ +/* ========== Card ========== */ +/* ========== Sliders ========== */ +/* ========== Progress ========== */ +/* ========== List ========== */ +/* ========== Item ========== */ +/* ========== Dropdown menu ========== */ +/* ========== Tooltips ========== */ +/* ========== Footer ========== */ +/* TEXTFIELD */ +/* SWITCH */ +/* SPINNER */ +/* RADIO */ +/* MENU */ +/* LIST */ +/* LAYOUT */ +/* ICON TOGGLE */ +/* FOOTER */ +/*mega-footer*/ +/*mini-footer*/ +/* CHECKBOX */ +/* CARD */ +/* Card dimensions */ +/* Cover image */ +/* BUTTON */ +/** + * + * Dimensions + * + */ +/* ANIMATION */ +/* PROGRESS */ +/* BADGE */ +/* SHADOWS */ +/* GRID */ +/* DATA TABLE */ +/* TOOLTIP */ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* Typography */ +/* Shadows */ +/* Animations */ +.mdl-data-table { + position: relative; + border: 1px solid rgba(0, 0, 0, 0.12); + border-collapse: collapse; + white-space: nowrap; + font-size: 13px; + background-color: rgb(255,255,255); } + .mdl-data-table thead { + padding-bottom: 3px; } + .mdl-data-table thead .mdl-data-table__select { + margin-top: 0; } + .mdl-data-table tbody tr { + position: relative; + height: 48px; + -webkit-transition-duration: 0.28s; + transition-duration: 0.28s; + -webkit-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + -webkit-transition-property: background-color; + transition-property: background-color; } + .mdl-data-table tbody tr.is-selected { + background-color: #e0e0e0; } + .mdl-data-table tbody tr:hover { + background-color: #eeeeee; } + .mdl-data-table td, .mdl-data-table th { + padding: 0 18px 0 18px; + text-align: right; } + .mdl-data-table td:first-of-type, .mdl-data-table th:first-of-type { + padding-left: 24px; } + .mdl-data-table td:last-of-type, .mdl-data-table th:last-of-type { + padding-right: 24px; } + .mdl-data-table td { + position: relative; + vertical-align: top; + height: 48px; + border-top: 1px solid rgba(0, 0, 0, 0.12); + border-bottom: 1px solid rgba(0, 0, 0, 0.12); + padding-top: 12px; + box-sizing: border-box; } + .mdl-data-table td .mdl-data-table__select { + vertical-align: top; + position: absolute; + left: 24px; } + .mdl-data-table th { + position: relative; + vertical-align: bottom; + text-overflow: ellipsis; + font-size: 14px; + font-weight: bold; + line-height: 24px; + letter-spacing: 0; + height: 48px; + font-size: 12px; + color: rgba(0, 0, 0, 0.54); + padding-bottom: 8px; + box-sizing: border-box; } + .mdl-data-table th .mdl-data-table__select { + position: absolute; + bottom: 8px; + left: 24px; } + +.mdl-data-table__select { + width: 16px; } + +.mdl-data-table__cell--non-numeric.mdl-data-table__cell--non-numeric { + text-align: left; } + +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/*------------------------------------* $CONTENTS +\*------------------------------------*/ +/** + * STYLE GUIDE VARIABLES------------------Declarations of Sass variables + * -----Typography + * -----Colors + * -----Textfield + * -----Switch + * -----Spinner + * -----Radio + * -----Menu + * -----List + * -----Layout + * -----Icon toggles + * -----Footer + * -----Column + * -----Checkbox + * -----Card + * -----Button + * -----Animation + * -----Progress + * -----Badge + * -----Shadows + * -----Grid + * -----Data table + */ +/* ========== TYPOGRAPHY ========== */ +/* We're splitting fonts into "preferred" and "performance" in order to optimize + page loading. For important text, such as the body, we want it to load + immediately and not wait for the web font load, whereas for other sections, + such as headers and titles, we're OK with things taking a bit longer to load. + We do have some optional classes and parameters in the mixins, in case you + definitely want to make sure you're using the preferred font and don't mind + the performance hit. + We should be able to improve on this once CSS Font Loading L3 becomes more + widely available. +*/ +/* ========== COLORS ========== */ +/** +* +* Material design color palettes. +* @see http://www.google.com/design/spec/style/color.html +* +**/ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* ========== Color Palettes ========== */ +/* colors.scss */ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* ========== IMAGES ========== */ +/* ========== Color & Themes ========== */ +/* ========== Typography ========== */ +/* ========== Components ========== */ +/* ========== Standard Buttons ========== */ +/* ========== Icon Toggles ========== */ +/* ========== Radio Buttons ========== */ +/* ========== Ripple effect ========== */ +/* ========== Layout ========== */ +/* ========== Content Tabs ========== */ +/* ========== Checkboxes ========== */ +/* ========== Switches ========== */ +/* ========== Spinner ========== */ +/* ========== Text fields ========== */ +/* ========== Card ========== */ +/* ========== Sliders ========== */ +/* ========== Progress ========== */ +/* ========== List ========== */ +/* ========== Item ========== */ +/* ========== Dropdown menu ========== */ +/* ========== Tooltips ========== */ +/* ========== Footer ========== */ +/* TEXTFIELD */ +/* SWITCH */ +/* SPINNER */ +/* RADIO */ +/* MENU */ +/* LIST */ +/* LAYOUT */ +/* ICON TOGGLE */ +/* FOOTER */ +/*mega-footer*/ +/*mini-footer*/ +/* CHECKBOX */ +/* CARD */ +/* Card dimensions */ +/* Cover image */ +/* BUTTON */ +/** + * + * Dimensions + * + */ +/* ANIMATION */ +/* PROGRESS */ +/* BADGE */ +/* SHADOWS */ +/* GRID */ +/* DATA TABLE */ +/* TOOLTIP */ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* Typography */ +/* Shadows */ +/* Animations */ +.mdl-mega-footer { + padding: 16px 40px; + color: rgb(158,158,158); + background-color: rgb(66,66,66); } + +.mdl-mega-footer--top-section:after, +.mdl-mega-footer--middle-section:after, +.mdl-mega-footer--bottom-section:after, +.mdl-mega-footer__top-section:after, +.mdl-mega-footer__middle-section:after, +.mdl-mega-footer__bottom-section:after { + content: ''; + display: block; + clear: both; } + +.mdl-mega-footer--left-section, +.mdl-mega-footer__left-section { + margin-bottom: 16px; } + +.mdl-mega-footer--right-section, +.mdl-mega-footer__right-section { + margin-bottom: 16px; } + +.mdl-mega-footer--right-section a, +.mdl-mega-footer__right-section a { + display: block; + margin-bottom: 16px; + color: inherit; + text-decoration: none; } + +@media screen and (min-width: 760px) { + .mdl-mega-footer--left-section, + .mdl-mega-footer__left-section { + float: left; } + .mdl-mega-footer--right-section, + .mdl-mega-footer__right-section { + float: right; } + .mdl-mega-footer--right-section a, + .mdl-mega-footer__right-section a { + display: inline-block; + margin-left: 16px; + line-height: 36px; + vertical-align: middle; } } + +.mdl-mega-footer--social-btn, +.mdl-mega-footer__social-btn { + width: 36px; + height: 36px; + padding: 0; + margin: 0; + background-color: rgb(158,158,158); + border: none; } + +.mdl-mega-footer--drop-down-section, +.mdl-mega-footer__drop-down-section { + display: block; + position: relative; } + +@media screen and (min-width: 760px) { + .mdl-mega-footer--drop-down-section, + .mdl-mega-footer__drop-down-section { + width: 33%; } + .mdl-mega-footer--drop-down-section:nth-child(1), + .mdl-mega-footer--drop-down-section:nth-child(2), + .mdl-mega-footer__drop-down-section:nth-child(1), + .mdl-mega-footer__drop-down-section:nth-child(2) { + float: left; } + .mdl-mega-footer--drop-down-section:nth-child(3), + .mdl-mega-footer__drop-down-section:nth-child(3) { + float: right; } + .mdl-mega-footer--drop-down-section:nth-child(3):after, + .mdl-mega-footer__drop-down-section:nth-child(3):after { + clear: right; } + .mdl-mega-footer--drop-down-section:nth-child(4), + .mdl-mega-footer__drop-down-section:nth-child(4) { + clear: right; + float: right; } + .mdl-mega-footer--middle-section:after, + .mdl-mega-footer__middle-section:after { + content: ''; + display: block; + clear: both; } + .mdl-mega-footer--bottom-section, + .mdl-mega-footer__bottom-section { + padding-top: 0; } } + +@media screen and (min-width: 1024px) { + .mdl-mega-footer--drop-down-section, + .mdl-mega-footer--drop-down-section:nth-child(3), + .mdl-mega-footer--drop-down-section:nth-child(4), + .mdl-mega-footer__drop-down-section, + .mdl-mega-footer__drop-down-section:nth-child(3), + .mdl-mega-footer__drop-down-section:nth-child(4) { + width: 24%; + float: left; } } + +.mdl-mega-footer--heading-checkbox, +.mdl-mega-footer__heading-checkbox { + position: absolute; + width: 100%; + height: 55.8px; + padding: 32px; + margin: 0; + margin-top: -16px; + cursor: pointer; + z-index: 1; + opacity: 0; } + .mdl-mega-footer--heading-checkbox + .mdl-mega-footer--heading:after, + .mdl-mega-footer--heading-checkbox + .mdl-mega-footer__heading:after, + .mdl-mega-footer__heading-checkbox + .mdl-mega-footer--heading:after, + .mdl-mega-footer__heading-checkbox + .mdl-mega-footer__heading:after { + font-family: 'Material Icons'; + content: '\E5CE'; } + +.mdl-mega-footer--heading-checkbox:checked ~ .mdl-mega-footer--link-list, +.mdl-mega-footer--heading-checkbox:checked ~ .mdl-mega-footer__link-list, +.mdl-mega-footer--heading-checkbox:checked + .mdl-mega-footer--heading + .mdl-mega-footer--link-list, +.mdl-mega-footer--heading-checkbox:checked + .mdl-mega-footer__heading + .mdl-mega-footer__link-list, +.mdl-mega-footer__heading-checkbox:checked ~ .mdl-mega-footer--link-list, +.mdl-mega-footer__heading-checkbox:checked ~ .mdl-mega-footer__link-list, +.mdl-mega-footer__heading-checkbox:checked + .mdl-mega-footer--heading + .mdl-mega-footer--link-list, +.mdl-mega-footer__heading-checkbox:checked + .mdl-mega-footer__heading + .mdl-mega-footer__link-list { + display: none; } + +.mdl-mega-footer--heading-checkbox:checked + .mdl-mega-footer--heading:after, +.mdl-mega-footer--heading-checkbox:checked + .mdl-mega-footer__heading:after, +.mdl-mega-footer__heading-checkbox:checked + .mdl-mega-footer--heading:after, +.mdl-mega-footer__heading-checkbox:checked + .mdl-mega-footer__heading:after { + font-family: 'Material Icons'; + content: '\E5CF'; } + +.mdl-mega-footer--heading, +.mdl-mega-footer__heading { + position: relative; + width: 100%; + padding-right: 39.8px; + margin-bottom: 16px; + box-sizing: border-box; + font-size: 14px; + line-height: 23.8px; + font-weight: 500; + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; + color: rgb(224,224,224); } + +.mdl-mega-footer--heading:after, +.mdl-mega-footer__heading:after { + content: ''; + position: absolute; + top: 0; + right: 0; + display: block; + width: 23.8px; + height: 23.8px; + background-size: cover; } + +.mdl-mega-footer--link-list, +.mdl-mega-footer__link-list { + list-style: none; + margin: 0; + padding: 0; + margin-bottom: 32px; } + .mdl-mega-footer--link-list:after, + .mdl-mega-footer__link-list:after { + clear: both; + display: block; + content: ''; } + +.mdl-mega-footer--link-list li, +.mdl-mega-footer__link-list li { + font-size: 14px; + font-weight: 400; + line-height: 24px; + letter-spacing: 0; + line-height: 20px; } + +.mdl-mega-footer--link-list a, +.mdl-mega-footer__link-list a { + color: inherit; + text-decoration: none; + white-space: nowrap; } + +@media screen and (min-width: 760px) { + .mdl-mega-footer--heading-checkbox, + .mdl-mega-footer__heading-checkbox { + display: none; } + .mdl-mega-footer--heading-checkbox + .mdl-mega-footer--heading:after, + .mdl-mega-footer--heading-checkbox + .mdl-mega-footer__heading:after, + .mdl-mega-footer__heading-checkbox + .mdl-mega-footer--heading:after, + .mdl-mega-footer__heading-checkbox + .mdl-mega-footer__heading:after { + background-image: none; } + .mdl-mega-footer--heading-checkbox:checked ~ .mdl-mega-footer--link-list, + .mdl-mega-footer--heading-checkbox:checked ~ .mdl-mega-footer__link-list, + .mdl-mega-footer--heading-checkbox:checked + .mdl-mega-footer__heading + .mdl-mega-footer__link-list, + .mdl-mega-footer--heading-checkbox:checked + .mdl-mega-footer--heading + .mdl-mega-footer--link-list, + .mdl-mega-footer__heading-checkbox:checked ~ .mdl-mega-footer--link-list, + .mdl-mega-footer__heading-checkbox:checked ~ .mdl-mega-footer__link-list, + .mdl-mega-footer__heading-checkbox:checked + .mdl-mega-footer__heading + .mdl-mega-footer__link-list, + .mdl-mega-footer__heading-checkbox:checked + .mdl-mega-footer--heading + .mdl-mega-footer--link-list { + display: block; } + .mdl-mega-footer--heading-checkbox:checked + .mdl-mega-footer--heading:after, + .mdl-mega-footer--heading-checkbox:checked + .mdl-mega-footer__heading:after, + .mdl-mega-footer__heading-checkbox:checked + .mdl-mega-footer--heading:after, + .mdl-mega-footer__heading-checkbox:checked + .mdl-mega-footer__heading:after { + content: ''; } } + +.mdl-mega-footer--bottom-section, +.mdl-mega-footer__bottom-section { + padding-top: 16px; + margin-bottom: 16px; } + +.mdl-logo { + margin-bottom: 16px; + color: white; } + +.mdl-mega-footer--bottom-section .mdl-mega-footer--link-list li, +.mdl-mega-footer__bottom-section .mdl-mega-footer__link-list li { + float: left; + margin-bottom: 0; + margin-right: 16px; } + +@media screen and (min-width: 760px) { + .mdl-logo { + float: left; + margin-bottom: 0; + margin-right: 16px; } } + +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/*------------------------------------* $CONTENTS +\*------------------------------------*/ +/** + * STYLE GUIDE VARIABLES------------------Declarations of Sass variables + * -----Typography + * -----Colors + * -----Textfield + * -----Switch + * -----Spinner + * -----Radio + * -----Menu + * -----List + * -----Layout + * -----Icon toggles + * -----Footer + * -----Column + * -----Checkbox + * -----Card + * -----Button + * -----Animation + * -----Progress + * -----Badge + * -----Shadows + * -----Grid + * -----Data table + */ +/* ========== TYPOGRAPHY ========== */ +/* We're splitting fonts into "preferred" and "performance" in order to optimize + page loading. For important text, such as the body, we want it to load + immediately and not wait for the web font load, whereas for other sections, + such as headers and titles, we're OK with things taking a bit longer to load. + We do have some optional classes and parameters in the mixins, in case you + definitely want to make sure you're using the preferred font and don't mind + the performance hit. + We should be able to improve on this once CSS Font Loading L3 becomes more + widely available. +*/ +/* ========== COLORS ========== */ +/** +* +* Material design color palettes. +* @see http://www.google.com/design/spec/style/color.html +* +**/ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* ========== Color Palettes ========== */ +/* colors.scss */ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* ========== IMAGES ========== */ +/* ========== Color & Themes ========== */ +/* ========== Typography ========== */ +/* ========== Components ========== */ +/* ========== Standard Buttons ========== */ +/* ========== Icon Toggles ========== */ +/* ========== Radio Buttons ========== */ +/* ========== Ripple effect ========== */ +/* ========== Layout ========== */ +/* ========== Content Tabs ========== */ +/* ========== Checkboxes ========== */ +/* ========== Switches ========== */ +/* ========== Spinner ========== */ +/* ========== Text fields ========== */ +/* ========== Card ========== */ +/* ========== Sliders ========== */ +/* ========== Progress ========== */ +/* ========== List ========== */ +/* ========== Item ========== */ +/* ========== Dropdown menu ========== */ +/* ========== Tooltips ========== */ +/* ========== Footer ========== */ +/* TEXTFIELD */ +/* SWITCH */ +/* SPINNER */ +/* RADIO */ +/* MENU */ +/* LIST */ +/* LAYOUT */ +/* ICON TOGGLE */ +/* FOOTER */ +/*mega-footer*/ +/*mini-footer*/ +/* CHECKBOX */ +/* CARD */ +/* Card dimensions */ +/* Cover image */ +/* BUTTON */ +/** + * + * Dimensions + * + */ +/* ANIMATION */ +/* PROGRESS */ +/* BADGE */ +/* SHADOWS */ +/* GRID */ +/* DATA TABLE */ +/* TOOLTIP */ +.mdl-mini-footer { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-flow: row wrap; + -ms-flex-flow: row wrap; + flex-flow: row wrap; + -webkit-box-pack: justify; + -webkit-justify-content: space-between; + -ms-flex-pack: justify; + justify-content: space-between; + padding: 32px 16px; + color: rgb(158,158,158); + background-color: rgb(66,66,66); } + .mdl-mini-footer:after { + content: ''; + display: block; } + .mdl-mini-footer .mdl-logo { + line-height: 36px; } + +.mdl-mini-footer--link-list, +.mdl-mini-footer__link-list { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-flow: row nowrap; + -ms-flex-flow: row nowrap; + flex-flow: row nowrap; + list-style: none; + margin: 0; + padding: 0; } + .mdl-mini-footer--link-list li, + .mdl-mini-footer__link-list li { + margin-bottom: 0; + margin-right: 16px; } + @media screen and (min-width: 760px) { + .mdl-mini-footer--link-list li, + .mdl-mini-footer__link-list li { + line-height: 36px; } } + .mdl-mini-footer--link-list a, + .mdl-mini-footer__link-list a { + color: inherit; + text-decoration: none; + white-space: nowrap; } + +.mdl-mini-footer--left-section, +.mdl-mini-footer__left-section { + display: inline-block; + -webkit-box-ordinal-group: 1; + -webkit-order: 0; + -ms-flex-order: 0; + order: 0; } + +.mdl-mini-footer--right-section, +.mdl-mini-footer__right-section { + display: inline-block; + -webkit-box-ordinal-group: 2; + -webkit-order: 1; + -ms-flex-order: 1; + order: 1; } + +.mdl-mini-footer--social-btn, +.mdl-mini-footer__social-btn { + width: 36px; + height: 36px; + padding: 0; + margin: 0; + background-color: rgb(158,158,158); + border: none; } + +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/*------------------------------------* $CONTENTS +\*------------------------------------*/ +/** + * STYLE GUIDE VARIABLES------------------Declarations of Sass variables + * -----Typography + * -----Colors + * -----Textfield + * -----Switch + * -----Spinner + * -----Radio + * -----Menu + * -----List + * -----Layout + * -----Icon toggles + * -----Footer + * -----Column + * -----Checkbox + * -----Card + * -----Button + * -----Animation + * -----Progress + * -----Badge + * -----Shadows + * -----Grid + * -----Data table + */ +/* ========== TYPOGRAPHY ========== */ +/* We're splitting fonts into "preferred" and "performance" in order to optimize + page loading. For important text, such as the body, we want it to load + immediately and not wait for the web font load, whereas for other sections, + such as headers and titles, we're OK with things taking a bit longer to load. + We do have some optional classes and parameters in the mixins, in case you + definitely want to make sure you're using the preferred font and don't mind + the performance hit. + We should be able to improve on this once CSS Font Loading L3 becomes more + widely available. +*/ +/* ========== COLORS ========== */ +/** +* +* Material design color palettes. +* @see http://www.google.com/design/spec/style/color.html +* +**/ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* ========== Color Palettes ========== */ +/* colors.scss */ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* ========== IMAGES ========== */ +/* ========== Color & Themes ========== */ +/* ========== Typography ========== */ +/* ========== Components ========== */ +/* ========== Standard Buttons ========== */ +/* ========== Icon Toggles ========== */ +/* ========== Radio Buttons ========== */ +/* ========== Ripple effect ========== */ +/* ========== Layout ========== */ +/* ========== Content Tabs ========== */ +/* ========== Checkboxes ========== */ +/* ========== Switches ========== */ +/* ========== Spinner ========== */ +/* ========== Text fields ========== */ +/* ========== Card ========== */ +/* ========== Sliders ========== */ +/* ========== Progress ========== */ +/* ========== List ========== */ +/* ========== Item ========== */ +/* ========== Dropdown menu ========== */ +/* ========== Tooltips ========== */ +/* ========== Footer ========== */ +/* TEXTFIELD */ +/* SWITCH */ +/* SPINNER */ +/* RADIO */ +/* MENU */ +/* LIST */ +/* LAYOUT */ +/* ICON TOGGLE */ +/* FOOTER */ +/*mega-footer*/ +/*mini-footer*/ +/* CHECKBOX */ +/* CARD */ +/* Card dimensions */ +/* Cover image */ +/* BUTTON */ +/** + * + * Dimensions + * + */ +/* ANIMATION */ +/* PROGRESS */ +/* BADGE */ +/* SHADOWS */ +/* GRID */ +/* DATA TABLE */ +/* TOOLTIP */ +.mdl-icon-toggle { + position: relative; + z-index: 1; + vertical-align: middle; + display: inline-block; + height: 32px; + margin: 0; + padding: 0; } + +.mdl-icon-toggle__input { + line-height: 32px; } + .mdl-icon-toggle.is-upgraded .mdl-icon-toggle__input { + position: absolute; + width: 0; + height: 0; + margin: 0; + padding: 0; + opacity: 0; + -ms-appearance: none; + -moz-appearance: none; + -webkit-appearance: none; + appearance: none; + border: none; } + +.mdl-icon-toggle__label { + display: inline-block; + position: relative; + cursor: pointer; + height: 32px; + width: 32px; + min-width: 32px; + color: rgb(97,97,97); + border-radius: 50%; + padding: 0; + margin-left: 0; + margin-right: 0; + text-align: center; + background-color: transparent; + will-change: background-color; + -webkit-transition: background-color 0.2s cubic-bezier(0.4, 0, 0.2, 1), color 0.2s cubic-bezier(0.4, 0, 0.2, 1); + transition: background-color 0.2s cubic-bezier(0.4, 0, 0.2, 1), color 0.2s cubic-bezier(0.4, 0, 0.2, 1); } + .mdl-icon-toggle__label.material-icons { + line-height: 32px; + font-size: 24px; } + .mdl-icon-toggle.is-checked .mdl-icon-toggle__label { + color: rgb(63,81,181); } + .mdl-icon-toggle.is-disabled .mdl-icon-toggle__label { + color: rgba(0,0,0, 0.26); + cursor: auto; + -webkit-transition: none; + transition: none; } + .mdl-icon-toggle.is-focused .mdl-icon-toggle__label { + background-color: rgba(0,0,0, 0.12); } + .mdl-icon-toggle.is-focused.is-checked .mdl-icon-toggle__label { + background-color: rgba(63,81,181, 0.26); } + +.mdl-icon-toggle__ripple-container { + position: absolute; + z-index: 2; + top: -2px; + left: -2px; + box-sizing: border-box; + width: 36px; + height: 36px; + border-radius: 50%; + cursor: pointer; + overflow: hidden; + -webkit-mask-image: -webkit-radial-gradient(circle, white, black); } + .mdl-icon-toggle__ripple-container .mdl-ripple { + background: rgb(97,97,97); } + .mdl-icon-toggle.is-disabled .mdl-icon-toggle__ripple-container { + cursor: auto; } + .mdl-icon-toggle.is-disabled .mdl-icon-toggle__ripple-container .mdl-ripple { + background: transparent; } + +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/*------------------------------------* $CONTENTS +\*------------------------------------*/ +/** + * STYLE GUIDE VARIABLES------------------Declarations of Sass variables + * -----Typography + * -----Colors + * -----Textfield + * -----Switch + * -----Spinner + * -----Radio + * -----Menu + * -----List + * -----Layout + * -----Icon toggles + * -----Footer + * -----Column + * -----Checkbox + * -----Card + * -----Button + * -----Animation + * -----Progress + * -----Badge + * -----Shadows + * -----Grid + * -----Data table + */ +/* ========== TYPOGRAPHY ========== */ +/* We're splitting fonts into "preferred" and "performance" in order to optimize + page loading. For important text, such as the body, we want it to load + immediately and not wait for the web font load, whereas for other sections, + such as headers and titles, we're OK with things taking a bit longer to load. + We do have some optional classes and parameters in the mixins, in case you + definitely want to make sure you're using the preferred font and don't mind + the performance hit. + We should be able to improve on this once CSS Font Loading L3 becomes more + widely available. +*/ +/* ========== COLORS ========== */ +/** +* +* Material design color palettes. +* @see http://www.google.com/design/spec/style/color.html +* +**/ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* ========== Color Palettes ========== */ +/* colors.scss */ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* ========== IMAGES ========== */ +/* ========== Color & Themes ========== */ +/* ========== Typography ========== */ +/* ========== Components ========== */ +/* ========== Standard Buttons ========== */ +/* ========== Icon Toggles ========== */ +/* ========== Radio Buttons ========== */ +/* ========== Ripple effect ========== */ +/* ========== Layout ========== */ +/* ========== Content Tabs ========== */ +/* ========== Checkboxes ========== */ +/* ========== Switches ========== */ +/* ========== Spinner ========== */ +/* ========== Text fields ========== */ +/* ========== Card ========== */ +/* ========== Sliders ========== */ +/* ========== Progress ========== */ +/* ========== List ========== */ +/* ========== Item ========== */ +/* ========== Dropdown menu ========== */ +/* ========== Tooltips ========== */ +/* ========== Footer ========== */ +/* TEXTFIELD */ +/* SWITCH */ +/* SPINNER */ +/* RADIO */ +/* MENU */ +/* LIST */ +/* LAYOUT */ +/* ICON TOGGLE */ +/* FOOTER */ +/*mega-footer*/ +/*mini-footer*/ +/* CHECKBOX */ +/* CARD */ +/* Card dimensions */ +/* Cover image */ +/* BUTTON */ +/** + * + * Dimensions + * + */ +/* ANIMATION */ +/* PROGRESS */ +/* BADGE */ +/* SHADOWS */ +/* GRID */ +/* DATA TABLE */ +/* TOOLTIP */ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* Typography */ +/* Shadows */ +/* Animations */ +.mdl-menu__container { + display: block; + margin: 0; + padding: 0; + border: none; + position: absolute; + overflow: visible; + height: 0; + width: 0; + visibility: hidden; + z-index: -1; } + .mdl-menu__container.is-visible, .mdl-menu__container.is-animating { + z-index: 999; + visibility: visible; } + +.mdl-menu__outline { + display: block; + background: rgb(255,255,255); + margin: 0; + padding: 0; + border: none; + border-radius: 2px; + position: absolute; + top: 0; + left: 0; + overflow: hidden; + opacity: 0; + -webkit-transform: scale(0); + -ms-transform: scale(0); + transform: scale(0); + -webkit-transform-origin: 0 0; + -ms-transform-origin: 0 0; + transform-origin: 0 0; + box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12); + will-change: transform; + -webkit-transition: -webkit-transform 0.3s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.2s cubic-bezier(0.4, 0, 0.2, 1); + transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.2s cubic-bezier(0.4, 0, 0.2, 1); + z-index: -1; } + .mdl-menu__container.is-visible .mdl-menu__outline { + opacity: 1; + -webkit-transform: scale(1); + -ms-transform: scale(1); + transform: scale(1); + z-index: 999; } + .mdl-menu__outline.mdl-menu--bottom-right { + -webkit-transform-origin: 100% 0; + -ms-transform-origin: 100% 0; + transform-origin: 100% 0; } + .mdl-menu__outline.mdl-menu--top-left { + -webkit-transform-origin: 0 100%; + -ms-transform-origin: 0 100%; + transform-origin: 0 100%; } + .mdl-menu__outline.mdl-menu--top-right { + -webkit-transform-origin: 100% 100%; + -ms-transform-origin: 100% 100%; + transform-origin: 100% 100%; } + +.mdl-menu { + position: absolute; + list-style: none; + top: 0; + left: 0; + height: auto; + width: auto; + min-width: 124px; + padding: 8px 0; + margin: 0; + opacity: 0; + clip: rect(0 0 0 0); + z-index: -1; } + .mdl-menu__container.is-visible .mdl-menu { + opacity: 1; + z-index: 999; } + .mdl-menu.is-animating { + -webkit-transition: opacity 0.2s cubic-bezier(0.4, 0, 0.2, 1), clip 0.3s cubic-bezier(0.4, 0, 0.2, 1); + transition: opacity 0.2s cubic-bezier(0.4, 0, 0.2, 1), clip 0.3s cubic-bezier(0.4, 0, 0.2, 1); } + .mdl-menu.mdl-menu--bottom-right { + left: auto; + right: 0; } + .mdl-menu.mdl-menu--top-left { + top: auto; + bottom: 0; } + .mdl-menu.mdl-menu--top-right { + top: auto; + left: auto; + bottom: 0; + right: 0; } + .mdl-menu.mdl-menu--unaligned { + top: auto; + left: auto; } + +.mdl-menu__item { + display: block; + border: none; + color: rgba(0,0,0, 0.87); + background-color: transparent; + text-align: left; + margin: 0; + padding: 0 16px; + outline-color: rgb(189,189,189); + position: relative; + overflow: hidden; + font-size: 14px; + font-weight: 400; + line-height: 24px; + letter-spacing: 0; + text-decoration: none; + cursor: pointer; + height: 48px; + line-height: 48px; + white-space: nowrap; + opacity: 0; + -webkit-transition: opacity 0.2s cubic-bezier(0.4, 0, 0.2, 1); + transition: opacity 0.2s cubic-bezier(0.4, 0, 0.2, 1); + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; } + .mdl-menu__container.is-visible .mdl-menu__item { + opacity: 1; } + .mdl-menu__item::-moz-focus-inner { + border: 0; } + .mdl-menu__item[disabled] { + color: rgb(189,189,189); + background-color: transparent; + cursor: auto; } + .mdl-menu__item[disabled]:hover { + background-color: transparent; } + .mdl-menu__item[disabled]:focus { + background-color: transparent; } + .mdl-menu__item[disabled] .mdl-ripple { + background: transparent; } + .mdl-menu__item:hover { + background-color: rgb(238,238,238); } + .mdl-menu__item:focus { + outline: none; + background-color: rgb(238,238,238); } + .mdl-menu__item:active { + background-color: rgb(224,224,224); } + +.mdl-menu__item--ripple-container { + display: block; + height: 100%; + left: 0px; + position: absolute; + top: 0px; + width: 100%; + z-index: 0; + overflow: hidden; } + +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/*------------------------------------* $CONTENTS +\*------------------------------------*/ +/** + * STYLE GUIDE VARIABLES------------------Declarations of Sass variables + * -----Typography + * -----Colors + * -----Textfield + * -----Switch + * -----Spinner + * -----Radio + * -----Menu + * -----List + * -----Layout + * -----Icon toggles + * -----Footer + * -----Column + * -----Checkbox + * -----Card + * -----Button + * -----Animation + * -----Progress + * -----Badge + * -----Shadows + * -----Grid + * -----Data table + */ +/* ========== TYPOGRAPHY ========== */ +/* We're splitting fonts into "preferred" and "performance" in order to optimize + page loading. For important text, such as the body, we want it to load + immediately and not wait for the web font load, whereas for other sections, + such as headers and titles, we're OK with things taking a bit longer to load. + We do have some optional classes and parameters in the mixins, in case you + definitely want to make sure you're using the preferred font and don't mind + the performance hit. + We should be able to improve on this once CSS Font Loading L3 becomes more + widely available. +*/ +/* ========== COLORS ========== */ +/** +* +* Material design color palettes. +* @see http://www.google.com/design/spec/style/color.html +* +**/ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* ========== Color Palettes ========== */ +/* colors.scss */ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* ========== IMAGES ========== */ +/* ========== Color & Themes ========== */ +/* ========== Typography ========== */ +/* ========== Components ========== */ +/* ========== Standard Buttons ========== */ +/* ========== Icon Toggles ========== */ +/* ========== Radio Buttons ========== */ +/* ========== Ripple effect ========== */ +/* ========== Layout ========== */ +/* ========== Content Tabs ========== */ +/* ========== Checkboxes ========== */ +/* ========== Switches ========== */ +/* ========== Spinner ========== */ +/* ========== Text fields ========== */ +/* ========== Card ========== */ +/* ========== Sliders ========== */ +/* ========== Progress ========== */ +/* ========== List ========== */ +/* ========== Item ========== */ +/* ========== Dropdown menu ========== */ +/* ========== Tooltips ========== */ +/* ========== Footer ========== */ +/* TEXTFIELD */ +/* SWITCH */ +/* SPINNER */ +/* RADIO */ +/* MENU */ +/* LIST */ +/* LAYOUT */ +/* ICON TOGGLE */ +/* FOOTER */ +/*mega-footer*/ +/*mini-footer*/ +/* CHECKBOX */ +/* CARD */ +/* Card dimensions */ +/* Cover image */ +/* BUTTON */ +/** + * + * Dimensions + * + */ +/* ANIMATION */ +/* PROGRESS */ +/* BADGE */ +/* SHADOWS */ +/* GRID */ +/* DATA TABLE */ +/* TOOLTIP */ +.mdl-progress { + display: block; + position: relative; + height: 4px; + width: 500px; } + +.mdl-progress > .bar { + display: block; + position: absolute; + top: 0; + bottom: 0; + width: 0%; + -webkit-transition: width 0.2s cubic-bezier(0.4, 0, 0.2, 1); + transition: width 0.2s cubic-bezier(0.4, 0, 0.2, 1); } + +.mdl-progress > .progressbar { + background-color: rgb(63,81,181); + z-index: 1; + left: 0; } + +.mdl-progress > .bufferbar { + background-image: -webkit-linear-gradient(left, rgba(255,255,255, 0.7), rgba(255,255,255, 0.7)), -webkit-linear-gradient(left, rgb(63,81,181), rgb(63,81,181)); + background-image: linear-gradient(to right, rgba(255,255,255, 0.7), rgba(255,255,255, 0.7)), linear-gradient(to right, rgb(63,81,181), rgb(63,81,181)); + z-index: 0; + left: 0; } + +.mdl-progress > .auxbar { + right: 0; } + +@supports (-webkit-appearance: none) { + .mdl-progress:not(.mdl-progress__indeterminate):not(.mdl-progress__indeterminate) > .auxbar { + background-image: -webkit-linear-gradient(left, rgba(255,255,255, 0.7), rgba(255,255,255, 0.7)), -webkit-linear-gradient(left, rgb(63,81,181), rgb(63,81,181)); + background-image: linear-gradient(to right, rgba(255,255,255, 0.7), rgba(255,255,255, 0.7)), linear-gradient(to right, rgb(63,81,181), rgb(63,81,181)); + -webkit-mask: url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIj8+Cjxzdmcgd2lkdGg9IjEyIiBoZWlnaHQ9IjQiIHZpZXdQb3J0PSIwIDAgMTIgNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgogIDxlbGxpcHNlIGN4PSIyIiBjeT0iMiIgcng9IjIiIHJ5PSIyIj4KICAgIDxhbmltYXRlIGF0dHJpYnV0ZU5hbWU9ImN4IiBmcm9tPSIyIiB0bz0iLTEwIiBkdXI9IjAuNnMiIHJlcGVhdENvdW50PSJpbmRlZmluaXRlIiAvPgogIDwvZWxsaXBzZT4KICA8ZWxsaXBzZSBjeD0iMTQiIGN5PSIyIiByeD0iMiIgcnk9IjIiIGNsYXNzPSJsb2FkZXIiPgogICAgPGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iY3giIGZyb209IjE0IiB0bz0iMiIgZHVyPSIwLjZzIiByZXBlYXRDb3VudD0iaW5kZWZpbml0ZSIgLz4KICA8L2VsbGlwc2U+Cjwvc3ZnPgo="); + mask: url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIj8+Cjxzdmcgd2lkdGg9IjEyIiBoZWlnaHQ9IjQiIHZpZXdQb3J0PSIwIDAgMTIgNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgogIDxlbGxpcHNlIGN4PSIyIiBjeT0iMiIgcng9IjIiIHJ5PSIyIj4KICAgIDxhbmltYXRlIGF0dHJpYnV0ZU5hbWU9ImN4IiBmcm9tPSIyIiB0bz0iLTEwIiBkdXI9IjAuNnMiIHJlcGVhdENvdW50PSJpbmRlZmluaXRlIiAvPgogIDwvZWxsaXBzZT4KICA8ZWxsaXBzZSBjeD0iMTQiIGN5PSIyIiByeD0iMiIgcnk9IjIiIGNsYXNzPSJsb2FkZXIiPgogICAgPGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iY3giIGZyb209IjE0IiB0bz0iMiIgZHVyPSIwLjZzIiByZXBlYXRDb3VudD0iaW5kZWZpbml0ZSIgLz4KICA8L2VsbGlwc2U+Cjwvc3ZnPgo="); } } + +.mdl-progress:not(.mdl-progress__indeterminate) > .auxbar { + background-image: -webkit-linear-gradient(left, rgba(255,255,255, 0.9), rgba(255,255,255, 0.9)), -webkit-linear-gradient(left, rgb(63,81,181), rgb(63,81,181)); + background-image: linear-gradient(to right, rgba(255,255,255, 0.9), rgba(255,255,255, 0.9)), linear-gradient(to right, rgb(63,81,181), rgb(63,81,181)); } + +.mdl-progress.mdl-progress__indeterminate > .bar1 { + background-color: rgb(63,81,181); + -webkit-animation-name: indeterminate1; + animation-name: indeterminate1; + -webkit-animation-duration: 2s; + animation-duration: 2s; + -webkit-animation-iteration-count: infinite; + animation-iteration-count: infinite; + -webkit-animation-timing-function: linear; + animation-timing-function: linear; } + +.mdl-progress.mdl-progress__indeterminate > .bar3 { + background-image: none; + background-color: rgb(63,81,181); + -webkit-animation-name: indeterminate2; + animation-name: indeterminate2; + -webkit-animation-duration: 2s; + animation-duration: 2s; + -webkit-animation-iteration-count: infinite; + animation-iteration-count: infinite; + -webkit-animation-timing-function: linear; + animation-timing-function: linear; } + +@-webkit-keyframes indeterminate1 { + 0% { + left: 0%; + width: 0%; } + 50% { + left: 25%; + width: 75%; } + 75% { + left: 100%; + width: 0%; } } + +@keyframes indeterminate1 { + 0% { + left: 0%; + width: 0%; } + 50% { + left: 25%; + width: 75%; } + 75% { + left: 100%; + width: 0%; } } + +@-webkit-keyframes indeterminate2 { + 0% { + left: 0%; + width: 0%; } + 50% { + left: 0%; + width: 0%; } + 75% { + left: 0%; + width: 25%; } + 100% { + left: 100%; + width: 0%; } } + +@keyframes indeterminate2 { + 0% { + left: 0%; + width: 0%; } + 50% { + left: 0%; + width: 0%; } + 75% { + left: 0%; + width: 25%; } + 100% { + left: 100%; + width: 0%; } } + +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/*------------------------------------* $CONTENTS +\*------------------------------------*/ +/** + * STYLE GUIDE VARIABLES------------------Declarations of Sass variables + * -----Typography + * -----Colors + * -----Textfield + * -----Switch + * -----Spinner + * -----Radio + * -----Menu + * -----List + * -----Layout + * -----Icon toggles + * -----Footer + * -----Column + * -----Checkbox + * -----Card + * -----Button + * -----Animation + * -----Progress + * -----Badge + * -----Shadows + * -----Grid + * -----Data table + */ +/* ========== TYPOGRAPHY ========== */ +/* We're splitting fonts into "preferred" and "performance" in order to optimize + page loading. For important text, such as the body, we want it to load + immediately and not wait for the web font load, whereas for other sections, + such as headers and titles, we're OK with things taking a bit longer to load. + We do have some optional classes and parameters in the mixins, in case you + definitely want to make sure you're using the preferred font and don't mind + the performance hit. + We should be able to improve on this once CSS Font Loading L3 becomes more + widely available. +*/ +/* ========== COLORS ========== */ +/** +* +* Material design color palettes. +* @see http://www.google.com/design/spec/style/color.html +* +**/ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* ========== Color Palettes ========== */ +/* colors.scss */ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* ========== IMAGES ========== */ +/* ========== Color & Themes ========== */ +/* ========== Typography ========== */ +/* ========== Components ========== */ +/* ========== Standard Buttons ========== */ +/* ========== Icon Toggles ========== */ +/* ========== Radio Buttons ========== */ +/* ========== Ripple effect ========== */ +/* ========== Layout ========== */ +/* ========== Content Tabs ========== */ +/* ========== Checkboxes ========== */ +/* ========== Switches ========== */ +/* ========== Spinner ========== */ +/* ========== Text fields ========== */ +/* ========== Card ========== */ +/* ========== Sliders ========== */ +/* ========== Progress ========== */ +/* ========== List ========== */ +/* ========== Item ========== */ +/* ========== Dropdown menu ========== */ +/* ========== Tooltips ========== */ +/* ========== Footer ========== */ +/* TEXTFIELD */ +/* SWITCH */ +/* SPINNER */ +/* RADIO */ +/* MENU */ +/* LIST */ +/* LAYOUT */ +/* ICON TOGGLE */ +/* FOOTER */ +/*mega-footer*/ +/*mini-footer*/ +/* CHECKBOX */ +/* CARD */ +/* Card dimensions */ +/* Cover image */ +/* BUTTON */ +/** + * + * Dimensions + * + */ +/* ANIMATION */ +/* PROGRESS */ +/* BADGE */ +/* SHADOWS */ +/* GRID */ +/* DATA TABLE */ +/* TOOLTIP */ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* Typography */ +/* Shadows */ +/* Animations */ +.mdl-navigation { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-wrap: nowrap; + -ms-flex-wrap: nowrap; + flex-wrap: nowrap; + box-sizing: border-box; } + +.mdl-navigation__link { + color: rgb(66,66,66); + text-decoration: none; + font-weight: 500; + font-size: 13px; + margin: 0; } + +.mdl-layout { + width: 100%; + height: 100%; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-box-orient: vertical; + -webkit-box-direction: normal; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + overflow-y: auto; + overflow-x: hidden; + position: relative; + -webkit-overflow-scrolling: touch; } + +.mdl-layout.is-small-screen .mdl-layout--large-screen-only { + display: none; } + +.mdl-layout:not(.is-small-screen) .mdl-layout--small-screen-only { + display: none; } + +.mdl-layout__container { + position: absolute; + width: 100%; + height: 100%; } + +.mdl-layout__title, +.mdl-layout-title { + display: block; + position: relative; + font-family: "Roboto", "Helvetica", "Arial", sans-serif; + font-size: 20px; + font-weight: 500; + line-height: 1; + letter-spacing: 0.02em; + font-weight: 400; + box-sizing: border-box; } + +.mdl-layout-spacer { + -webkit-box-flex: 1; + -webkit-flex-grow: 1; + -ms-flex-positive: 1; + flex-grow: 1; } + +.mdl-layout__drawer { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-box-orient: vertical; + -webkit-box-direction: normal; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + -webkit-flex-wrap: nowrap; + -ms-flex-wrap: nowrap; + flex-wrap: nowrap; + width: 240px; + height: 100%; + max-height: 100%; + position: absolute; + top: 0; + left: 0; + box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12); + box-sizing: border-box; + border-right: 1px solid rgb(224,224,224); + background: rgb(250,250,250); + -webkit-transform: translateX(-250px); + -ms-transform: translateX(-250px); + transform: translateX(-250px); + -webkit-transform-style: preserve-3d; + transform-style: preserve-3d; + will-change: transform; + -webkit-transition-duration: 0.2s; + transition-duration: 0.2s; + -webkit-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + -webkit-transition-property: -webkit-transform; + transition-property: transform; + color: rgb(66,66,66); + overflow: visible; + overflow-y: auto; + z-index: 5; } + .mdl-layout__drawer.is-visible { + -webkit-transform: translateX(0); + -ms-transform: translateX(0); + transform: translateX(0); } + .mdl-layout__drawer.is-visible ~ .mdl-layout__content.mdl-layout__content { + overflow: hidden; } + .mdl-layout__drawer > * { + -webkit-flex-shrink: 0; + -ms-flex-negative: 0; + flex-shrink: 0; } + .mdl-layout__drawer > .mdl-layout__title, + .mdl-layout__drawer > .mdl-layout-title { + line-height: 64px; + padding-left: 40px; } + @media screen and (max-width: 1024px) { + .mdl-layout__drawer > .mdl-layout__title, + .mdl-layout__drawer > .mdl-layout-title { + line-height: 56px; + padding-left: 16px; } } + .mdl-layout__drawer .mdl-navigation { + -webkit-box-orient: vertical; + -webkit-box-direction: normal; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + -webkit-box-align: stretch; + -webkit-align-items: stretch; + -ms-flex-align: stretch; + align-items: stretch; + padding-top: 16px; } + .mdl-layout__drawer .mdl-navigation .mdl-navigation__link { + display: block; + -webkit-flex-shrink: 0; + -ms-flex-negative: 0; + flex-shrink: 0; + padding: 16px 40px; + margin: 0; + color: #757575; } + @media screen and (max-width: 1024px) { + .mdl-layout__drawer .mdl-navigation .mdl-navigation__link { + padding: 16px 16px; } } + .mdl-layout__drawer .mdl-navigation .mdl-navigation__link:hover { + background-color: rgb(224,224,224); } + .mdl-layout__drawer .mdl-navigation .mdl-navigation__link--current { + background-color: rgb(0,0,0); + color: rgb(224,224,224); } + @media screen and (min-width: 1025px) { + .mdl-layout--fixed-drawer > .mdl-layout__drawer { + -webkit-transform: translateX(0); + -ms-transform: translateX(0); + transform: translateX(0); } } + +.mdl-layout__drawer-button { + display: block; + position: absolute; + height: 48px; + width: 48px; + border: 0; + -webkit-flex-shrink: 0; + -ms-flex-negative: 0; + flex-shrink: 0; + overflow: hidden; + text-align: center; + cursor: pointer; + font-size: 26px; + line-height: 50px; + font-family: Helvetica, Arial, sans-serif; + margin: 10px 12px; + top: 0; + left: 0; + color: rgb(255,255,255); + z-index: 4; } + .mdl-layout__header .mdl-layout__drawer-button { + position: absolute; + color: rgb(255,255,255); + background-color: inherit; } + @media screen and (max-width: 1024px) { + .mdl-layout__header .mdl-layout__drawer-button { + margin: 4px; } } + @media screen and (max-width: 1024px) { + .mdl-layout__drawer-button { + margin: 4px; + color: rgba(0, 0, 0, 0.5); } } + @media screen and (min-width: 1025px) { + .mdl-layout--fixed-drawer > .mdl-layout__drawer-button { + display: none; } } + +.mdl-layout__header { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-box-orient: vertical; + -webkit-box-direction: normal; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + -webkit-flex-wrap: nowrap; + -ms-flex-wrap: nowrap; + flex-wrap: nowrap; + -webkit-box-pack: start; + -webkit-justify-content: flex-start; + -ms-flex-pack: start; + justify-content: flex-start; + box-sizing: border-box; + -webkit-flex-shrink: 0; + -ms-flex-negative: 0; + flex-shrink: 0; + width: 100%; + margin: 0; + padding: 0; + border: none; + min-height: 64px; + max-height: 1000px; + z-index: 3; + background-color: rgb(63,81,181); + color: rgb(255,255,255); + box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12); + -webkit-transition-duration: 0.2s; + transition-duration: 0.2s; + -webkit-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + -webkit-transition-property: max-height, box-shadow; + transition-property: max-height, box-shadow; } + @media screen and (max-width: 1024px) { + .mdl-layout__header { + min-height: 56px; } } + .mdl-layout--fixed-drawer.is-upgraded:not(.is-small-screen) > .mdl-layout__header { + margin-left: 240px; + width: calc(100% - 240px); } + @media screen and (min-width: 1025px) { + .mdl-layout--fixed-drawer > .mdl-layout__header .mdl-layout__header-row { + padding-left: 40px; } } + .mdl-layout__header > .mdl-layout-icon { + position: absolute; + left: 40px; + top: 16px; + height: 32px; + width: 32px; + overflow: hidden; + z-index: 3; + display: block; } + @media screen and (max-width: 1024px) { + .mdl-layout__header > .mdl-layout-icon { + left: 16px; + top: 12px; } } + .mdl-layout.has-drawer .mdl-layout__header > .mdl-layout-icon { + display: none; } + .mdl-layout__header.is-compact { + max-height: 64px; } + @media screen and (max-width: 1024px) { + .mdl-layout__header.is-compact { + max-height: 56px; } } + .mdl-layout__header.is-compact.has-tabs { + height: 112px; } + @media screen and (max-width: 1024px) { + .mdl-layout__header.is-compact.has-tabs { + min-height: 104px; } } + @media screen and (max-width: 1024px) { + .mdl-layout__header { + display: none; } + .mdl-layout--fixed-header > .mdl-layout__header { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; } } + +.mdl-layout__header--transparent.mdl-layout__header--transparent { + background-color: transparent; + box-shadow: none; } + +.mdl-layout__header--seamed { + box-shadow: none; } + +.mdl-layout__header--scroll { + box-shadow: none; } + +.mdl-layout__header--waterfall { + box-shadow: none; + overflow: hidden; } + .mdl-layout__header--waterfall.is-casting-shadow { + box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12); } + +.mdl-layout__header-row { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-box-orient: horizontal; + -webkit-box-direction: normal; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-flex-wrap: nowrap; + -ms-flex-wrap: nowrap; + flex-wrap: nowrap; + -webkit-flex-shrink: 0; + -ms-flex-negative: 0; + flex-shrink: 0; + box-sizing: border-box; + -webkit-align-self: stretch; + -ms-flex-item-align: stretch; + align-self: stretch; + -webkit-box-align: center; + -webkit-align-items: center; + -ms-flex-align: center; + align-items: center; + height: 64px; + margin: 0; + padding: 0 40px 0 80px; } + @media screen and (max-width: 1024px) { + .mdl-layout__header-row { + height: 56px; + padding: 0 16px 0 72px; } } + .mdl-layout__header-row > * { + -webkit-flex-shrink: 0; + -ms-flex-negative: 0; + flex-shrink: 0; } + .mdl-layout__header--scroll .mdl-layout__header-row { + width: 100%; } + .mdl-layout__header-row .mdl-navigation { + margin: 0; + padding: 0; + height: 64px; + -webkit-box-orient: horizontal; + -webkit-box-direction: normal; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-box-align: center; + -webkit-align-items: center; + -ms-flex-align: center; + align-items: center; } + @media screen and (max-width: 1024px) { + .mdl-layout__header-row .mdl-navigation { + height: 56px; } } + .mdl-layout__header-row .mdl-navigation__link { + display: block; + color: rgb(255,255,255); + line-height: 64px; + padding: 0 24px; } + @media screen and (max-width: 1024px) { + .mdl-layout__header-row .mdl-navigation__link { + line-height: 56px; + padding: 0 16px; } } + +.mdl-layout__obfuscator { + background-color: transparent; + position: absolute; + top: 0; + left: 0; + height: 100%; + width: 100%; + z-index: 4; + visibility: hidden; + -webkit-transition-property: background-color; + transition-property: background-color; + -webkit-transition-duration: 0.2s; + transition-duration: 0.2s; + -webkit-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); } + .mdl-layout__obfuscator.is-visible { + background-color: rgba(0, 0, 0, 0.5); + visibility: visible; } + +.mdl-layout__content { + -ms-flex: 0 1 auto; + display: inline-block; + overflow-y: auto; + overflow-x: hidden; + -webkit-box-flex: 1; + -webkit-flex-grow: 1; + -ms-flex-positive: 1; + flex-grow: 1; + z-index: 1; + -webkit-overflow-scrolling: touch; } + .mdl-layout--fixed-drawer > .mdl-layout__content { + margin-left: 240px; } + .mdl-layout__container.has-scrolling-header .mdl-layout__content { + overflow: visible; } + @media screen and (max-width: 1024px) { + .mdl-layout--fixed-drawer > .mdl-layout__content { + margin-left: 0; } + .mdl-layout__container.has-scrolling-header .mdl-layout__content { + overflow-y: auto; + overflow-x: hidden; } } + +.mdl-layout__tab-bar { + height: 96px; + margin: 0; + width: calc(100% - 112px); + padding: 0 0 0 56px; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + background-color: rgb(63,81,181); + overflow-y: hidden; + overflow-x: scroll; } + .mdl-layout__tab-bar::-webkit-scrollbar { + display: none; } + @media screen and (max-width: 1024px) { + .mdl-layout__tab-bar { + width: calc(100% - 60px); + padding: 0 0 0 60px; } } + .mdl-layout--fixed-tabs .mdl-layout__tab-bar { + padding: 0; + overflow: hidden; + width: 100%; } + +.mdl-layout__tab-bar-container { + position: relative; + height: 48px; + width: 100%; + border: none; + margin: 0; + z-index: 2; + -webkit-box-flex: 0; + -webkit-flex-grow: 0; + -ms-flex-positive: 0; + flex-grow: 0; + -webkit-flex-shrink: 0; + -ms-flex-negative: 0; + flex-shrink: 0; + overflow: hidden; } + .mdl-layout__container > .mdl-layout__tab-bar-container { + position: absolute; + top: 0; + left: 0; } + +.mdl-layout__tab-bar-button { + display: inline-block; + position: absolute; + top: 0; + height: 48px; + width: 56px; + z-index: 4; + text-align: center; + background-color: rgb(63,81,181); + color: transparent; + cursor: pointer; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; } + @media screen and (max-width: 1024px) { + .mdl-layout__tab-bar-button { + display: none; + width: 60px; } } + .mdl-layout--fixed-tabs .mdl-layout__tab-bar-button { + display: none; } + .mdl-layout__tab-bar-button .material-icons { + line-height: 48px; } + .mdl-layout__tab-bar-button.is-active { + color: rgb(255,255,255); } + +.mdl-layout__tab-bar-left-button { + left: 0; } + +.mdl-layout__tab-bar-right-button { + right: 0; } + +.mdl-layout__tab { + margin: 0; + border: none; + padding: 0 24px 0 24px; + float: left; + position: relative; + display: block; + -webkit-box-flex: 0; + -webkit-flex-grow: 0; + -ms-flex-positive: 0; + flex-grow: 0; + -webkit-flex-shrink: 0; + -ms-flex-negative: 0; + flex-shrink: 0; + text-decoration: none; + height: 48px; + line-height: 48px; + text-align: center; + font-weight: 500; + font-size: 14px; + text-transform: uppercase; + color: rgba(255,255,255, 0.6); + overflow: hidden; } + @media screen and (max-width: 1024px) { + .mdl-layout__tab { + padding: 0 12px 0 12px; } } + .mdl-layout--fixed-tabs .mdl-layout__tab { + float: none; + -webkit-box-flex: 1; + -webkit-flex-grow: 1; + -ms-flex-positive: 1; + flex-grow: 1; + padding: 0; } + .mdl-layout.is-upgraded .mdl-layout__tab.is-active { + color: rgb(255,255,255); } + .mdl-layout.is-upgraded .mdl-layout__tab.is-active::after { + height: 2px; + width: 100%; + display: block; + content: " "; + bottom: 0; + left: 0; + position: absolute; + background: rgb(255,64,129); + -webkit-animation: border-expand 0.2s cubic-bezier(0.4, 0, 0.4, 1) 0.01s alternate forwards; + animation: border-expand 0.2s cubic-bezier(0.4, 0, 0.4, 1) 0.01s alternate forwards; + -webkit-transition: all 1s cubic-bezier(0.4, 0, 1, 1); + transition: all 1s cubic-bezier(0.4, 0, 1, 1); } + .mdl-layout__tab .mdl-layout__tab-ripple-container { + display: block; + position: absolute; + height: 100%; + width: 100%; + left: 0; + top: 0; + z-index: 1; + overflow: hidden; } + .mdl-layout__tab .mdl-layout__tab-ripple-container .mdl-ripple { + background-color: rgb(255,255,255); } + +.mdl-layout__tab-panel { + display: block; } + .mdl-layout.is-upgraded .mdl-layout__tab-panel { + display: none; } + .mdl-layout.is-upgraded .mdl-layout__tab-panel.is-active { + display: block; } + +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/*------------------------------------* $CONTENTS +\*------------------------------------*/ +/** + * STYLE GUIDE VARIABLES------------------Declarations of Sass variables + * -----Typography + * -----Colors + * -----Textfield + * -----Switch + * -----Spinner + * -----Radio + * -----Menu + * -----List + * -----Layout + * -----Icon toggles + * -----Footer + * -----Column + * -----Checkbox + * -----Card + * -----Button + * -----Animation + * -----Progress + * -----Badge + * -----Shadows + * -----Grid + * -----Data table + */ +/* ========== TYPOGRAPHY ========== */ +/* We're splitting fonts into "preferred" and "performance" in order to optimize + page loading. For important text, such as the body, we want it to load + immediately and not wait for the web font load, whereas for other sections, + such as headers and titles, we're OK with things taking a bit longer to load. + We do have some optional classes and parameters in the mixins, in case you + definitely want to make sure you're using the preferred font and don't mind + the performance hit. + We should be able to improve on this once CSS Font Loading L3 becomes more + widely available. +*/ +/* ========== COLORS ========== */ +/** +* +* Material design color palettes. +* @see http://www.google.com/design/spec/style/color.html +* +**/ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* ========== Color Palettes ========== */ +/* colors.scss */ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* ========== IMAGES ========== */ +/* ========== Color & Themes ========== */ +/* ========== Typography ========== */ +/* ========== Components ========== */ +/* ========== Standard Buttons ========== */ +/* ========== Icon Toggles ========== */ +/* ========== Radio Buttons ========== */ +/* ========== Ripple effect ========== */ +/* ========== Layout ========== */ +/* ========== Content Tabs ========== */ +/* ========== Checkboxes ========== */ +/* ========== Switches ========== */ +/* ========== Spinner ========== */ +/* ========== Text fields ========== */ +/* ========== Card ========== */ +/* ========== Sliders ========== */ +/* ========== Progress ========== */ +/* ========== List ========== */ +/* ========== Item ========== */ +/* ========== Dropdown menu ========== */ +/* ========== Tooltips ========== */ +/* ========== Footer ========== */ +/* TEXTFIELD */ +/* SWITCH */ +/* SPINNER */ +/* RADIO */ +/* MENU */ +/* LIST */ +/* LAYOUT */ +/* ICON TOGGLE */ +/* FOOTER */ +/*mega-footer*/ +/*mini-footer*/ +/* CHECKBOX */ +/* CARD */ +/* Card dimensions */ +/* Cover image */ +/* BUTTON */ +/** + * + * Dimensions + * + */ +/* ANIMATION */ +/* PROGRESS */ +/* BADGE */ +/* SHADOWS */ +/* GRID */ +/* DATA TABLE */ +/* TOOLTIP */ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* Typography */ +/* Shadows */ +/* Animations */ +.mdl-radio { + position: relative; + font-size: 16px; + line-height: 24px; + display: inline-block; + box-sizing: border-box; + margin: 0; + padding-left: 0; } + .mdl-radio.is-upgraded { + padding-left: 24px; } + +.mdl-radio__button { + line-height: 24px; } + .mdl-radio.is-upgraded .mdl-radio__button { + position: absolute; + width: 0; + height: 0; + margin: 0; + padding: 0; + opacity: 0; + -ms-appearance: none; + -moz-appearance: none; + -webkit-appearance: none; + appearance: none; + border: none; } + +.mdl-radio__outer-circle { + position: absolute; + top: 4px; + left: 0; + display: inline-block; + box-sizing: border-box; + width: 16px; + height: 16px; + margin: 0; + cursor: pointer; + border: 2px solid rgba(0,0,0, 0.54); + border-radius: 50%; + z-index: 2; } + .mdl-radio.is-checked .mdl-radio__outer-circle { + border: 2px solid rgb(63,81,181); } + .mdl-radio.is-disabled .mdl-radio__outer-circle { + border: 2px solid rgba(0,0,0, 0.26); + cursor: auto; } + +.mdl-radio__inner-circle { + position: absolute; + z-index: 1; + margin: 0; + top: 8px; + left: 4px; + box-sizing: border-box; + width: 8px; + height: 8px; + cursor: pointer; + -webkit-transition-duration: 0.28s; + transition-duration: 0.28s; + -webkit-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + -webkit-transition-property: -webkit-transform; + transition-property: transform; + -webkit-transform: scale3d(0, 0, 0); + transform: scale3d(0, 0, 0); + border-radius: 50%; + background: rgb(63,81,181); } + .mdl-radio.is-checked .mdl-radio__inner-circle { + -webkit-transform: scale3d(1, 1, 1); + transform: scale3d(1, 1, 1); } + .mdl-radio.is-disabled .mdl-radio__inner-circle { + background: rgba(0,0,0, 0.26); + cursor: auto; } + .mdl-radio.is-focused .mdl-radio__inner-circle { + box-shadow: 0 0 0px 10px rgba(0, 0, 0, 0.1); } + +.mdl-radio__label { + cursor: pointer; } + .mdl-radio.is-disabled .mdl-radio__label { + color: rgba(0,0,0, 0.26); + cursor: auto; } + +.mdl-radio__ripple-container { + position: absolute; + z-index: 2; + top: -9px; + left: -13px; + box-sizing: border-box; + width: 42px; + height: 42px; + border-radius: 50%; + cursor: pointer; + overflow: hidden; + -webkit-mask-image: -webkit-radial-gradient(circle, white, black); } + .mdl-radio__ripple-container .mdl-ripple { + background: rgb(63,81,181); } + .mdl-radio.is-disabled .mdl-radio__ripple-container { + cursor: auto; } + .mdl-radio.is-disabled .mdl-radio__ripple-container .mdl-ripple { + background: transparent; } + +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/*------------------------------------* $CONTENTS +\*------------------------------------*/ +/** + * STYLE GUIDE VARIABLES------------------Declarations of Sass variables + * -----Typography + * -----Colors + * -----Textfield + * -----Switch + * -----Spinner + * -----Radio + * -----Menu + * -----List + * -----Layout + * -----Icon toggles + * -----Footer + * -----Column + * -----Checkbox + * -----Card + * -----Button + * -----Animation + * -----Progress + * -----Badge + * -----Shadows + * -----Grid + * -----Data table + */ +/* ========== TYPOGRAPHY ========== */ +/* We're splitting fonts into "preferred" and "performance" in order to optimize + page loading. For important text, such as the body, we want it to load + immediately and not wait for the web font load, whereas for other sections, + such as headers and titles, we're OK with things taking a bit longer to load. + We do have some optional classes and parameters in the mixins, in case you + definitely want to make sure you're using the preferred font and don't mind + the performance hit. + We should be able to improve on this once CSS Font Loading L3 becomes more + widely available. +*/ +/* ========== COLORS ========== */ +/** +* +* Material design color palettes. +* @see http://www.google.com/design/spec/style/color.html +* +**/ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* ========== Color Palettes ========== */ +/* colors.scss */ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* ========== IMAGES ========== */ +/* ========== Color & Themes ========== */ +/* ========== Typography ========== */ +/* ========== Components ========== */ +/* ========== Standard Buttons ========== */ +/* ========== Icon Toggles ========== */ +/* ========== Radio Buttons ========== */ +/* ========== Ripple effect ========== */ +/* ========== Layout ========== */ +/* ========== Content Tabs ========== */ +/* ========== Checkboxes ========== */ +/* ========== Switches ========== */ +/* ========== Spinner ========== */ +/* ========== Text fields ========== */ +/* ========== Card ========== */ +/* ========== Sliders ========== */ +/* ========== Progress ========== */ +/* ========== List ========== */ +/* ========== Item ========== */ +/* ========== Dropdown menu ========== */ +/* ========== Tooltips ========== */ +/* ========== Footer ========== */ +/* TEXTFIELD */ +/* SWITCH */ +/* SPINNER */ +/* RADIO */ +/* MENU */ +/* LIST */ +/* LAYOUT */ +/* ICON TOGGLE */ +/* FOOTER */ +/*mega-footer*/ +/*mini-footer*/ +/* CHECKBOX */ +/* CARD */ +/* Card dimensions */ +/* Cover image */ +/* BUTTON */ +/** + * + * Dimensions + * + */ +/* ANIMATION */ +/* PROGRESS */ +/* BADGE */ +/* SHADOWS */ +/* GRID */ +/* DATA TABLE */ +/* TOOLTIP */ +_:-ms-input-placeholder, :root .mdl-slider.mdl-slider.is-upgraded { + -ms-appearance: none; + height: 32px; + margin: 0; } + +.mdl-slider { + width: calc(100% - 40px); + margin: 0 20px; } + .mdl-slider.is-upgraded { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + height: 2px; + background: transparent; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + outline: 0; + padding: 0; + color: rgb(63,81,181); + -webkit-align-self: center; + -ms-flex-item-align: center; + align-self: center; + z-index: 1; + cursor: pointer; + /**************************** Tracks ****************************/ + /**************************** Thumbs ****************************/ + /**************************** 0-value ****************************/ + /**************************** Disabled ****************************/ } + .mdl-slider.is-upgraded::-moz-focus-outer { + border: 0; } + .mdl-slider.is-upgraded::-ms-tooltip { + display: none; } + .mdl-slider.is-upgraded::-webkit-slider-runnable-track { + background: transparent; } + .mdl-slider.is-upgraded::-moz-range-track { + background: transparent; + border: none; } + .mdl-slider.is-upgraded::-ms-track { + background: none; + color: transparent; + height: 2px; + width: 100%; + border: none; } + .mdl-slider.is-upgraded::-ms-fill-lower { + padding: 0; + background: linear-gradient(to right, transparent, transparent 16px, rgb(63,81,181) 16px, rgb(63,81,181) 0); } + .mdl-slider.is-upgraded::-ms-fill-upper { + padding: 0; + background: linear-gradient(to left, transparent, transparent 16px, rgba(0,0,0, 0.26) 16px, rgba(0,0,0, 0.26) 0); } + .mdl-slider.is-upgraded::-webkit-slider-thumb { + -webkit-appearance: none; + width: 12px; + height: 12px; + box-sizing: border-box; + border-radius: 50%; + background: rgb(63,81,181); + border: none; + -webkit-transition: -webkit-transform 0.18s cubic-bezier(0.4, 0, 0.2, 1), border 0.18s cubic-bezier(0.4, 0, 0.2, 1), box-shadow 0.18s cubic-bezier(0.4, 0, 0.2, 1), background 0.28s cubic-bezier(0.4, 0, 0.2, 1); + transition: transform 0.18s cubic-bezier(0.4, 0, 0.2, 1), border 0.18s cubic-bezier(0.4, 0, 0.2, 1), box-shadow 0.18s cubic-bezier(0.4, 0, 0.2, 1), background 0.28s cubic-bezier(0.4, 0, 0.2, 1); } + .mdl-slider.is-upgraded::-moz-range-thumb { + -moz-appearance: none; + width: 12px; + height: 12px; + box-sizing: border-box; + border-radius: 50%; + background-image: none; + background: rgb(63,81,181); + border: none; } + .mdl-slider.is-upgraded:focus:not(:active)::-webkit-slider-thumb { + box-shadow: 0 0 0 10px rgba(63,81,181, 0.26); } + .mdl-slider.is-upgraded:focus:not(:active)::-moz-range-thumb { + box-shadow: 0 0 0 10px rgba(63,81,181, 0.26); } + .mdl-slider.is-upgraded:active::-webkit-slider-thumb { + background-image: none; + background: rgb(63,81,181); + -webkit-transform: scale(1.5); + transform: scale(1.5); } + .mdl-slider.is-upgraded:active::-moz-range-thumb { + background-image: none; + background: rgb(63,81,181); + transform: scale(1.5); } + .mdl-slider.is-upgraded::-ms-thumb { + width: 32px; + height: 32px; + border: none; + border-radius: 50%; + background: rgb(63,81,181); + -ms-transform: scale(0.375); + transform: scale(0.375); + transition: transform 0.18s cubic-bezier(0.4, 0, 0.2, 1), background 0.28s cubic-bezier(0.4, 0, 0.2, 1); } + .mdl-slider.is-upgraded:focus:not(:active)::-ms-thumb { + background: radial-gradient(circle closest-side, rgb(63,81,181) 0%, rgb(63,81,181) 37.5%, rgba(63,81,181, 0.26) 37.5%, rgba(63,81,181, 0.26) 100%); + -ms-transform: scale(1); + transform: scale(1); } + .mdl-slider.is-upgraded:active::-ms-thumb { + background: rgb(63,81,181); + -ms-transform: scale(0.5625); + transform: scale(0.5625); } + .mdl-slider.is-upgraded.is-lowest-value::-webkit-slider-thumb { + border: 2px solid rgba(0,0,0, 0.26); + background: transparent; } + .mdl-slider.is-upgraded.is-lowest-value::-moz-range-thumb { + border: 2px solid rgba(0,0,0, 0.26); + background: transparent; } + .mdl-slider.is-upgraded.is-lowest-value + +.mdl-slider__background-flex > .mdl-slider__background-upper { + left: 6px; } + .mdl-slider.is-upgraded.is-lowest-value:focus:not(:active)::-webkit-slider-thumb { + box-shadow: 0 0 0 10px rgba(0,0,0, 0.12); + background: rgba(0,0,0, 0.12); } + .mdl-slider.is-upgraded.is-lowest-value:focus:not(:active)::-moz-range-thumb { + box-shadow: 0 0 0 10px rgba(0,0,0, 0.12); + background: rgba(0,0,0, 0.12); } + .mdl-slider.is-upgraded.is-lowest-value:active::-webkit-slider-thumb { + border: 1.6px solid rgba(0,0,0, 0.26); + -webkit-transform: scale(1.5); + transform: scale(1.5); } + .mdl-slider.is-upgraded.is-lowest-value:active + +.mdl-slider__background-flex > .mdl-slider__background-upper { + left: 9px; } + .mdl-slider.is-upgraded.is-lowest-value:active::-moz-range-thumb { + border: 1.5px solid rgba(0,0,0, 0.26); + transform: scale(1.5); } + .mdl-slider.is-upgraded.is-lowest-value::-ms-thumb { + background: radial-gradient(circle closest-side, transparent 0%, transparent 66.67%, rgba(0,0,0, 0.26) 66.67%, rgba(0,0,0, 0.26) 100%); } + .mdl-slider.is-upgraded.is-lowest-value:focus:not(:active)::-ms-thumb { + background: radial-gradient(circle closest-side, rgba(0,0,0, 0.12) 0%, rgba(0,0,0, 0.12) 25%, rgba(0,0,0, 0.26) 25%, rgba(0,0,0, 0.26) 37.5%, rgba(0,0,0, 0.12) 37.5%, rgba(0,0,0, 0.12) 100%); + -ms-transform: scale(1); + transform: scale(1); } + .mdl-slider.is-upgraded.is-lowest-value:active::-ms-thumb { + -ms-transform: scale(0.5625); + transform: scale(0.5625); + background: radial-gradient(circle closest-side, transparent 0%, transparent 77.78%, rgba(0,0,0, 0.26) 77.78%, rgba(0,0,0, 0.26) 100%); } + .mdl-slider.is-upgraded.is-lowest-value::-ms-fill-lower { + background: transparent; } + .mdl-slider.is-upgraded.is-lowest-value::-ms-fill-upper { + margin-left: 6px; } + .mdl-slider.is-upgraded.is-lowest-value:active::-ms-fill-upper { + margin-left: 9px; } + .mdl-slider.is-upgraded:disabled:focus::-webkit-slider-thumb, .mdl-slider.is-upgraded:disabled:active::-webkit-slider-thumb, .mdl-slider.is-upgraded:disabled::-webkit-slider-thumb { + -webkit-transform: scale(0.667); + transform: scale(0.667); + background: rgba(0,0,0, 0.26); } + .mdl-slider.is-upgraded:disabled:focus::-moz-range-thumb, .mdl-slider.is-upgraded:disabled:active::-moz-range-thumb, .mdl-slider.is-upgraded:disabled::-moz-range-thumb { + transform: scale(0.667); + background: rgba(0,0,0, 0.26); } + .mdl-slider.is-upgraded:disabled + +.mdl-slider__background-flex > .mdl-slider__background-lower { + background-color: rgba(0,0,0, 0.26); + left: -6px; } + .mdl-slider.is-upgraded:disabled + +.mdl-slider__background-flex > .mdl-slider__background-upper { + left: 6px; } + .mdl-slider.is-upgraded.is-lowest-value:disabled:focus::-webkit-slider-thumb, .mdl-slider.is-upgraded.is-lowest-value:disabled:active::-webkit-slider-thumb, .mdl-slider.is-upgraded.is-lowest-value:disabled::-webkit-slider-thumb { + border: 3px solid rgba(0,0,0, 0.26); + background: transparent; + -webkit-transform: scale(0.667); + transform: scale(0.667); } + .mdl-slider.is-upgraded.is-lowest-value:disabled:focus::-moz-range-thumb, .mdl-slider.is-upgraded.is-lowest-value:disabled:active::-moz-range-thumb, .mdl-slider.is-upgraded.is-lowest-value:disabled::-moz-range-thumb { + border: 3px solid rgba(0,0,0, 0.26); + background: transparent; + transform: scale(0.667); } + .mdl-slider.is-upgraded.is-lowest-value:disabled:active + +.mdl-slider__background-flex > .mdl-slider__background-upper { + left: 6px; } + .mdl-slider.is-upgraded:disabled:focus::-ms-thumb, .mdl-slider.is-upgraded:disabled:active::-ms-thumb, .mdl-slider.is-upgraded:disabled::-ms-thumb { + -ms-transform: scale(0.25); + transform: scale(0.25); + background: rgba(0,0,0, 0.26); } + .mdl-slider.is-upgraded.is-lowest-value:disabled:focus::-ms-thumb, .mdl-slider.is-upgraded.is-lowest-value:disabled:active::-ms-thumb, .mdl-slider.is-upgraded.is-lowest-value:disabled::-ms-thumb { + -ms-transform: scale(0.25); + transform: scale(0.25); + background: radial-gradient(circle closest-side, transparent 0%, transparent 50%, rgba(0,0,0, 0.26) 50%, rgba(0,0,0, 0.26) 100%); } + .mdl-slider.is-upgraded:disabled::-ms-fill-lower { + margin-right: 6px; + background: linear-gradient(to right, transparent, transparent 25px, rgba(0,0,0, 0.26) 25px, rgba(0,0,0, 0.26) 0); } + .mdl-slider.is-upgraded:disabled::-ms-fill-upper { + margin-left: 6px; } + .mdl-slider.is-upgraded.is-lowest-value:disabled:active::-ms-fill-upper { + margin-left: 6px; } + +.mdl-slider__ie-container { + height: 18px; + overflow: visible; + border: none; + margin: none; + padding: none; } + +.mdl-slider__container { + height: 18px; + position: relative; + background: none; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-box-orient: horizontal; + -webkit-box-direction: normal; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; } + +.mdl-slider__background-flex { + background: transparent; + position: absolute; + height: 2px; + width: calc(100% - 52px); + top: 50%; + left: 0; + margin: 0 26px; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + overflow: hidden; + border: 0; + padding: 0; + -webkit-transform: translate(0, -1px); + -ms-transform: translate(0, -1px); + transform: translate(0, -1px); } + +.mdl-slider__background-lower { + background: rgb(63,81,181); + -webkit-box-flex: 0; + -webkit-flex: 0; + -ms-flex: 0; + flex: 0; + position: relative; + border: 0; + padding: 0; } + +.mdl-slider__background-upper { + background: rgba(0,0,0, 0.26); + -webkit-box-flex: 0; + -webkit-flex: 0; + -ms-flex: 0; + flex: 0; + position: relative; + border: 0; + padding: 0; + -webkit-transition: left 0.18s cubic-bezier(0.4, 0, 0.2, 1); + transition: left 0.18s cubic-bezier(0.4, 0, 0.2, 1); } + +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/*------------------------------------* $CONTENTS +\*------------------------------------*/ +/** + * STYLE GUIDE VARIABLES------------------Declarations of Sass variables + * -----Typography + * -----Colors + * -----Textfield + * -----Switch + * -----Spinner + * -----Radio + * -----Menu + * -----List + * -----Layout + * -----Icon toggles + * -----Footer + * -----Column + * -----Checkbox + * -----Card + * -----Button + * -----Animation + * -----Progress + * -----Badge + * -----Shadows + * -----Grid + * -----Data table + */ +/* ========== TYPOGRAPHY ========== */ +/* We're splitting fonts into "preferred" and "performance" in order to optimize + page loading. For important text, such as the body, we want it to load + immediately and not wait for the web font load, whereas for other sections, + such as headers and titles, we're OK with things taking a bit longer to load. + We do have some optional classes and parameters in the mixins, in case you + definitely want to make sure you're using the preferred font and don't mind + the performance hit. + We should be able to improve on this once CSS Font Loading L3 becomes more + widely available. +*/ +/* ========== COLORS ========== */ +/** +* +* Material design color palettes. +* @see http://www.google.com/design/spec/style/color.html +* +**/ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* ========== Color Palettes ========== */ +/* colors.scss */ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* ========== IMAGES ========== */ +/* ========== Color & Themes ========== */ +/* ========== Typography ========== */ +/* ========== Components ========== */ +/* ========== Standard Buttons ========== */ +/* ========== Icon Toggles ========== */ +/* ========== Radio Buttons ========== */ +/* ========== Ripple effect ========== */ +/* ========== Layout ========== */ +/* ========== Content Tabs ========== */ +/* ========== Checkboxes ========== */ +/* ========== Switches ========== */ +/* ========== Spinner ========== */ +/* ========== Text fields ========== */ +/* ========== Card ========== */ +/* ========== Sliders ========== */ +/* ========== Progress ========== */ +/* ========== List ========== */ +/* ========== Item ========== */ +/* ========== Dropdown menu ========== */ +/* ========== Tooltips ========== */ +/* ========== Footer ========== */ +/* TEXTFIELD */ +/* SWITCH */ +/* SPINNER */ +/* RADIO */ +/* MENU */ +/* LIST */ +/* LAYOUT */ +/* ICON TOGGLE */ +/* FOOTER */ +/*mega-footer*/ +/*mini-footer*/ +/* CHECKBOX */ +/* CARD */ +/* Card dimensions */ +/* Cover image */ +/* BUTTON */ +/** + * + * Dimensions + * + */ +/* ANIMATION */ +/* PROGRESS */ +/* BADGE */ +/* SHADOWS */ +/* GRID */ +/* DATA TABLE */ +/* TOOLTIP */ +.mdl-spinner { + display: inline-block; + position: relative; + width: 28px; + height: 28px; } + .mdl-spinner:not(.is-upgraded).is-active:after { + content: "Loading..."; } + .mdl-spinner.is-upgraded.is-active { + -webkit-animation: mdl-spinner__container-rotate 1568.23529412ms linear infinite; + animation: mdl-spinner__container-rotate 1568.23529412ms linear infinite; } + +@-webkit-keyframes mdl-spinner__container-rotate { + to { + -webkit-transform: rotate(360deg); + transform: rotate(360deg); } } + +@keyframes mdl-spinner__container-rotate { + to { + -webkit-transform: rotate(360deg); + transform: rotate(360deg); } } + +.mdl-spinner__layer { + position: absolute; + width: 100%; + height: 100%; + opacity: 0; } + +.mdl-spinner__layer-1 { + border-color: rgb(66,165,245); } + .mdl-spinner--single-color .mdl-spinner__layer-1 { + border-color: rgb(63,81,181); } + .mdl-spinner.is-active .mdl-spinner__layer-1 { + -webkit-animation: mdl-spinner__fill-unfill-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both, mdl-spinner__layer-1-fade-in-out 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both; + animation: mdl-spinner__fill-unfill-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both, mdl-spinner__layer-1-fade-in-out 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both; } + +.mdl-spinner__layer-2 { + border-color: rgb(244,67,54); } + .mdl-spinner--single-color .mdl-spinner__layer-2 { + border-color: rgb(63,81,181); } + .mdl-spinner.is-active .mdl-spinner__layer-2 { + -webkit-animation: mdl-spinner__fill-unfill-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both, mdl-spinner__layer-2-fade-in-out 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both; + animation: mdl-spinner__fill-unfill-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both, mdl-spinner__layer-2-fade-in-out 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both; } + +.mdl-spinner__layer-3 { + border-color: rgb(253,216,53); } + .mdl-spinner--single-color .mdl-spinner__layer-3 { + border-color: rgb(63,81,181); } + .mdl-spinner.is-active .mdl-spinner__layer-3 { + -webkit-animation: mdl-spinner__fill-unfill-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both, mdl-spinner__layer-3-fade-in-out 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both; + animation: mdl-spinner__fill-unfill-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both, mdl-spinner__layer-3-fade-in-out 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both; } + +.mdl-spinner__layer-4 { + border-color: rgb(76,175,80); } + .mdl-spinner--single-color .mdl-spinner__layer-4 { + border-color: rgb(63,81,181); } + .mdl-spinner.is-active .mdl-spinner__layer-4 { + -webkit-animation: mdl-spinner__fill-unfill-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both, mdl-spinner__layer-4-fade-in-out 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both; + animation: mdl-spinner__fill-unfill-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both, mdl-spinner__layer-4-fade-in-out 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both; } + +@-webkit-keyframes mdl-spinner__fill-unfill-rotate { + 12.5% { + -webkit-transform: rotate(135deg); + transform: rotate(135deg); } + 25% { + -webkit-transform: rotate(270deg); + transform: rotate(270deg); } + 37.5% { + -webkit-transform: rotate(405deg); + transform: rotate(405deg); } + 50% { + -webkit-transform: rotate(540deg); + transform: rotate(540deg); } + 62.5% { + -webkit-transform: rotate(675deg); + transform: rotate(675deg); } + 75% { + -webkit-transform: rotate(810deg); + transform: rotate(810deg); } + 87.5% { + -webkit-transform: rotate(945deg); + transform: rotate(945deg); } + to { + -webkit-transform: rotate(1080deg); + transform: rotate(1080deg); } } + +@keyframes mdl-spinner__fill-unfill-rotate { + 12.5% { + -webkit-transform: rotate(135deg); + transform: rotate(135deg); } + 25% { + -webkit-transform: rotate(270deg); + transform: rotate(270deg); } + 37.5% { + -webkit-transform: rotate(405deg); + transform: rotate(405deg); } + 50% { + -webkit-transform: rotate(540deg); + transform: rotate(540deg); } + 62.5% { + -webkit-transform: rotate(675deg); + transform: rotate(675deg); } + 75% { + -webkit-transform: rotate(810deg); + transform: rotate(810deg); } + 87.5% { + -webkit-transform: rotate(945deg); + transform: rotate(945deg); } + to { + -webkit-transform: rotate(1080deg); + transform: rotate(1080deg); } } + +/** +* HACK: Even though the intention is to have the current .mdl-spinner__layer-N +* at `opacity: 1`, we set it to `opacity: 0.99` instead since this forces Chrome +* to do proper subpixel rendering for the elements being animated. This is +* especially visible in Chrome 39 on Ubuntu 14.04. See: +* +* - https://github.com/Polymer/paper-spinner/issues/9 +* - https://code.google.com/p/chromium/issues/detail?id=436255 +*/ +@-webkit-keyframes mdl-spinner__layer-1-fade-in-out { + from { + opacity: 0.99; } + 25% { + opacity: 0.99; } + 26% { + opacity: 0; } + 89% { + opacity: 0; } + 90% { + opacity: 0.99; } + 100% { + opacity: 0.99; } } +@keyframes mdl-spinner__layer-1-fade-in-out { + from { + opacity: 0.99; } + 25% { + opacity: 0.99; } + 26% { + opacity: 0; } + 89% { + opacity: 0; } + 90% { + opacity: 0.99; } + 100% { + opacity: 0.99; } } + +@-webkit-keyframes mdl-spinner__layer-2-fade-in-out { + from { + opacity: 0; } + 15% { + opacity: 0; } + 25% { + opacity: 0.99; } + 50% { + opacity: 0.99; } + 51% { + opacity: 0; } } + +@keyframes mdl-spinner__layer-2-fade-in-out { + from { + opacity: 0; } + 15% { + opacity: 0; } + 25% { + opacity: 0.99; } + 50% { + opacity: 0.99; } + 51% { + opacity: 0; } } + +@-webkit-keyframes mdl-spinner__layer-3-fade-in-out { + from { + opacity: 0; } + 40% { + opacity: 0; } + 50% { + opacity: 0.99; } + 75% { + opacity: 0.99; } + 76% { + opacity: 0; } } + +@keyframes mdl-spinner__layer-3-fade-in-out { + from { + opacity: 0; } + 40% { + opacity: 0; } + 50% { + opacity: 0.99; } + 75% { + opacity: 0.99; } + 76% { + opacity: 0; } } + +@-webkit-keyframes mdl-spinner__layer-4-fade-in-out { + from { + opacity: 0; } + 65% { + opacity: 0; } + 75% { + opacity: 0.99; } + 90% { + opacity: 0.99; } + 100% { + opacity: 0; } } + +@keyframes mdl-spinner__layer-4-fade-in-out { + from { + opacity: 0; } + 65% { + opacity: 0; } + 75% { + opacity: 0.99; } + 90% { + opacity: 0.99; } + 100% { + opacity: 0; } } + +/** +* Patch the gap that appear between the two adjacent +* div.mdl-spinner__circle-clipper while the spinner is rotating +* (appears on Chrome 38, Safari 7.1, and IE 11). +* +* Update: the gap no longer appears on Chrome when .mdl-spinner__layer-N's +* opacity is 0.99, but still does on Safari and IE. +*/ +.mdl-spinner__gap-patch { + position: absolute; + box-sizing: border-box; + top: 0; + left: 45%; + width: 10%; + height: 100%; + overflow: hidden; + border-color: inherit; } + .mdl-spinner__gap-patch .mdl-spinner__circle { + width: 1000%; + left: -450%; } + +.mdl-spinner__circle-clipper { + display: inline-block; + position: relative; + width: 50%; + height: 100%; + overflow: hidden; + border-color: inherit; } + .mdl-spinner__circle-clipper .mdl-spinner__circle { + width: 200%; } + +.mdl-spinner__circle { + box-sizing: border-box; + height: 100%; + border-width: 3px; + border-style: solid; + border-color: inherit; + border-bottom-color: transparent !important; + border-radius: 50%; + -webkit-animation: none; + animation: none; + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; } + .mdl-spinner__left .mdl-spinner__circle { + border-right-color: transparent !important; + -webkit-transform: rotate(129deg); + -ms-transform: rotate(129deg); + transform: rotate(129deg); } + .mdl-spinner.is-active .mdl-spinner__left .mdl-spinner__circle { + -webkit-animation: mdl-spinner__left-spin 1333ms cubic-bezier(0.4, 0, 0.2, 1) infinite both; + animation: mdl-spinner__left-spin 1333ms cubic-bezier(0.4, 0, 0.2, 1) infinite both; } + .mdl-spinner__right .mdl-spinner__circle { + left: -100%; + border-left-color: transparent !important; + -webkit-transform: rotate(-129deg); + -ms-transform: rotate(-129deg); + transform: rotate(-129deg); } + .mdl-spinner.is-active .mdl-spinner__right .mdl-spinner__circle { + -webkit-animation: mdl-spinner__right-spin 1333ms cubic-bezier(0.4, 0, 0.2, 1) infinite both; + animation: mdl-spinner__right-spin 1333ms cubic-bezier(0.4, 0, 0.2, 1) infinite both; } + +@-webkit-keyframes mdl-spinner__left-spin { + from { + -webkit-transform: rotate(130deg); + transform: rotate(130deg); } + 50% { + -webkit-transform: rotate(-5deg); + transform: rotate(-5deg); } + to { + -webkit-transform: rotate(130deg); + transform: rotate(130deg); } } + +@keyframes mdl-spinner__left-spin { + from { + -webkit-transform: rotate(130deg); + transform: rotate(130deg); } + 50% { + -webkit-transform: rotate(-5deg); + transform: rotate(-5deg); } + to { + -webkit-transform: rotate(130deg); + transform: rotate(130deg); } } + +@-webkit-keyframes mdl-spinner__right-spin { + from { + -webkit-transform: rotate(-130deg); + transform: rotate(-130deg); } + 50% { + -webkit-transform: rotate(5deg); + transform: rotate(5deg); } + to { + -webkit-transform: rotate(-130deg); + transform: rotate(-130deg); } } + +@keyframes mdl-spinner__right-spin { + from { + -webkit-transform: rotate(-130deg); + transform: rotate(-130deg); } + 50% { + -webkit-transform: rotate(5deg); + transform: rotate(5deg); } + to { + -webkit-transform: rotate(-130deg); + transform: rotate(-130deg); } } + +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/*------------------------------------* $CONTENTS +\*------------------------------------*/ +/** + * STYLE GUIDE VARIABLES------------------Declarations of Sass variables + * -----Typography + * -----Colors + * -----Textfield + * -----Switch + * -----Spinner + * -----Radio + * -----Menu + * -----List + * -----Layout + * -----Icon toggles + * -----Footer + * -----Column + * -----Checkbox + * -----Card + * -----Button + * -----Animation + * -----Progress + * -----Badge + * -----Shadows + * -----Grid + * -----Data table + */ +/* ========== TYPOGRAPHY ========== */ +/* We're splitting fonts into "preferred" and "performance" in order to optimize + page loading. For important text, such as the body, we want it to load + immediately and not wait for the web font load, whereas for other sections, + such as headers and titles, we're OK with things taking a bit longer to load. + We do have some optional classes and parameters in the mixins, in case you + definitely want to make sure you're using the preferred font and don't mind + the performance hit. + We should be able to improve on this once CSS Font Loading L3 becomes more + widely available. +*/ +/* ========== COLORS ========== */ +/** +* +* Material design color palettes. +* @see http://www.google.com/design/spec/style/color.html +* +**/ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* ========== Color Palettes ========== */ +/* colors.scss */ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* ========== IMAGES ========== */ +/* ========== Color & Themes ========== */ +/* ========== Typography ========== */ +/* ========== Components ========== */ +/* ========== Standard Buttons ========== */ +/* ========== Icon Toggles ========== */ +/* ========== Radio Buttons ========== */ +/* ========== Ripple effect ========== */ +/* ========== Layout ========== */ +/* ========== Content Tabs ========== */ +/* ========== Checkboxes ========== */ +/* ========== Switches ========== */ +/* ========== Spinner ========== */ +/* ========== Text fields ========== */ +/* ========== Card ========== */ +/* ========== Sliders ========== */ +/* ========== Progress ========== */ +/* ========== List ========== */ +/* ========== Item ========== */ +/* ========== Dropdown menu ========== */ +/* ========== Tooltips ========== */ +/* ========== Footer ========== */ +/* TEXTFIELD */ +/* SWITCH */ +/* SPINNER */ +/* RADIO */ +/* MENU */ +/* LIST */ +/* LAYOUT */ +/* ICON TOGGLE */ +/* FOOTER */ +/*mega-footer*/ +/*mini-footer*/ +/* CHECKBOX */ +/* CARD */ +/* Card dimensions */ +/* Cover image */ +/* BUTTON */ +/** + * + * Dimensions + * + */ +/* ANIMATION */ +/* PROGRESS */ +/* BADGE */ +/* SHADOWS */ +/* GRID */ +/* DATA TABLE */ +/* TOOLTIP */ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* Typography */ +/* Shadows */ +/* Animations */ +.mdl-switch { + position: relative; + z-index: 1; + vertical-align: middle; + display: inline-block; + box-sizing: border-box; + width: 100%; + height: 24px; + margin: 0; + padding: 0; + overflow: visible; + -webkit-touch-callout: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; } + .mdl-switch.is-upgraded { + padding-left: 28px; } + +.mdl-switch__input { + line-height: 24px; } + .mdl-switch.is-upgraded .mdl-switch__input { + position: absolute; + width: 0; + height: 0; + margin: 0; + padding: 0; + opacity: 0; + -ms-appearance: none; + -moz-appearance: none; + -webkit-appearance: none; + appearance: none; + border: none; } + +.mdl-switch__track { + background: rgba(0,0,0, 0.26); + position: absolute; + left: 0; + top: 5px; + height: 14px; + width: 36px; + border-radius: 14px; + cursor: pointer; } + .mdl-switch.is-checked .mdl-switch__track { + background: rgba(63,81,181, 0.5); } + .mdl-switch.is-disabled .mdl-switch__track { + background: rgba(0,0,0, 0.12); + cursor: auto; } + +.mdl-switch__thumb { + background: rgb(250,250,250); + position: absolute; + left: 0; + top: 2px; + height: 20px; + width: 20px; + border-radius: 50%; + cursor: pointer; + box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12); + -webkit-transition-duration: 0.28s; + transition-duration: 0.28s; + -webkit-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + -webkit-transition-property: left; + transition-property: left; } + .mdl-switch.is-checked .mdl-switch__thumb { + background: rgb(63,81,181); + left: 16px; + box-shadow: 0 3px 4px 0 rgba(0, 0, 0, 0.14), 0 3px 3px -2px rgba(0, 0, 0, 0.2), 0 1px 8px 0 rgba(0, 0, 0, 0.12); } + .mdl-switch.is-disabled .mdl-switch__thumb { + background: rgb(189,189,189); + cursor: auto; } + +.mdl-switch__focus-helper { + position: absolute; + top: 50%; + left: 50%; + -webkit-transform: translate(-4px, -4px); + -ms-transform: translate(-4px, -4px); + transform: translate(-4px, -4px); + display: inline-block; + box-sizing: border-box; + width: 8px; + height: 8px; + border-radius: 50%; + background-color: transparent; } + .mdl-switch.is-focused .mdl-switch__focus-helper { + box-shadow: 0 0 0px 20px rgba(0, 0, 0, 0.1); + background-color: rgba(0, 0, 0, 0.1); } + .mdl-switch.is-focused.is-checked .mdl-switch__focus-helper { + box-shadow: 0 0 0px 20px rgba(63,81,181, 0.26); + background-color: rgba(63,81,181, 0.26); } + +.mdl-switch__label { + position: relative; + cursor: pointer; + font-size: 16px; + line-height: 24px; + margin: 0; + left: 24px; } + .mdl-switch.is-disabled .mdl-switch__label { + color: rgb(189,189,189); + cursor: auto; } + +.mdl-switch__ripple-container { + position: absolute; + z-index: 2; + top: -12px; + left: -14px; + box-sizing: border-box; + width: 48px; + height: 48px; + border-radius: 50%; + cursor: pointer; + overflow: hidden; + -webkit-mask-image: -webkit-radial-gradient(circle, white, black); + -webkit-transition-duration: 0.40s; + transition-duration: 0.40s; + -webkit-transition-timing-function: step-end; + transition-timing-function: step-end; + -webkit-transition-property: left; + transition-property: left; } + .mdl-switch__ripple-container .mdl-ripple { + background: rgb(63,81,181); } + .mdl-switch.is-disabled .mdl-switch__ripple-container { + cursor: auto; } + .mdl-switch.is-disabled .mdl-switch__ripple-container .mdl-ripple { + background: transparent; } + .mdl-switch.is-checked .mdl-switch__ripple-container { + cursor: auto; + left: 2px; } + +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/*------------------------------------* $CONTENTS +\*------------------------------------*/ +/** + * STYLE GUIDE VARIABLES------------------Declarations of Sass variables + * -----Typography + * -----Colors + * -----Textfield + * -----Switch + * -----Spinner + * -----Radio + * -----Menu + * -----List + * -----Layout + * -----Icon toggles + * -----Footer + * -----Column + * -----Checkbox + * -----Card + * -----Button + * -----Animation + * -----Progress + * -----Badge + * -----Shadows + * -----Grid + * -----Data table + */ +/* ========== TYPOGRAPHY ========== */ +/* We're splitting fonts into "preferred" and "performance" in order to optimize + page loading. For important text, such as the body, we want it to load + immediately and not wait for the web font load, whereas for other sections, + such as headers and titles, we're OK with things taking a bit longer to load. + We do have some optional classes and parameters in the mixins, in case you + definitely want to make sure you're using the preferred font and don't mind + the performance hit. + We should be able to improve on this once CSS Font Loading L3 becomes more + widely available. +*/ +/* ========== COLORS ========== */ +/** +* +* Material design color palettes. +* @see http://www.google.com/design/spec/style/color.html +* +**/ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* ========== Color Palettes ========== */ +/* colors.scss */ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* ========== IMAGES ========== */ +/* ========== Color & Themes ========== */ +/* ========== Typography ========== */ +/* ========== Components ========== */ +/* ========== Standard Buttons ========== */ +/* ========== Icon Toggles ========== */ +/* ========== Radio Buttons ========== */ +/* ========== Ripple effect ========== */ +/* ========== Layout ========== */ +/* ========== Content Tabs ========== */ +/* ========== Checkboxes ========== */ +/* ========== Switches ========== */ +/* ========== Spinner ========== */ +/* ========== Text fields ========== */ +/* ========== Card ========== */ +/* ========== Sliders ========== */ +/* ========== Progress ========== */ +/* ========== List ========== */ +/* ========== Item ========== */ +/* ========== Dropdown menu ========== */ +/* ========== Tooltips ========== */ +/* ========== Footer ========== */ +/* TEXTFIELD */ +/* SWITCH */ +/* SPINNER */ +/* RADIO */ +/* MENU */ +/* LIST */ +/* LAYOUT */ +/* ICON TOGGLE */ +/* FOOTER */ +/*mega-footer*/ +/*mini-footer*/ +/* CHECKBOX */ +/* CARD */ +/* Card dimensions */ +/* Cover image */ +/* BUTTON */ +/** + * + * Dimensions + * + */ +/* ANIMATION */ +/* PROGRESS */ +/* BADGE */ +/* SHADOWS */ +/* GRID */ +/* DATA TABLE */ +/* TOOLTIP */ +.mdl-tabs { + display: block; + width: 100%; } + +.mdl-tabs__tab-bar { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-box-orient: horizontal; + -webkit-box-direction: normal; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-box-pack: center; + -webkit-justify-content: center; + -ms-flex-pack: center; + justify-content: center; + -webkit-align-content: space-between; + -ms-flex-line-pack: justify; + align-content: space-between; + -webkit-box-align: start; + -webkit-align-items: flex-start; + -ms-flex-align: start; + align-items: flex-start; + height: 48px; + padding: 0 0 0 0; + margin: 0; + border-bottom: 1px solid rgb(224,224,224); } + +.mdl-tabs__tab { + margin: 0; + border: none; + padding: 0 24px 0 24px; + float: left; + position: relative; + display: block; + color: red; + text-decoration: none; + height: 48px; + line-height: 48px; + text-align: center; + font-weight: 500; + font-size: 14px; + text-transform: uppercase; + color: rgba(0,0,0, 0.54); + overflow: hidden; } + .mdl-tabs.is-upgraded .mdl-tabs__tab.is-active { + color: rgba(0,0,0, 0.87); } + .mdl-tabs.is-upgraded .mdl-tabs__tab.is-active:after { + height: 2px; + width: 100%; + display: block; + content: " "; + bottom: 0px; + left: 0px; + position: absolute; + background: rgb(63,81,181); + -webkit-animation: border-expand 0.2s cubic-bezier(0.4, 0, 0.4, 1) 0.01s alternate forwards; + animation: border-expand 0.2s cubic-bezier(0.4, 0, 0.4, 1) 0.01s alternate forwards; + -webkit-transition: all 1s cubic-bezier(0.4, 0, 1, 1); + transition: all 1s cubic-bezier(0.4, 0, 1, 1); } + .mdl-tabs__tab .mdl-tabs__ripple-container { + display: block; + position: absolute; + height: 100%; + width: 100%; + left: 0px; + top: 0px; + z-index: 1; + overflow: hidden; } + .mdl-tabs__tab .mdl-tabs__ripple-container .mdl-ripple { + background: rgb(63,81,181); } + +.mdl-tabs__panel { + display: block; } + .mdl-tabs.is-upgraded .mdl-tabs__panel { + display: none; } + .mdl-tabs.is-upgraded .mdl-tabs__panel.is-active { + display: block; } + +@-webkit-keyframes border-expand { + 0% { + opacity: 0; + width: 0; } + 100% { + opacity: 1; + width: 100%; } } + +@keyframes border-expand { + 0% { + opacity: 0; + width: 0; } + 100% { + opacity: 1; + width: 100%; } } + +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/*------------------------------------* $CONTENTS +\*------------------------------------*/ +/** + * STYLE GUIDE VARIABLES------------------Declarations of Sass variables + * -----Typography + * -----Colors + * -----Textfield + * -----Switch + * -----Spinner + * -----Radio + * -----Menu + * -----List + * -----Layout + * -----Icon toggles + * -----Footer + * -----Column + * -----Checkbox + * -----Card + * -----Button + * -----Animation + * -----Progress + * -----Badge + * -----Shadows + * -----Grid + * -----Data table + */ +/* ========== TYPOGRAPHY ========== */ +/* We're splitting fonts into "preferred" and "performance" in order to optimize + page loading. For important text, such as the body, we want it to load + immediately and not wait for the web font load, whereas for other sections, + such as headers and titles, we're OK with things taking a bit longer to load. + We do have some optional classes and parameters in the mixins, in case you + definitely want to make sure you're using the preferred font and don't mind + the performance hit. + We should be able to improve on this once CSS Font Loading L3 becomes more + widely available. +*/ +/* ========== COLORS ========== */ +/** +* +* Material design color palettes. +* @see http://www.google.com/design/spec/style/color.html +* +**/ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* ========== Color Palettes ========== */ +/* colors.scss */ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* ========== IMAGES ========== */ +/* ========== Color & Themes ========== */ +/* ========== Typography ========== */ +/* ========== Components ========== */ +/* ========== Standard Buttons ========== */ +/* ========== Icon Toggles ========== */ +/* ========== Radio Buttons ========== */ +/* ========== Ripple effect ========== */ +/* ========== Layout ========== */ +/* ========== Content Tabs ========== */ +/* ========== Checkboxes ========== */ +/* ========== Switches ========== */ +/* ========== Spinner ========== */ +/* ========== Text fields ========== */ +/* ========== Card ========== */ +/* ========== Sliders ========== */ +/* ========== Progress ========== */ +/* ========== List ========== */ +/* ========== Item ========== */ +/* ========== Dropdown menu ========== */ +/* ========== Tooltips ========== */ +/* ========== Footer ========== */ +/* TEXTFIELD */ +/* SWITCH */ +/* SPINNER */ +/* RADIO */ +/* MENU */ +/* LIST */ +/* LAYOUT */ +/* ICON TOGGLE */ +/* FOOTER */ +/*mega-footer*/ +/*mini-footer*/ +/* CHECKBOX */ +/* CARD */ +/* Card dimensions */ +/* Cover image */ +/* BUTTON */ +/** + * + * Dimensions + * + */ +/* ANIMATION */ +/* PROGRESS */ +/* BADGE */ +/* SHADOWS */ +/* GRID */ +/* DATA TABLE */ +/* TOOLTIP */ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* Typography */ +/* Shadows */ +/* Animations */ +.mdl-textfield { + position: relative; + font-size: 16px; + display: inline-block; + box-sizing: border-box; + width: 300px; + max-width: 100%; + margin: 0; + padding: 20px 0; } + .mdl-textfield .mdl-button { + position: absolute; + bottom: 20px; } + +.mdl-textfield--align-right { + text-align: right; } + +.mdl-textfield--full-width { + width: 100%; } + +.mdl-textfield--expandable { + min-width: 32px; + width: auto; + min-height: 32px; } + +.mdl-textfield__input { + border: none; + border-bottom: 1px solid rgba(0,0,0, 0.12); + display: block; + font-size: 16px; + margin: 0; + padding: 4px 0; + width: 100%; + background: none; + text-align: left; + color: inherit; } + .mdl-textfield.is-focused .mdl-textfield__input { + outline: none; } + .mdl-textfield.is-invalid .mdl-textfield__input { + border-color: rgb(222, 50, 38); + box-shadow: none; } + .mdl-textfield.is-disabled .mdl-textfield__input { + background-color: transparent; + border-bottom: 1px dotted rgba(0,0,0, 0.12); + color: rgba(0,0,0, 0.26); } + +.mdl-textfield textarea.mdl-textfield__input { + display: block; } + +.mdl-textfield__label { + bottom: 0; + color: rgba(0,0,0, 0.26); + font-size: 16px; + left: 0; + right: 0; + pointer-events: none; + position: absolute; + display: block; + top: 24px; + width: 100%; + overflow: hidden; + white-space: nowrap; + text-align: left; } + .mdl-textfield.is-dirty .mdl-textfield__label { + visibility: hidden; } + .mdl-textfield--floating-label .mdl-textfield__label { + -webkit-transition-duration: 0.2s; + transition-duration: 0.2s; + -webkit-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); } + .mdl-textfield.is-disabled.is-disabled .mdl-textfield__label { + color: rgba(0,0,0, 0.26); } + .mdl-textfield--floating-label.is-focused .mdl-textfield__label, + .mdl-textfield--floating-label.is-dirty .mdl-textfield__label { + color: rgb(63,81,181); + font-size: 12px; + top: 4px; + visibility: visible; } + .mdl-textfield--floating-label.is-focused .mdl-textfield__expandable-holder .mdl-textfield__label, + .mdl-textfield--floating-label.is-dirty .mdl-textfield__expandable-holder .mdl-textfield__label { + top: -16px; } + .mdl-textfield--floating-label.is-invalid .mdl-textfield__label { + color: rgb(222, 50, 38); + font-size: 12px; } + .mdl-textfield__label:after { + background-color: rgb(63,81,181); + bottom: 20px; + content: ''; + height: 2px; + left: 45%; + position: absolute; + -webkit-transition-duration: 0.2s; + transition-duration: 0.2s; + -webkit-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + visibility: hidden; + width: 10px; } + .mdl-textfield.is-focused .mdl-textfield__label:after { + left: 0; + visibility: visible; + width: 100%; } + .mdl-textfield.is-invalid .mdl-textfield__label:after { + background-color: rgb(222, 50, 38); } + +.mdl-textfield__error { + color: rgb(222, 50, 38); + position: absolute; + font-size: 12px; + margin-top: 3px; + visibility: hidden; + display: block; } + .mdl-textfield.is-invalid .mdl-textfield__error { + visibility: visible; } + +.mdl-textfield__expandable-holder { + display: inline-block; + position: relative; + margin-left: 32px; + -webkit-transition-duration: 0.2s; + transition-duration: 0.2s; + -webkit-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + display: inline-block; + max-width: 0.1px; } + .mdl-textfield.is-focused .mdl-textfield__expandable-holder, .mdl-textfield.is-dirty .mdl-textfield__expandable-holder { + max-width: 600px; } + .mdl-textfield__expandable-holder .mdl-textfield__label:after { + bottom: 0; } + +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/*------------------------------------* $CONTENTS +\*------------------------------------*/ +/** + * STYLE GUIDE VARIABLES------------------Declarations of Sass variables + * -----Typography + * -----Colors + * -----Textfield + * -----Switch + * -----Spinner + * -----Radio + * -----Menu + * -----List + * -----Layout + * -----Icon toggles + * -----Footer + * -----Column + * -----Checkbox + * -----Card + * -----Button + * -----Animation + * -----Progress + * -----Badge + * -----Shadows + * -----Grid + * -----Data table + */ +/* ========== TYPOGRAPHY ========== */ +/* We're splitting fonts into "preferred" and "performance" in order to optimize + page loading. For important text, such as the body, we want it to load + immediately and not wait for the web font load, whereas for other sections, + such as headers and titles, we're OK with things taking a bit longer to load. + We do have some optional classes and parameters in the mixins, in case you + definitely want to make sure you're using the preferred font and don't mind + the performance hit. + We should be able to improve on this once CSS Font Loading L3 becomes more + widely available. +*/ +/* ========== COLORS ========== */ +/** +* +* Material design color palettes. +* @see http://www.google.com/design/spec/style/color.html +* +**/ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* ========== Color Palettes ========== */ +/* colors.scss */ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* ========== IMAGES ========== */ +/* ========== Color & Themes ========== */ +/* ========== Typography ========== */ +/* ========== Components ========== */ +/* ========== Standard Buttons ========== */ +/* ========== Icon Toggles ========== */ +/* ========== Radio Buttons ========== */ +/* ========== Ripple effect ========== */ +/* ========== Layout ========== */ +/* ========== Content Tabs ========== */ +/* ========== Checkboxes ========== */ +/* ========== Switches ========== */ +/* ========== Spinner ========== */ +/* ========== Text fields ========== */ +/* ========== Card ========== */ +/* ========== Sliders ========== */ +/* ========== Progress ========== */ +/* ========== List ========== */ +/* ========== Item ========== */ +/* ========== Dropdown menu ========== */ +/* ========== Tooltips ========== */ +/* ========== Footer ========== */ +/* TEXTFIELD */ +/* SWITCH */ +/* SPINNER */ +/* RADIO */ +/* MENU */ +/* LIST */ +/* LAYOUT */ +/* ICON TOGGLE */ +/* FOOTER */ +/*mega-footer*/ +/*mini-footer*/ +/* CHECKBOX */ +/* CARD */ +/* Card dimensions */ +/* Cover image */ +/* BUTTON */ +/** + * + * Dimensions + * + */ +/* ANIMATION */ +/* PROGRESS */ +/* BADGE */ +/* SHADOWS */ +/* GRID */ +/* DATA TABLE */ +/* TOOLTIP */ +.mdl-tooltip { + -webkit-transform: scale(0); + -ms-transform: scale(0); + transform: scale(0); + -webkit-transform-origin: top center; + -ms-transform-origin: top center; + transform-origin: top center; + will-change: transform; + z-index: 999; + background: rgba(97,97,97, 0.9); + border-radius: 2px; + color: rgb(255,255,255); + display: inline-block; + font-size: 10px; + font-weight: 500; + line-height: 14px; + max-width: 170px; + position: fixed; + top: -500px; + left: -500px; + padding: 8px; + text-align: center; } + +.mdl-tooltip.is-active { + -webkit-animation: pulse 200ms cubic-bezier(0, 0, 0.2, 1) forwards; + animation: pulse 200ms cubic-bezier(0, 0, 0.2, 1) forwards; } + +.mdl-tooltip--large { + line-height: 14px; + font-size: 14px; + padding: 16px; } + +@-webkit-keyframes pulse { + 0% { + -webkit-transform: scale(0); + transform: scale(0); + opacity: 0; } + 50% { + -webkit-transform: scale(0.99); + transform: scale(0.99); } + 100% { + -webkit-transform: scale(1); + transform: scale(1); + opacity: 1; + visibility: visible; } } + +@keyframes pulse { + 0% { + -webkit-transform: scale(0); + transform: scale(0); + opacity: 0; } + 50% { + -webkit-transform: scale(0.99); + transform: scale(0.99); } + 100% { + -webkit-transform: scale(1); + transform: scale(1); + opacity: 1; + visibility: visible; } } + +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/*------------------------------------* $CONTENTS +\*------------------------------------*/ +/** + * STYLE GUIDE VARIABLES------------------Declarations of Sass variables + * -----Typography + * -----Colors + * -----Textfield + * -----Switch + * -----Spinner + * -----Radio + * -----Menu + * -----List + * -----Layout + * -----Icon toggles + * -----Footer + * -----Column + * -----Checkbox + * -----Card + * -----Button + * -----Animation + * -----Progress + * -----Badge + * -----Shadows + * -----Grid + * -----Data table + */ +/* ========== TYPOGRAPHY ========== */ +/* We're splitting fonts into "preferred" and "performance" in order to optimize + page loading. For important text, such as the body, we want it to load + immediately and not wait for the web font load, whereas for other sections, + such as headers and titles, we're OK with things taking a bit longer to load. + We do have some optional classes and parameters in the mixins, in case you + definitely want to make sure you're using the preferred font and don't mind + the performance hit. + We should be able to improve on this once CSS Font Loading L3 becomes more + widely available. +*/ +/* ========== COLORS ========== */ +/** +* +* Material design color palettes. +* @see http://www.google.com/design/spec/style/color.html +* +**/ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* ========== Color Palettes ========== */ +/* colors.scss */ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* ========== IMAGES ========== */ +/* ========== Color & Themes ========== */ +/* ========== Typography ========== */ +/* ========== Components ========== */ +/* ========== Standard Buttons ========== */ +/* ========== Icon Toggles ========== */ +/* ========== Radio Buttons ========== */ +/* ========== Ripple effect ========== */ +/* ========== Layout ========== */ +/* ========== Content Tabs ========== */ +/* ========== Checkboxes ========== */ +/* ========== Switches ========== */ +/* ========== Spinner ========== */ +/* ========== Text fields ========== */ +/* ========== Card ========== */ +/* ========== Sliders ========== */ +/* ========== Progress ========== */ +/* ========== List ========== */ +/* ========== Item ========== */ +/* ========== Dropdown menu ========== */ +/* ========== Tooltips ========== */ +/* ========== Footer ========== */ +/* TEXTFIELD */ +/* SWITCH */ +/* SPINNER */ +/* RADIO */ +/* MENU */ +/* LIST */ +/* LAYOUT */ +/* ICON TOGGLE */ +/* FOOTER */ +/*mega-footer*/ +/*mini-footer*/ +/* CHECKBOX */ +/* CARD */ +/* Card dimensions */ +/* Cover image */ +/* BUTTON */ +/** + * + * Dimensions + * + */ +/* ANIMATION */ +/* PROGRESS */ +/* BADGE */ +/* SHADOWS */ +/* GRID */ +/* DATA TABLE */ +/* TOOLTIP */ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* Typography */ +/* Shadows */ +/* Animations */ +.mdl-shadow--2dp { + box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12); } + +.mdl-shadow--3dp { + box-shadow: 0 3px 4px 0 rgba(0, 0, 0, 0.14), 0 3px 3px -2px rgba(0, 0, 0, 0.2), 0 1px 8px 0 rgba(0, 0, 0, 0.12); } + +.mdl-shadow--4dp { + box-shadow: 0 4px 5px 0 rgba(0, 0, 0, 0.14), 0 1px 10px 0 rgba(0, 0, 0, 0.12), 0 2px 4px -1px rgba(0, 0, 0, 0.2); } + +.mdl-shadow--6dp { + box-shadow: 0 6px 10px 0 rgba(0, 0, 0, 0.14), 0 1px 18px 0 rgba(0, 0, 0, 0.12), 0 3px 5px -1px rgba(0, 0, 0, 0.2); } + +.mdl-shadow--8dp { + box-shadow: 0 8px 10px 1px rgba(0, 0, 0, 0.14), 0 3px 14px 2px rgba(0, 0, 0, 0.12), 0 5px 5px -3px rgba(0, 0, 0, 0.2); } + +.mdl-shadow--16dp { + box-shadow: 0 16px 24px 2px rgba(0, 0, 0, 0.14), 0 6px 30px 5px rgba(0, 0, 0, 0.12), 0 8px 10px -5px rgba(0, 0, 0, 0.2); } + +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* +* NOTE: Some rules here are applied using duplicate selectors. +* This is on purpose to increase their specificity when applied. +* For example: `.mdl-cell--1-col-phone.mdl-cell--1-col-phone` +*/ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/*------------------------------------* $CONTENTS +\*------------------------------------*/ +/** + * STYLE GUIDE VARIABLES------------------Declarations of Sass variables + * -----Typography + * -----Colors + * -----Textfield + * -----Switch + * -----Spinner + * -----Radio + * -----Menu + * -----List + * -----Layout + * -----Icon toggles + * -----Footer + * -----Column + * -----Checkbox + * -----Card + * -----Button + * -----Animation + * -----Progress + * -----Badge + * -----Shadows + * -----Grid + * -----Data table + */ +/* ========== TYPOGRAPHY ========== */ +/* We're splitting fonts into "preferred" and "performance" in order to optimize + page loading. For important text, such as the body, we want it to load + immediately and not wait for the web font load, whereas for other sections, + such as headers and titles, we're OK with things taking a bit longer to load. + We do have some optional classes and parameters in the mixins, in case you + definitely want to make sure you're using the preferred font and don't mind + the performance hit. + We should be able to improve on this once CSS Font Loading L3 becomes more + widely available. +*/ +/* ========== COLORS ========== */ +/** +* +* Material design color palettes. +* @see http://www.google.com/design/spec/style/color.html +* +**/ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* ========== Color Palettes ========== */ +/* colors.scss */ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* ========== IMAGES ========== */ +/* ========== Color & Themes ========== */ +/* ========== Typography ========== */ +/* ========== Components ========== */ +/* ========== Standard Buttons ========== */ +/* ========== Icon Toggles ========== */ +/* ========== Radio Buttons ========== */ +/* ========== Ripple effect ========== */ +/* ========== Layout ========== */ +/* ========== Content Tabs ========== */ +/* ========== Checkboxes ========== */ +/* ========== Switches ========== */ +/* ========== Spinner ========== */ +/* ========== Text fields ========== */ +/* ========== Card ========== */ +/* ========== Sliders ========== */ +/* ========== Progress ========== */ +/* ========== List ========== */ +/* ========== Item ========== */ +/* ========== Dropdown menu ========== */ +/* ========== Tooltips ========== */ +/* ========== Footer ========== */ +/* TEXTFIELD */ +/* SWITCH */ +/* SPINNER */ +/* RADIO */ +/* MENU */ +/* LIST */ +/* LAYOUT */ +/* ICON TOGGLE */ +/* FOOTER */ +/*mega-footer*/ +/*mini-footer*/ +/* CHECKBOX */ +/* CARD */ +/* Card dimensions */ +/* Cover image */ +/* BUTTON */ +/** + * + * Dimensions + * + */ +/* ANIMATION */ +/* PROGRESS */ +/* BADGE */ +/* SHADOWS */ +/* GRID */ +/* DATA TABLE */ +/* TOOLTIP */ +.mdl-grid { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-flow: row wrap; + -ms-flex-flow: row wrap; + flex-flow: row wrap; + margin: 0 auto 0 auto; + -webkit-box-align: stretch; + -webkit-align-items: stretch; + -ms-flex-align: stretch; + align-items: stretch; } + .mdl-grid.mdl-grid--no-spacing { + padding: 0; } + +.mdl-cell { + box-sizing: border-box; } + +.mdl-cell--top { + -webkit-align-self: flex-start; + -ms-flex-item-align: start; + align-self: flex-start; } + +.mdl-cell--middle { + -webkit-align-self: center; + -ms-flex-item-align: center; + align-self: center; } + +.mdl-cell--bottom { + -webkit-align-self: flex-end; + -ms-flex-item-align: end; + align-self: flex-end; } + +.mdl-cell--stretch { + -webkit-align-self: stretch; + -ms-flex-item-align: stretch; + align-self: stretch; } + +.mdl-grid.mdl-grid--no-spacing > .mdl-cell { + margin: 0; } + +@media (max-width: 479px) { + .mdl-grid { + padding: 8px; } + .mdl-cell { + margin: 8px; + width: calc(100% - 16px); } + .mdl-grid--no-spacing > .mdl-cell { + width: 100%; } + .mdl-cell--hide-phone { + display: none !important; } + .mdl-cell--1-col, + .mdl-cell--1-col-phone.mdl-cell--1-col-phone { + width: calc(25% - 16px); } + .mdl-grid--no-spacing > .mdl-cell--1-col, .mdl-grid--no-spacing > + .mdl-cell--1-col-phone.mdl-cell--1-col-phone { + width: 25%; } + .mdl-cell--2-col, + .mdl-cell--2-col-phone.mdl-cell--2-col-phone { + width: calc(50% - 16px); } + .mdl-grid--no-spacing > .mdl-cell--2-col, .mdl-grid--no-spacing > + .mdl-cell--2-col-phone.mdl-cell--2-col-phone { + width: 50%; } + .mdl-cell--3-col, + .mdl-cell--3-col-phone.mdl-cell--3-col-phone { + width: calc(75% - 16px); } + .mdl-grid--no-spacing > .mdl-cell--3-col, .mdl-grid--no-spacing > + .mdl-cell--3-col-phone.mdl-cell--3-col-phone { + width: 75%; } + .mdl-cell--4-col, + .mdl-cell--4-col-phone.mdl-cell--4-col-phone { + width: calc(100% - 16px); } + .mdl-grid--no-spacing > .mdl-cell--4-col, .mdl-grid--no-spacing > + .mdl-cell--4-col-phone.mdl-cell--4-col-phone { + width: 100%; } + .mdl-cell--5-col, + .mdl-cell--5-col-phone.mdl-cell--5-col-phone { + width: calc(100% - 16px); } + .mdl-grid--no-spacing > .mdl-cell--5-col, .mdl-grid--no-spacing > + .mdl-cell--5-col-phone.mdl-cell--5-col-phone { + width: 100%; } + .mdl-cell--6-col, + .mdl-cell--6-col-phone.mdl-cell--6-col-phone { + width: calc(100% - 16px); } + .mdl-grid--no-spacing > .mdl-cell--6-col, .mdl-grid--no-spacing > + .mdl-cell--6-col-phone.mdl-cell--6-col-phone { + width: 100%; } + .mdl-cell--7-col, + .mdl-cell--7-col-phone.mdl-cell--7-col-phone { + width: calc(100% - 16px); } + .mdl-grid--no-spacing > .mdl-cell--7-col, .mdl-grid--no-spacing > + .mdl-cell--7-col-phone.mdl-cell--7-col-phone { + width: 100%; } + .mdl-cell--8-col, + .mdl-cell--8-col-phone.mdl-cell--8-col-phone { + width: calc(100% - 16px); } + .mdl-grid--no-spacing > .mdl-cell--8-col, .mdl-grid--no-spacing > + .mdl-cell--8-col-phone.mdl-cell--8-col-phone { + width: 100%; } + .mdl-cell--9-col, + .mdl-cell--9-col-phone.mdl-cell--9-col-phone { + width: calc(100% - 16px); } + .mdl-grid--no-spacing > .mdl-cell--9-col, .mdl-grid--no-spacing > + .mdl-cell--9-col-phone.mdl-cell--9-col-phone { + width: 100%; } + .mdl-cell--10-col, + .mdl-cell--10-col-phone.mdl-cell--10-col-phone { + width: calc(100% - 16px); } + .mdl-grid--no-spacing > .mdl-cell--10-col, .mdl-grid--no-spacing > + .mdl-cell--10-col-phone.mdl-cell--10-col-phone { + width: 100%; } + .mdl-cell--11-col, + .mdl-cell--11-col-phone.mdl-cell--11-col-phone { + width: calc(100% - 16px); } + .mdl-grid--no-spacing > .mdl-cell--11-col, .mdl-grid--no-spacing > + .mdl-cell--11-col-phone.mdl-cell--11-col-phone { + width: 100%; } + .mdl-cell--12-col, + .mdl-cell--12-col-phone.mdl-cell--12-col-phone { + width: calc(100% - 16px); } + .mdl-grid--no-spacing > .mdl-cell--12-col, .mdl-grid--no-spacing > + .mdl-cell--12-col-phone.mdl-cell--12-col-phone { + width: 100%; } } + +@media (min-width: 480px) and (max-width: 839px) { + .mdl-grid { + padding: 8px; } + .mdl-cell { + margin: 8px; + width: calc(50% - 16px); } + .mdl-grid--no-spacing > .mdl-cell { + width: 50%; } + .mdl-cell--hide-tablet { + display: none !important; } + .mdl-cell--1-col, + .mdl-cell--1-col-tablet.mdl-cell--1-col-tablet { + width: calc(12.5% - 16px); } + .mdl-grid--no-spacing > .mdl-cell--1-col, .mdl-grid--no-spacing > + .mdl-cell--1-col-tablet.mdl-cell--1-col-tablet { + width: 12.5%; } + .mdl-cell--2-col, + .mdl-cell--2-col-tablet.mdl-cell--2-col-tablet { + width: calc(25% - 16px); } + .mdl-grid--no-spacing > .mdl-cell--2-col, .mdl-grid--no-spacing > + .mdl-cell--2-col-tablet.mdl-cell--2-col-tablet { + width: 25%; } + .mdl-cell--3-col, + .mdl-cell--3-col-tablet.mdl-cell--3-col-tablet { + width: calc(37.5% - 16px); } + .mdl-grid--no-spacing > .mdl-cell--3-col, .mdl-grid--no-spacing > + .mdl-cell--3-col-tablet.mdl-cell--3-col-tablet { + width: 37.5%; } + .mdl-cell--4-col, + .mdl-cell--4-col-tablet.mdl-cell--4-col-tablet { + width: calc(50% - 16px); } + .mdl-grid--no-spacing > .mdl-cell--4-col, .mdl-grid--no-spacing > + .mdl-cell--4-col-tablet.mdl-cell--4-col-tablet { + width: 50%; } + .mdl-cell--5-col, + .mdl-cell--5-col-tablet.mdl-cell--5-col-tablet { + width: calc(62.5% - 16px); } + .mdl-grid--no-spacing > .mdl-cell--5-col, .mdl-grid--no-spacing > + .mdl-cell--5-col-tablet.mdl-cell--5-col-tablet { + width: 62.5%; } + .mdl-cell--6-col, + .mdl-cell--6-col-tablet.mdl-cell--6-col-tablet { + width: calc(75% - 16px); } + .mdl-grid--no-spacing > .mdl-cell--6-col, .mdl-grid--no-spacing > + .mdl-cell--6-col-tablet.mdl-cell--6-col-tablet { + width: 75%; } + .mdl-cell--7-col, + .mdl-cell--7-col-tablet.mdl-cell--7-col-tablet { + width: calc(87.5% - 16px); } + .mdl-grid--no-spacing > .mdl-cell--7-col, .mdl-grid--no-spacing > + .mdl-cell--7-col-tablet.mdl-cell--7-col-tablet { + width: 87.5%; } + .mdl-cell--8-col, + .mdl-cell--8-col-tablet.mdl-cell--8-col-tablet { + width: calc(100% - 16px); } + .mdl-grid--no-spacing > .mdl-cell--8-col, .mdl-grid--no-spacing > + .mdl-cell--8-col-tablet.mdl-cell--8-col-tablet { + width: 100%; } + .mdl-cell--9-col, + .mdl-cell--9-col-tablet.mdl-cell--9-col-tablet { + width: calc(100% - 16px); } + .mdl-grid--no-spacing > .mdl-cell--9-col, .mdl-grid--no-spacing > + .mdl-cell--9-col-tablet.mdl-cell--9-col-tablet { + width: 100%; } + .mdl-cell--10-col, + .mdl-cell--10-col-tablet.mdl-cell--10-col-tablet { + width: calc(100% - 16px); } + .mdl-grid--no-spacing > .mdl-cell--10-col, .mdl-grid--no-spacing > + .mdl-cell--10-col-tablet.mdl-cell--10-col-tablet { + width: 100%; } + .mdl-cell--11-col, + .mdl-cell--11-col-tablet.mdl-cell--11-col-tablet { + width: calc(100% - 16px); } + .mdl-grid--no-spacing > .mdl-cell--11-col, .mdl-grid--no-spacing > + .mdl-cell--11-col-tablet.mdl-cell--11-col-tablet { + width: 100%; } + .mdl-cell--12-col, + .mdl-cell--12-col-tablet.mdl-cell--12-col-tablet { + width: calc(100% - 16px); } + .mdl-grid--no-spacing > .mdl-cell--12-col, .mdl-grid--no-spacing > + .mdl-cell--12-col-tablet.mdl-cell--12-col-tablet { + width: 100%; } } + +@media (min-width: 840px) { + .mdl-grid { + padding: 8px; } + .mdl-cell { + margin: 8px; + width: calc(33.3333333333% - 16px); } + .mdl-grid--no-spacing > .mdl-cell { + width: 33.3333333333%; } + .mdl-cell--hide-desktop { + display: none !important; } + .mdl-cell--1-col, + .mdl-cell--1-col-desktop.mdl-cell--1-col-desktop { + width: calc(8.3333333333% - 16px); } + .mdl-grid--no-spacing > .mdl-cell--1-col, .mdl-grid--no-spacing > + .mdl-cell--1-col-desktop.mdl-cell--1-col-desktop { + width: 8.3333333333%; } + .mdl-cell--2-col, + .mdl-cell--2-col-desktop.mdl-cell--2-col-desktop { + width: calc(16.6666666667% - 16px); } + .mdl-grid--no-spacing > .mdl-cell--2-col, .mdl-grid--no-spacing > + .mdl-cell--2-col-desktop.mdl-cell--2-col-desktop { + width: 16.6666666667%; } + .mdl-cell--3-col, + .mdl-cell--3-col-desktop.mdl-cell--3-col-desktop { + width: calc(25% - 16px); } + .mdl-grid--no-spacing > .mdl-cell--3-col, .mdl-grid--no-spacing > + .mdl-cell--3-col-desktop.mdl-cell--3-col-desktop { + width: 25%; } + .mdl-cell--4-col, + .mdl-cell--4-col-desktop.mdl-cell--4-col-desktop { + width: calc(33.3333333333% - 16px); } + .mdl-grid--no-spacing > .mdl-cell--4-col, .mdl-grid--no-spacing > + .mdl-cell--4-col-desktop.mdl-cell--4-col-desktop { + width: 33.3333333333%; } + .mdl-cell--5-col, + .mdl-cell--5-col-desktop.mdl-cell--5-col-desktop { + width: calc(41.6666666667% - 16px); } + .mdl-grid--no-spacing > .mdl-cell--5-col, .mdl-grid--no-spacing > + .mdl-cell--5-col-desktop.mdl-cell--5-col-desktop { + width: 41.6666666667%; } + .mdl-cell--6-col, + .mdl-cell--6-col-desktop.mdl-cell--6-col-desktop { + width: calc(50% - 16px); } + .mdl-grid--no-spacing > .mdl-cell--6-col, .mdl-grid--no-spacing > + .mdl-cell--6-col-desktop.mdl-cell--6-col-desktop { + width: 50%; } + .mdl-cell--7-col, + .mdl-cell--7-col-desktop.mdl-cell--7-col-desktop { + width: calc(58.3333333333% - 16px); } + .mdl-grid--no-spacing > .mdl-cell--7-col, .mdl-grid--no-spacing > + .mdl-cell--7-col-desktop.mdl-cell--7-col-desktop { + width: 58.3333333333%; } + .mdl-cell--8-col, + .mdl-cell--8-col-desktop.mdl-cell--8-col-desktop { + width: calc(66.6666666667% - 16px); } + .mdl-grid--no-spacing > .mdl-cell--8-col, .mdl-grid--no-spacing > + .mdl-cell--8-col-desktop.mdl-cell--8-col-desktop { + width: 66.6666666667%; } + .mdl-cell--9-col, + .mdl-cell--9-col-desktop.mdl-cell--9-col-desktop { + width: calc(75% - 16px); } + .mdl-grid--no-spacing > .mdl-cell--9-col, .mdl-grid--no-spacing > + .mdl-cell--9-col-desktop.mdl-cell--9-col-desktop { + width: 75%; } + .mdl-cell--10-col, + .mdl-cell--10-col-desktop.mdl-cell--10-col-desktop { + width: calc(83.3333333333% - 16px); } + .mdl-grid--no-spacing > .mdl-cell--10-col, .mdl-grid--no-spacing > + .mdl-cell--10-col-desktop.mdl-cell--10-col-desktop { + width: 83.3333333333%; } + .mdl-cell--11-col, + .mdl-cell--11-col-desktop.mdl-cell--11-col-desktop { + width: calc(91.6666666667% - 16px); } + .mdl-grid--no-spacing > .mdl-cell--11-col, .mdl-grid--no-spacing > + .mdl-cell--11-col-desktop.mdl-cell--11-col-desktop { + width: 91.6666666667%; } + .mdl-cell--12-col, + .mdl-cell--12-col-desktop.mdl-cell--12-col-desktop { + width: calc(100% - 16px); } + .mdl-grid--no-spacing > .mdl-cell--12-col, .mdl-grid--no-spacing > + .mdl-cell--12-col-desktop.mdl-cell--12-col-desktop { + width: 100%; } } diff --git a/examples/scalajs-play-core-react/server/src/main/assets/material/material.js b/examples/scalajs-play-core-react/server/src/main/assets/material/material.js new file mode 100644 index 0000000..c6d5c08 --- /dev/null +++ b/examples/scalajs-play-core-react/server/src/main/assets/material/material.js @@ -0,0 +1,3865 @@ +;(function() { +"use strict"; + +/** + * @license + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * A component handler interface using the revealing module design pattern. + * More details on this design pattern here: + * https://github.com/jasonmayes/mdl-component-design-pattern + * + * @author Jason Mayes. + */ +/* exported componentHandler */ + +// Pre-defining the componentHandler interface, for closure documentation and +// static verification. +var componentHandler = { + /** + * Searches existing DOM for elements of our component type and upgrades them + * if they have not already been upgraded. + * + * @param {string=} optJsClass the programatic name of the element class we + * need to create a new instance of. + * @param {string=} optCssClass the name of the CSS class elements of this + * type will have. + */ + upgradeDom: function(optJsClass, optCssClass) {}, + /** + * Upgrades a specific element rather than all in the DOM. + * + * @param {!Element} element The element we wish to upgrade. + * @param {string=} optJsClass Optional name of the class we want to upgrade + * the element to. + */ + upgradeElement: function(element, optJsClass) {}, + /** + * Upgrades a specific list of elements rather than all in the DOM. + * + * @param {!Element|!Array|!NodeList|!HTMLCollection} elements + * The elements we wish to upgrade. + */ + upgradeElements: function(elements) {}, + /** + * Upgrades all registered components found in the current DOM. This is + * automatically called on window load. + */ + upgradeAllRegistered: function() {}, + /** + * Allows user to be alerted to any upgrades that are performed for a given + * component type + * + * @param {string} jsClass The class name of the MDL component we wish + * to hook into for any upgrades performed. + * @param {function(!HTMLElement)} callback The function to call upon an + * upgrade. This function should expect 1 parameter - the HTMLElement which + * got upgraded. + */ + registerUpgradedCallback: function(jsClass, callback) {}, + /** + * Registers a class for future use and attempts to upgrade existing DOM. + * + * @param {componentHandler.ComponentConfigPublic} config the registration configuration + */ + register: function(config) {}, + /** + * Downgrade either a given node, an array of nodes, or a NodeList. + * + * @param {!Node|!Array|!NodeList} nodes + */ + downgradeElements: function(nodes) {} +}; + +componentHandler = (function() { + 'use strict'; + + /** @type {!Array} */ + var registeredComponents_ = []; + + /** @type {!Array} */ + var createdComponents_ = []; + + var downgradeMethod_ = 'mdlDowngrade'; + var componentConfigProperty_ = 'mdlComponentConfigInternal_'; + + /** + * Searches registered components for a class we are interested in using. + * Optionally replaces a match with passed object if specified. + * + * @param {string} name The name of a class we want to use. + * @param {componentHandler.ComponentConfig=} optReplace Optional object to replace match with. + * @return {!Object|boolean} + * @private + */ + function findRegisteredClass_(name, optReplace) { + for (var i = 0; i < registeredComponents_.length; i++) { + if (registeredComponents_[i].className === name) { + if (typeof optReplace !== 'undefined') { + registeredComponents_[i] = optReplace; + } + return registeredComponents_[i]; + } + } + return false; + } + + /** + * Returns an array of the classNames of the upgraded classes on the element. + * + * @param {!Element} element The element to fetch data from. + * @return {!Array} + * @private + */ + function getUpgradedListOfElement_(element) { + var dataUpgraded = element.getAttribute('data-upgraded'); + // Use `['']` as default value to conform the `,name,name...` style. + return dataUpgraded === null ? [''] : dataUpgraded.split(','); + } + + /** + * Returns true if the given element has already been upgraded for the given + * class. + * + * @param {!Element} element The element we want to check. + * @param {string} jsClass The class to check for. + * @returns {boolean} + * @private + */ + function isElementUpgraded_(element, jsClass) { + var upgradedList = getUpgradedListOfElement_(element); + return upgradedList.indexOf(jsClass) !== -1; + } + + /** + * Searches existing DOM for elements of our component type and upgrades them + * if they have not already been upgraded. + * + * @param {string=} optJsClass the programatic name of the element class we + * need to create a new instance of. + * @param {string=} optCssClass the name of the CSS class elements of this + * type will have. + */ + function upgradeDomInternal(optJsClass, optCssClass) { + if (typeof optJsClass === 'undefined' && + typeof optCssClass === 'undefined') { + for (var i = 0; i < registeredComponents_.length; i++) { + upgradeDomInternal(registeredComponents_[i].className, + registeredComponents_[i].cssClass); + } + } else { + var jsClass = /** @type {string} */ (optJsClass); + if (typeof optCssClass === 'undefined') { + var registeredClass = findRegisteredClass_(jsClass); + if (registeredClass) { + optCssClass = registeredClass.cssClass; + } + } + + var elements = document.querySelectorAll('.' + optCssClass); + for (var n = 0; n < elements.length; n++) { + upgradeElementInternal(elements[n], jsClass); + } + } + } + + /** + * Upgrades a specific element rather than all in the DOM. + * + * @param {!Element} element The element we wish to upgrade. + * @param {string=} optJsClass Optional name of the class we want to upgrade + * the element to. + */ + function upgradeElementInternal(element, optJsClass) { + // Verify argument type. + if (!(typeof element === 'object' && element instanceof Element)) { + throw new Error('Invalid argument provided to upgrade MDL element.'); + } + var upgradedList = getUpgradedListOfElement_(element); + var classesToUpgrade = []; + // If jsClass is not provided scan the registered components to find the + // ones matching the element's CSS classList. + if (!optJsClass) { + var classList = element.classList; + registeredComponents_.forEach(function(component) { + // Match CSS & Not to be upgraded & Not upgraded. + if (classList.contains(component.cssClass) && + classesToUpgrade.indexOf(component) === -1 && + !isElementUpgraded_(element, component.className)) { + classesToUpgrade.push(component); + } + }); + } else if (!isElementUpgraded_(element, optJsClass)) { + classesToUpgrade.push(findRegisteredClass_(optJsClass)); + } + + // Upgrade the element for each classes. + for (var i = 0, n = classesToUpgrade.length, registeredClass; i < n; i++) { + registeredClass = classesToUpgrade[i]; + if (registeredClass) { + // Mark element as upgraded. + upgradedList.push(registeredClass.className); + element.setAttribute('data-upgraded', upgradedList.join(',')); + var instance = new registeredClass.classConstructor(element); + instance[componentConfigProperty_] = registeredClass; + createdComponents_.push(instance); + // Call any callbacks the user has registered with this component type. + for (var j = 0, m = registeredClass.callbacks.length; j < m; j++) { + registeredClass.callbacks[j](element); + } + + if (registeredClass.widget) { + // Assign per element instance for control over API + element[registeredClass.className] = instance; + } + } else { + throw new Error( + 'Unable to find a registered component for the given class.'); + } + + var ev = document.createEvent('Events'); + ev.initEvent('mdl-componentupgraded', true, true); + element.dispatchEvent(ev); + } + } + + /** + * Upgrades a specific list of elements rather than all in the DOM. + * + * @param {!Element|!Array|!NodeList|!HTMLCollection} elements + * The elements we wish to upgrade. + */ + function upgradeElementsInternal(elements) { + if (!Array.isArray(elements)) { + if (typeof elements.item === 'function') { + elements = Array.prototype.slice.call(/** @type {Array} */ (elements)); + } else { + elements = [elements]; + } + } + for (var i = 0, n = elements.length, element; i < n; i++) { + element = elements[i]; + if (element instanceof HTMLElement) { + upgradeElementInternal(element); + if (element.children.length > 0) { + upgradeElementsInternal(element.children); + } + } + } + } + + /** + * Registers a class for future use and attempts to upgrade existing DOM. + * + * @param {componentHandler.ComponentConfigPublic} config + */ + function registerInternal(config) { + // In order to support both Closure-compiled and uncompiled code accessing + // this method, we need to allow for both the dot and array syntax for + // property access. You'll therefore see the `foo.bar || foo['bar']` + // pattern repeated across this method. + var widgetMissing = (typeof config.widget === 'undefined' && + typeof config['widget'] === 'undefined'); + var widget = true; + + if (!widgetMissing) { + widget = config.widget || config['widget']; + } + + var newConfig = /** @type {componentHandler.ComponentConfig} */ ({ + classConstructor: config.constructor || config['constructor'], + className: config.classAsString || config['classAsString'], + cssClass: config.cssClass || config['cssClass'], + widget: widget, + callbacks: [] + }); + + registeredComponents_.forEach(function(item) { + if (item.cssClass === newConfig.cssClass) { + throw new Error('The provided cssClass has already been registered: ' + item.cssClass); + } + if (item.className === newConfig.className) { + throw new Error('The provided className has already been registered'); + } + }); + + if (config.constructor.prototype + .hasOwnProperty(componentConfigProperty_)) { + throw new Error( + 'MDL component classes must not have ' + componentConfigProperty_ + + ' defined as a property.'); + } + + var found = findRegisteredClass_(config.classAsString, newConfig); + + if (!found) { + registeredComponents_.push(newConfig); + } + } + + /** + * Allows user to be alerted to any upgrades that are performed for a given + * component type + * + * @param {string} jsClass The class name of the MDL component we wish + * to hook into for any upgrades performed. + * @param {function(!HTMLElement)} callback The function to call upon an + * upgrade. This function should expect 1 parameter - the HTMLElement which + * got upgraded. + */ + function registerUpgradedCallbackInternal(jsClass, callback) { + var regClass = findRegisteredClass_(jsClass); + if (regClass) { + regClass.callbacks.push(callback); + } + } + + /** + * Upgrades all registered components found in the current DOM. This is + * automatically called on window load. + */ + function upgradeAllRegisteredInternal() { + for (var n = 0; n < registeredComponents_.length; n++) { + upgradeDomInternal(registeredComponents_[n].className); + } + } + + /** + * Finds a created component by a given DOM node. + * + * @param {!Node} node + * @return {*} + */ + function findCreatedComponentByNodeInternal(node) { + for (var n = 0; n < createdComponents_.length; n++) { + var component = createdComponents_[n]; + if (component.element_ === node) { + return component; + } + } + } + + /** + * Check the component for the downgrade method. + * Execute if found. + * Remove component from createdComponents list. + * + * @param {*} component + */ + function deconstructComponentInternal(component) { + if (component && + component[componentConfigProperty_] + .classConstructor.prototype + .hasOwnProperty(downgradeMethod_)) { + component[downgradeMethod_](); + var componentIndex = createdComponents_.indexOf(component); + createdComponents_.splice(componentIndex, 1); + + var upgrades = component.element_.getAttribute('data-upgraded').split(','); + var componentPlace = upgrades.indexOf( + component[componentConfigProperty_].classAsString); + upgrades.splice(componentPlace, 1); + component.element_.setAttribute('data-upgraded', upgrades.join(',')); + + var ev = document.createEvent('Events'); + ev.initEvent('mdl-componentdowngraded', true, true); + component.element_.dispatchEvent(ev); + } + } + + /** + * Downgrade either a given node, an array of nodes, or a NodeList. + * + * @param {!Node|!Array|!NodeList} nodes + */ + function downgradeNodesInternal(nodes) { + /** + * Auxiliary function to downgrade a single node. + * @param {!Node} node the node to be downgraded + */ + var downgradeNode = function(node) { + deconstructComponentInternal(findCreatedComponentByNodeInternal(node)); + }; + if (nodes instanceof Array || nodes instanceof NodeList) { + for (var n = 0; n < nodes.length; n++) { + downgradeNode(nodes[n]); + } + } else if (nodes instanceof Node) { + downgradeNode(nodes); + } else { + throw new Error('Invalid argument provided to downgrade MDL nodes.'); + } + } + + // Now return the functions that should be made public with their publicly + // facing names... + return { + upgradeDom: upgradeDomInternal, + upgradeElement: upgradeElementInternal, + upgradeElements: upgradeElementsInternal, + upgradeAllRegistered: upgradeAllRegisteredInternal, + registerUpgradedCallback: registerUpgradedCallbackInternal, + register: registerInternal, + downgradeElements: downgradeNodesInternal + }; +})(); + +/** + * Describes the type of a registered component type managed by + * componentHandler. Provided for benefit of the Closure compiler. + * + * @typedef {{ + * constructor: Function, + * classAsString: string, + * cssClass: string, + * widget: (string|boolean|undefined) + * }} + */ +componentHandler.ComponentConfigPublic; // jshint ignore:line + +/** + * Describes the type of a registered component type managed by + * componentHandler. Provided for benefit of the Closure compiler. + * + * @typedef {{ + * constructor: !Function, + * className: string, + * cssClass: string, + * widget: (string|boolean), + * callbacks: !Array + * }} + */ +componentHandler.ComponentConfig; // jshint ignore:line + +/** + * Created component (i.e., upgraded element) type as managed by + * componentHandler. Provided for benefit of the Closure compiler. + * + * @typedef {{ + * element_: !HTMLElement, + * className: string, + * classAsString: string, + * cssClass: string, + * widget: string + * }} + */ +componentHandler.Component; // jshint ignore:line + +// Export all symbols, for the benefit of Closure compiler. +// No effect on uncompiled code. +componentHandler['upgradeDom'] = componentHandler.upgradeDom; +componentHandler['upgradeElement'] = componentHandler.upgradeElement; +componentHandler['upgradeElements'] = componentHandler.upgradeElements; +componentHandler['upgradeAllRegistered'] = + componentHandler.upgradeAllRegistered; +componentHandler['registerUpgradedCallback'] = + componentHandler.registerUpgradedCallback; +componentHandler['register'] = componentHandler.register; +componentHandler['downgradeElements'] = componentHandler.downgradeElements; +window.componentHandler = componentHandler; +window['componentHandler'] = componentHandler; + +window.addEventListener('load', function() { + 'use strict'; + + /** + * Performs a "Cutting the mustard" test. If the browser supports the features + * tested, adds a mdl-js class to the element. It then upgrades all MDL + * components requiring JavaScript. + */ + if ('classList' in document.createElement('div') && + 'querySelector' in document && + 'addEventListener' in window && Array.prototype.forEach) { + document.documentElement.classList.add('mdl-js'); + componentHandler.upgradeAllRegistered(); + } else { + /** + * Dummy function to avoid JS errors. + */ + componentHandler.upgradeElement = function() {}; + /** + * Dummy function to avoid JS errors. + */ + componentHandler.register = function() {}; + } +}); + +// Source: https://github.com/darius/requestAnimationFrame/blob/master/requestAnimationFrame.js +// Adapted from https://gist.github.com/paulirish/1579671 which derived from +// http://paulirish.com/2011/requestanimationframe-for-smart-animating/ +// http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating +// requestAnimationFrame polyfill by Erik Möller. +// Fixes from Paul Irish, Tino Zijdel, Andrew Mao, Klemen Slavič, Darius Bacon +// MIT license +if (!Date.now) { + /** + * Date.now polyfill. + * @return {number} the current Date + */ + Date.now = function () { + return new Date().getTime(); + }; + Date['now'] = Date.now; +} +var vendors = [ + 'webkit', + 'moz' +]; +for (var i = 0; i < vendors.length && !window.requestAnimationFrame; ++i) { + var vp = vendors[i]; + window.requestAnimationFrame = window[vp + 'RequestAnimationFrame']; + window.cancelAnimationFrame = window[vp + 'CancelAnimationFrame'] || window[vp + 'CancelRequestAnimationFrame']; + window['requestAnimationFrame'] = window.requestAnimationFrame; + window['cancelAnimationFrame'] = window.cancelAnimationFrame; +} +if (/iP(ad|hone|od).*OS 6/.test(window.navigator.userAgent) || !window.requestAnimationFrame || !window.cancelAnimationFrame) { + var lastTime = 0; + /** + * requestAnimationFrame polyfill. + * @param {!Function} callback the callback function. + */ + window.requestAnimationFrame = function (callback) { + var now = Date.now(); + var nextTime = Math.max(lastTime + 16, now); + return setTimeout(function () { + callback(lastTime = nextTime); + }, nextTime - now); + }; + window.cancelAnimationFrame = clearTimeout; + window['requestAnimationFrame'] = window.requestAnimationFrame; + window['cancelAnimationFrame'] = window.cancelAnimationFrame; +} +/** + * @license + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * Class constructor for Button MDL component. + * Implements MDL component design pattern defined at: + * https://github.com/jasonmayes/mdl-component-design-pattern + * + * @param {HTMLElement} element The element that will be upgraded. + */ +var MaterialButton = function MaterialButton(element) { + this.element_ = element; + // Initialize instance. + this.init(); +}; +window['MaterialButton'] = MaterialButton; +/** + * Store constants in one place so they can be updated easily. + * + * @enum {string | number} + * @private + */ +MaterialButton.prototype.Constant_ = {}; +/** + * Store strings for class names defined by this component that are used in + * JavaScript. This allows us to simply change it in one place should we + * decide to modify at a later date. + * + * @enum {string} + * @private + */ +MaterialButton.prototype.CssClasses_ = { + RIPPLE_EFFECT: 'mdl-js-ripple-effect', + RIPPLE_CONTAINER: 'mdl-button__ripple-container', + RIPPLE: 'mdl-ripple' +}; +/** + * Handle blur of element. + * + * @param {Event} event The event that fired. + * @private + */ +MaterialButton.prototype.blurHandler_ = function (event) { + if (event) { + this.element_.blur(); + } +}; +// Public methods. +/** + * Disable button. + * + * @public + */ +MaterialButton.prototype.disable = function () { + this.element_.disabled = true; +}; +MaterialButton.prototype['disable'] = MaterialButton.prototype.disable; +/** + * Enable button. + * + * @public + */ +MaterialButton.prototype.enable = function () { + this.element_.disabled = false; +}; +MaterialButton.prototype['enable'] = MaterialButton.prototype.enable; +/** + * Initialize element. + */ +MaterialButton.prototype.init = function () { + if (this.element_) { + if (this.element_.classList.contains(this.CssClasses_.RIPPLE_EFFECT)) { + var rippleContainer = document.createElement('span'); + rippleContainer.classList.add(this.CssClasses_.RIPPLE_CONTAINER); + this.rippleElement_ = document.createElement('span'); + this.rippleElement_.classList.add(this.CssClasses_.RIPPLE); + rippleContainer.appendChild(this.rippleElement_); + this.boundRippleBlurHandler = this.blurHandler_.bind(this); + this.rippleElement_.addEventListener('mouseup', this.boundRippleBlurHandler); + this.element_.appendChild(rippleContainer); + } + this.boundButtonBlurHandler = this.blurHandler_.bind(this); + this.element_.addEventListener('mouseup', this.boundButtonBlurHandler); + this.element_.addEventListener('mouseleave', this.boundButtonBlurHandler); + } +}; +/** + * Downgrade the element. + * + * @private + */ +MaterialButton.prototype.mdlDowngrade_ = function () { + if (this.rippleElement_) { + this.rippleElement_.removeEventListener('mouseup', this.boundRippleBlurHandler); + } + this.element_.removeEventListener('mouseup', this.boundButtonBlurHandler); + this.element_.removeEventListener('mouseleave', this.boundButtonBlurHandler); +}; +/** + * Public alias for the downgrade method. + * + * @public + */ +MaterialButton.prototype.mdlDowngrade = MaterialButton.prototype.mdlDowngrade_; +MaterialButton.prototype['mdlDowngrade'] = MaterialButton.prototype.mdlDowngrade; +// The component registers itself. It can assume componentHandler is available +// in the global scope. +componentHandler.register({ + constructor: MaterialButton, + classAsString: 'MaterialButton', + cssClass: 'mdl-js-button', + widget: true +}); +/** + * @license + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * Class constructor for Checkbox MDL component. + * Implements MDL component design pattern defined at: + * https://github.com/jasonmayes/mdl-component-design-pattern + * + * @constructor + * @param {HTMLElement} element The element that will be upgraded. + */ +var MaterialCheckbox = function MaterialCheckbox(element) { + this.element_ = element; + // Initialize instance. + this.init(); +}; +window['MaterialCheckbox'] = MaterialCheckbox; +/** + * Store constants in one place so they can be updated easily. + * + * @enum {string | number} + * @private + */ +MaterialCheckbox.prototype.Constant_ = { TINY_TIMEOUT: 0.001 }; +/** + * Store strings for class names defined by this component that are used in + * JavaScript. This allows us to simply change it in one place should we + * decide to modify at a later date. + * + * @enum {string} + * @private + */ +MaterialCheckbox.prototype.CssClasses_ = { + INPUT: 'mdl-checkbox__input', + BOX_OUTLINE: 'mdl-checkbox__box-outline', + FOCUS_HELPER: 'mdl-checkbox__focus-helper', + TICK_OUTLINE: 'mdl-checkbox__tick-outline', + RIPPLE_EFFECT: 'mdl-js-ripple-effect', + RIPPLE_IGNORE_EVENTS: 'mdl-js-ripple-effect--ignore-events', + RIPPLE_CONTAINER: 'mdl-checkbox__ripple-container', + RIPPLE_CENTER: 'mdl-ripple--center', + RIPPLE: 'mdl-ripple', + IS_FOCUSED: 'is-focused', + IS_DISABLED: 'is-disabled', + IS_CHECKED: 'is-checked', + IS_UPGRADED: 'is-upgraded' +}; +/** + * Handle change of state. + * + * @param {Event} event The event that fired. + * @private + */ +MaterialCheckbox.prototype.onChange_ = function (event) { + this.updateClasses_(); +}; +/** + * Handle focus of element. + * + * @param {Event} event The event that fired. + * @private + */ +MaterialCheckbox.prototype.onFocus_ = function (event) { + this.element_.classList.add(this.CssClasses_.IS_FOCUSED); +}; +/** + * Handle lost focus of element. + * + * @param {Event} event The event that fired. + * @private + */ +MaterialCheckbox.prototype.onBlur_ = function (event) { + this.element_.classList.remove(this.CssClasses_.IS_FOCUSED); +}; +/** + * Handle mouseup. + * + * @param {Event} event The event that fired. + * @private + */ +MaterialCheckbox.prototype.onMouseUp_ = function (event) { + this.blur_(); +}; +/** + * Handle class updates. + * + * @private + */ +MaterialCheckbox.prototype.updateClasses_ = function () { + this.checkDisabled(); + this.checkToggleState(); +}; +/** + * Add blur. + * + * @private + */ +MaterialCheckbox.prototype.blur_ = function () { + // TODO: figure out why there's a focus event being fired after our blur, + // so that we can avoid this hack. + window.setTimeout(function () { + this.inputElement_.blur(); + }.bind(this), this.Constant_.TINY_TIMEOUT); +}; +// Public methods. +/** + * Check the inputs toggle state and update display. + * + * @public + */ +MaterialCheckbox.prototype.checkToggleState = function () { + if (this.inputElement_.checked) { + this.element_.classList.add(this.CssClasses_.IS_CHECKED); + } else { + this.element_.classList.remove(this.CssClasses_.IS_CHECKED); + } +}; +MaterialCheckbox.prototype['checkToggleState'] = MaterialCheckbox.prototype.checkToggleState; +/** + * Check the inputs disabled state and update display. + * + * @public + */ +MaterialCheckbox.prototype.checkDisabled = function () { + if (this.inputElement_.disabled) { + this.element_.classList.add(this.CssClasses_.IS_DISABLED); + } else { + this.element_.classList.remove(this.CssClasses_.IS_DISABLED); + } +}; +MaterialCheckbox.prototype['checkDisabled'] = MaterialCheckbox.prototype.checkDisabled; +/** + * Disable checkbox. + * + * @public + */ +MaterialCheckbox.prototype.disable = function () { + this.inputElement_.disabled = true; + this.updateClasses_(); +}; +MaterialCheckbox.prototype['disable'] = MaterialCheckbox.prototype.disable; +/** + * Enable checkbox. + * + * @public + */ +MaterialCheckbox.prototype.enable = function () { + this.inputElement_.disabled = false; + this.updateClasses_(); +}; +MaterialCheckbox.prototype['enable'] = MaterialCheckbox.prototype.enable; +/** + * Check checkbox. + * + * @public + */ +MaterialCheckbox.prototype.check = function () { + this.inputElement_.checked = true; + this.updateClasses_(); +}; +MaterialCheckbox.prototype['check'] = MaterialCheckbox.prototype.check; +/** + * Uncheck checkbox. + * + * @public + */ +MaterialCheckbox.prototype.uncheck = function () { + this.inputElement_.checked = false; + this.updateClasses_(); +}; +MaterialCheckbox.prototype['uncheck'] = MaterialCheckbox.prototype.uncheck; +/** + * Initialize element. + */ +MaterialCheckbox.prototype.init = function () { + if (this.element_) { + this.inputElement_ = this.element_.querySelector('.' + this.CssClasses_.INPUT); + var boxOutline = document.createElement('span'); + boxOutline.classList.add(this.CssClasses_.BOX_OUTLINE); + var tickContainer = document.createElement('span'); + tickContainer.classList.add(this.CssClasses_.FOCUS_HELPER); + var tickOutline = document.createElement('span'); + tickOutline.classList.add(this.CssClasses_.TICK_OUTLINE); + boxOutline.appendChild(tickOutline); + this.element_.appendChild(tickContainer); + this.element_.appendChild(boxOutline); + if (this.element_.classList.contains(this.CssClasses_.RIPPLE_EFFECT)) { + this.element_.classList.add(this.CssClasses_.RIPPLE_IGNORE_EVENTS); + this.rippleContainerElement_ = document.createElement('span'); + this.rippleContainerElement_.classList.add(this.CssClasses_.RIPPLE_CONTAINER); + this.rippleContainerElement_.classList.add(this.CssClasses_.RIPPLE_EFFECT); + this.rippleContainerElement_.classList.add(this.CssClasses_.RIPPLE_CENTER); + this.boundRippleMouseUp = this.onMouseUp_.bind(this); + this.rippleContainerElement_.addEventListener('mouseup', this.boundRippleMouseUp); + var ripple = document.createElement('span'); + ripple.classList.add(this.CssClasses_.RIPPLE); + this.rippleContainerElement_.appendChild(ripple); + this.element_.appendChild(this.rippleContainerElement_); + } + this.boundInputOnChange = this.onChange_.bind(this); + this.boundInputOnFocus = this.onFocus_.bind(this); + this.boundInputOnBlur = this.onBlur_.bind(this); + this.boundElementMouseUp = this.onMouseUp_.bind(this); + this.inputElement_.addEventListener('change', this.boundInputOnChange); + this.inputElement_.addEventListener('focus', this.boundInputOnFocus); + this.inputElement_.addEventListener('blur', this.boundInputOnBlur); + this.element_.addEventListener('mouseup', this.boundElementMouseUp); + this.updateClasses_(); + this.element_.classList.add(this.CssClasses_.IS_UPGRADED); + } +}; +/** + * Downgrade the component. + * + * @private + */ +MaterialCheckbox.prototype.mdlDowngrade_ = function () { + if (this.rippleContainerElement_) { + this.rippleContainerElement_.removeEventListener('mouseup', this.boundRippleMouseUp); + } + this.inputElement_.removeEventListener('change', this.boundInputOnChange); + this.inputElement_.removeEventListener('focus', this.boundInputOnFocus); + this.inputElement_.removeEventListener('blur', this.boundInputOnBlur); + this.element_.removeEventListener('mouseup', this.boundElementMouseUp); +}; +/** + * Public alias for the downgrade method. + * + * @public + */ +MaterialCheckbox.prototype.mdlDowngrade = MaterialCheckbox.prototype.mdlDowngrade_; +MaterialCheckbox.prototype['mdlDowngrade'] = MaterialCheckbox.prototype.mdlDowngrade; +// The component registers itself. It can assume componentHandler is available +// in the global scope. +componentHandler.register({ + constructor: MaterialCheckbox, + classAsString: 'MaterialCheckbox', + cssClass: 'mdl-js-checkbox', + widget: true +}); +/** + * @license + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * Class constructor for icon toggle MDL component. + * Implements MDL component design pattern defined at: + * https://github.com/jasonmayes/mdl-component-design-pattern + * + * @constructor + * @param {HTMLElement} element The element that will be upgraded. + */ +var MaterialIconToggle = function MaterialIconToggle(element) { + this.element_ = element; + // Initialize instance. + this.init(); +}; +window['MaterialIconToggle'] = MaterialIconToggle; +/** + * Store constants in one place so they can be updated easily. + * + * @enum {string | number} + * @private + */ +MaterialIconToggle.prototype.Constant_ = { TINY_TIMEOUT: 0.001 }; +/** + * Store strings for class names defined by this component that are used in + * JavaScript. This allows us to simply change it in one place should we + * decide to modify at a later date. + * + * @enum {string} + * @private + */ +MaterialIconToggle.prototype.CssClasses_ = { + INPUT: 'mdl-icon-toggle__input', + JS_RIPPLE_EFFECT: 'mdl-js-ripple-effect', + RIPPLE_IGNORE_EVENTS: 'mdl-js-ripple-effect--ignore-events', + RIPPLE_CONTAINER: 'mdl-icon-toggle__ripple-container', + RIPPLE_CENTER: 'mdl-ripple--center', + RIPPLE: 'mdl-ripple', + IS_FOCUSED: 'is-focused', + IS_DISABLED: 'is-disabled', + IS_CHECKED: 'is-checked' +}; +/** + * Handle change of state. + * + * @param {Event} event The event that fired. + * @private + */ +MaterialIconToggle.prototype.onChange_ = function (event) { + this.updateClasses_(); +}; +/** + * Handle focus of element. + * + * @param {Event} event The event that fired. + * @private + */ +MaterialIconToggle.prototype.onFocus_ = function (event) { + this.element_.classList.add(this.CssClasses_.IS_FOCUSED); +}; +/** + * Handle lost focus of element. + * + * @param {Event} event The event that fired. + * @private + */ +MaterialIconToggle.prototype.onBlur_ = function (event) { + this.element_.classList.remove(this.CssClasses_.IS_FOCUSED); +}; +/** + * Handle mouseup. + * + * @param {Event} event The event that fired. + * @private + */ +MaterialIconToggle.prototype.onMouseUp_ = function (event) { + this.blur_(); +}; +/** + * Handle class updates. + * + * @private + */ +MaterialIconToggle.prototype.updateClasses_ = function () { + this.checkDisabled(); + this.checkToggleState(); +}; +/** + * Add blur. + * + * @private + */ +MaterialIconToggle.prototype.blur_ = function () { + // TODO: figure out why there's a focus event being fired after our blur, + // so that we can avoid this hack. + window.setTimeout(function () { + this.inputElement_.blur(); + }.bind(this), this.Constant_.TINY_TIMEOUT); +}; +// Public methods. +/** + * Check the inputs toggle state and update display. + * + * @public + */ +MaterialIconToggle.prototype.checkToggleState = function () { + if (this.inputElement_.checked) { + this.element_.classList.add(this.CssClasses_.IS_CHECKED); + } else { + this.element_.classList.remove(this.CssClasses_.IS_CHECKED); + } +}; +MaterialIconToggle.prototype['checkToggleState'] = MaterialIconToggle.prototype.checkToggleState; +/** + * Check the inputs disabled state and update display. + * + * @public + */ +MaterialIconToggle.prototype.checkDisabled = function () { + if (this.inputElement_.disabled) { + this.element_.classList.add(this.CssClasses_.IS_DISABLED); + } else { + this.element_.classList.remove(this.CssClasses_.IS_DISABLED); + } +}; +MaterialIconToggle.prototype['checkDisabled'] = MaterialIconToggle.prototype.checkDisabled; +/** + * Disable icon toggle. + * + * @public + */ +MaterialIconToggle.prototype.disable = function () { + this.inputElement_.disabled = true; + this.updateClasses_(); +}; +MaterialIconToggle.prototype['disable'] = MaterialIconToggle.prototype.disable; +/** + * Enable icon toggle. + * + * @public + */ +MaterialIconToggle.prototype.enable = function () { + this.inputElement_.disabled = false; + this.updateClasses_(); +}; +MaterialIconToggle.prototype['enable'] = MaterialIconToggle.prototype.enable; +/** + * Check icon toggle. + * + * @public + */ +MaterialIconToggle.prototype.check = function () { + this.inputElement_.checked = true; + this.updateClasses_(); +}; +MaterialIconToggle.prototype['check'] = MaterialIconToggle.prototype.check; +/** + * Uncheck icon toggle. + * + * @public + */ +MaterialIconToggle.prototype.uncheck = function () { + this.inputElement_.checked = false; + this.updateClasses_(); +}; +MaterialIconToggle.prototype['uncheck'] = MaterialIconToggle.prototype.uncheck; +/** + * Initialize element. + */ +MaterialIconToggle.prototype.init = function () { + if (this.element_) { + this.inputElement_ = this.element_.querySelector('.' + this.CssClasses_.INPUT); + if (this.element_.classList.contains(this.CssClasses_.JS_RIPPLE_EFFECT)) { + this.element_.classList.add(this.CssClasses_.RIPPLE_IGNORE_EVENTS); + this.rippleContainerElement_ = document.createElement('span'); + this.rippleContainerElement_.classList.add(this.CssClasses_.RIPPLE_CONTAINER); + this.rippleContainerElement_.classList.add(this.CssClasses_.JS_RIPPLE_EFFECT); + this.rippleContainerElement_.classList.add(this.CssClasses_.RIPPLE_CENTER); + this.boundRippleMouseUp = this.onMouseUp_.bind(this); + this.rippleContainerElement_.addEventListener('mouseup', this.boundRippleMouseUp); + var ripple = document.createElement('span'); + ripple.classList.add(this.CssClasses_.RIPPLE); + this.rippleContainerElement_.appendChild(ripple); + this.element_.appendChild(this.rippleContainerElement_); + } + this.boundInputOnChange = this.onChange_.bind(this); + this.boundInputOnFocus = this.onFocus_.bind(this); + this.boundInputOnBlur = this.onBlur_.bind(this); + this.boundElementOnMouseUp = this.onMouseUp_.bind(this); + this.inputElement_.addEventListener('change', this.boundInputOnChange); + this.inputElement_.addEventListener('focus', this.boundInputOnFocus); + this.inputElement_.addEventListener('blur', this.boundInputOnBlur); + this.element_.addEventListener('mouseup', this.boundElementOnMouseUp); + this.updateClasses_(); + this.element_.classList.add('is-upgraded'); + } +}; +/** + * Downgrade the component + * + * @private + */ +MaterialIconToggle.prototype.mdlDowngrade_ = function () { + if (this.rippleContainerElement_) { + this.rippleContainerElement_.removeEventListener('mouseup', this.boundRippleMouseUp); + } + this.inputElement_.removeEventListener('change', this.boundInputOnChange); + this.inputElement_.removeEventListener('focus', this.boundInputOnFocus); + this.inputElement_.removeEventListener('blur', this.boundInputOnBlur); + this.element_.removeEventListener('mouseup', this.boundElementOnMouseUp); +}; +/** + * Public alias for the downgrade method. + * + * @public + */ +MaterialIconToggle.prototype.mdlDowngrade = MaterialIconToggle.prototype.mdlDowngrade_; +MaterialIconToggle.prototype['mdlDowngrade'] = MaterialIconToggle.prototype.mdlDowngrade; +// The component registers itself. It can assume componentHandler is available +// in the global scope. +componentHandler.register({ + constructor: MaterialIconToggle, + classAsString: 'MaterialIconToggle', + cssClass: 'mdl-js-icon-toggle', + widget: true +}); +/** + * @license + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * Class constructor for dropdown MDL component. + * Implements MDL component design pattern defined at: + * https://github.com/jasonmayes/mdl-component-design-pattern + * + * @constructor + * @param {HTMLElement} element The element that will be upgraded. + */ +var MaterialMenu = function MaterialMenu(element) { + this.element_ = element; + // Initialize instance. + this.init(); +}; +window['MaterialMenu'] = MaterialMenu; +/** + * Store constants in one place so they can be updated easily. + * + * @enum {string | number} + * @private + */ +MaterialMenu.prototype.Constant_ = { + // Total duration of the menu animation. + TRANSITION_DURATION_SECONDS: 0.3, + // The fraction of the total duration we want to use for menu item animations. + TRANSITION_DURATION_FRACTION: 0.8, + // How long the menu stays open after choosing an option (so the user can see + // the ripple). + CLOSE_TIMEOUT: 150 +}; +/** + * Keycodes, for code readability. + * + * @enum {number} + * @private + */ +MaterialMenu.prototype.Keycodes_ = { + ENTER: 13, + ESCAPE: 27, + SPACE: 32, + UP_ARROW: 38, + DOWN_ARROW: 40 +}; +/** + * Store strings for class names defined by this component that are used in + * JavaScript. This allows us to simply change it in one place should we + * decide to modify at a later date. + * + * @enum {string} + * @private + */ +MaterialMenu.prototype.CssClasses_ = { + CONTAINER: 'mdl-menu__container', + OUTLINE: 'mdl-menu__outline', + ITEM: 'mdl-menu__item', + ITEM_RIPPLE_CONTAINER: 'mdl-menu__item-ripple-container', + RIPPLE_EFFECT: 'mdl-js-ripple-effect', + RIPPLE_IGNORE_EVENTS: 'mdl-js-ripple-effect--ignore-events', + RIPPLE: 'mdl-ripple', + // Statuses + IS_UPGRADED: 'is-upgraded', + IS_VISIBLE: 'is-visible', + IS_ANIMATING: 'is-animating', + // Alignment options + BOTTOM_LEFT: 'mdl-menu--bottom-left', + // This is the default. + BOTTOM_RIGHT: 'mdl-menu--bottom-right', + TOP_LEFT: 'mdl-menu--top-left', + TOP_RIGHT: 'mdl-menu--top-right', + UNALIGNED: 'mdl-menu--unaligned' +}; +/** + * Initialize element. + */ +MaterialMenu.prototype.init = function () { + if (this.element_) { + // Create container for the menu. + var container = document.createElement('div'); + container.classList.add(this.CssClasses_.CONTAINER); + this.element_.parentElement.insertBefore(container, this.element_); + this.element_.parentElement.removeChild(this.element_); + container.appendChild(this.element_); + this.container_ = container; + // Create outline for the menu (shadow and background). + var outline = document.createElement('div'); + outline.classList.add(this.CssClasses_.OUTLINE); + this.outline_ = outline; + container.insertBefore(outline, this.element_); + // Find the "for" element and bind events to it. + var forElId = this.element_.getAttribute('for'); + var forEl = null; + if (forElId) { + forEl = document.getElementById(forElId); + if (forEl) { + this.forElement_ = forEl; + forEl.addEventListener('click', this.handleForClick_.bind(this)); + forEl.addEventListener('keydown', this.handleForKeyboardEvent_.bind(this)); + } + } + var items = this.element_.querySelectorAll('.' + this.CssClasses_.ITEM); + this.boundItemKeydown_ = this.handleItemKeyboardEvent_.bind(this); + this.boundItemClick_ = this.handleItemClick_.bind(this); + for (var i = 0; i < items.length; i++) { + // Add a listener to each menu item. + items[i].addEventListener('click', this.boundItemClick_); + // Add a tab index to each menu item. + items[i].tabIndex = '-1'; + // Add a keyboard listener to each menu item. + items[i].addEventListener('keydown', this.boundItemKeydown_); + } + // Add ripple classes to each item, if the user has enabled ripples. + if (this.element_.classList.contains(this.CssClasses_.RIPPLE_EFFECT)) { + this.element_.classList.add(this.CssClasses_.RIPPLE_IGNORE_EVENTS); + for (i = 0; i < items.length; i++) { + var item = items[i]; + var rippleContainer = document.createElement('span'); + rippleContainer.classList.add(this.CssClasses_.ITEM_RIPPLE_CONTAINER); + var ripple = document.createElement('span'); + ripple.classList.add(this.CssClasses_.RIPPLE); + rippleContainer.appendChild(ripple); + item.appendChild(rippleContainer); + item.classList.add(this.CssClasses_.RIPPLE_EFFECT); + } + } + // Copy alignment classes to the container, so the outline can use them. + if (this.element_.classList.contains(this.CssClasses_.BOTTOM_LEFT)) { + this.outline_.classList.add(this.CssClasses_.BOTTOM_LEFT); + } + if (this.element_.classList.contains(this.CssClasses_.BOTTOM_RIGHT)) { + this.outline_.classList.add(this.CssClasses_.BOTTOM_RIGHT); + } + if (this.element_.classList.contains(this.CssClasses_.TOP_LEFT)) { + this.outline_.classList.add(this.CssClasses_.TOP_LEFT); + } + if (this.element_.classList.contains(this.CssClasses_.TOP_RIGHT)) { + this.outline_.classList.add(this.CssClasses_.TOP_RIGHT); + } + if (this.element_.classList.contains(this.CssClasses_.UNALIGNED)) { + this.outline_.classList.add(this.CssClasses_.UNALIGNED); + } + container.classList.add(this.CssClasses_.IS_UPGRADED); + } +}; +/** + * Handles a click on the "for" element, by positioning the menu and then + * toggling it. + * + * @param {Event} evt The event that fired. + * @private + */ +MaterialMenu.prototype.handleForClick_ = function (evt) { + if (this.element_ && this.forElement_) { + var rect = this.forElement_.getBoundingClientRect(); + var forRect = this.forElement_.parentElement.getBoundingClientRect(); + if (this.element_.classList.contains(this.CssClasses_.UNALIGNED)) { + } else if (this.element_.classList.contains(this.CssClasses_.BOTTOM_RIGHT)) { + // Position below the "for" element, aligned to its right. + this.container_.style.right = forRect.right - rect.right + 'px'; + this.container_.style.top = this.forElement_.offsetTop + this.forElement_.offsetHeight + 'px'; + } else if (this.element_.classList.contains(this.CssClasses_.TOP_LEFT)) { + // Position above the "for" element, aligned to its left. + this.container_.style.left = this.forElement_.offsetLeft + 'px'; + this.container_.style.bottom = forRect.bottom - rect.top + 'px'; + } else if (this.element_.classList.contains(this.CssClasses_.TOP_RIGHT)) { + // Position above the "for" element, aligned to its right. + this.container_.style.right = forRect.right - rect.right + 'px'; + this.container_.style.bottom = forRect.bottom - rect.top + 'px'; + } else { + // Default: position below the "for" element, aligned to its left. + this.container_.style.left = this.forElement_.offsetLeft + 'px'; + this.container_.style.top = this.forElement_.offsetTop + this.forElement_.offsetHeight + 'px'; + } + } + this.toggle(evt); +}; +/** + * Handles a keyboard event on the "for" element. + * + * @param {Event} evt The event that fired. + * @private + */ +MaterialMenu.prototype.handleForKeyboardEvent_ = function (evt) { + if (this.element_ && this.container_ && this.forElement_) { + var items = this.element_.querySelectorAll('.' + this.CssClasses_.ITEM + ':not([disabled])'); + if (items && items.length > 0 && this.container_.classList.contains(this.CssClasses_.IS_VISIBLE)) { + if (evt.keyCode === this.Keycodes_.UP_ARROW) { + evt.preventDefault(); + items[items.length - 1].focus(); + } else if (evt.keyCode === this.Keycodes_.DOWN_ARROW) { + evt.preventDefault(); + items[0].focus(); + } + } + } +}; +/** + * Handles a keyboard event on an item. + * + * @param {Event} evt The event that fired. + * @private + */ +MaterialMenu.prototype.handleItemKeyboardEvent_ = function (evt) { + if (this.element_ && this.container_) { + var items = this.element_.querySelectorAll('.' + this.CssClasses_.ITEM + ':not([disabled])'); + if (items && items.length > 0 && this.container_.classList.contains(this.CssClasses_.IS_VISIBLE)) { + var currentIndex = Array.prototype.slice.call(items).indexOf(evt.target); + if (evt.keyCode === this.Keycodes_.UP_ARROW) { + evt.preventDefault(); + if (currentIndex > 0) { + items[currentIndex - 1].focus(); + } else { + items[items.length - 1].focus(); + } + } else if (evt.keyCode === this.Keycodes_.DOWN_ARROW) { + evt.preventDefault(); + if (items.length > currentIndex + 1) { + items[currentIndex + 1].focus(); + } else { + items[0].focus(); + } + } else if (evt.keyCode === this.Keycodes_.SPACE || evt.keyCode === this.Keycodes_.ENTER) { + evt.preventDefault(); + // Send mousedown and mouseup to trigger ripple. + var e = new MouseEvent('mousedown'); + evt.target.dispatchEvent(e); + e = new MouseEvent('mouseup'); + evt.target.dispatchEvent(e); + // Send click. + evt.target.click(); + } else if (evt.keyCode === this.Keycodes_.ESCAPE) { + evt.preventDefault(); + this.hide(); + } + } + } +}; +/** + * Handles a click event on an item. + * + * @param {Event} evt The event that fired. + * @private + */ +MaterialMenu.prototype.handleItemClick_ = function (evt) { + if (evt.target.hasAttribute('disabled')) { + evt.stopPropagation(); + } else { + // Wait some time before closing menu, so the user can see the ripple. + this.closing_ = true; + window.setTimeout(function (evt) { + this.hide(); + this.closing_ = false; + }.bind(this), this.Constant_.CLOSE_TIMEOUT); + } +}; +/** + * Calculates the initial clip (for opening the menu) or final clip (for closing + * it), and applies it. This allows us to animate from or to the correct point, + * that is, the point it's aligned to in the "for" element. + * + * @param {number} height Height of the clip rectangle + * @param {number} width Width of the clip rectangle + * @private + */ +MaterialMenu.prototype.applyClip_ = function (height, width) { + if (this.element_.classList.contains(this.CssClasses_.UNALIGNED)) { + // Do not clip. + this.element_.style.clip = ''; + } else if (this.element_.classList.contains(this.CssClasses_.BOTTOM_RIGHT)) { + // Clip to the top right corner of the menu. + this.element_.style.clip = 'rect(0 ' + width + 'px ' + '0 ' + width + 'px)'; + } else if (this.element_.classList.contains(this.CssClasses_.TOP_LEFT)) { + // Clip to the bottom left corner of the menu. + this.element_.style.clip = 'rect(' + height + 'px 0 ' + height + 'px 0)'; + } else if (this.element_.classList.contains(this.CssClasses_.TOP_RIGHT)) { + // Clip to the bottom right corner of the menu. + this.element_.style.clip = 'rect(' + height + 'px ' + width + 'px ' + height + 'px ' + width + 'px)'; + } else { + // Default: do not clip (same as clipping to the top left corner). + this.element_.style.clip = ''; + } +}; +/** + * Adds an event listener to clean up after the animation ends. + * + * @private + */ +MaterialMenu.prototype.addAnimationEndListener_ = function () { + var cleanup = function () { + this.element_.removeEventListener('transitionend', cleanup); + this.element_.removeEventListener('webkitTransitionEnd', cleanup); + this.element_.classList.remove(this.CssClasses_.IS_ANIMATING); + }.bind(this); + // Remove animation class once the transition is done. + this.element_.addEventListener('transitionend', cleanup); + this.element_.addEventListener('webkitTransitionEnd', cleanup); +}; +/** + * Displays the menu. + * + * @public + */ +MaterialMenu.prototype.show = function (evt) { + if (this.element_ && this.container_ && this.outline_) { + // Measure the inner element. + var height = this.element_.getBoundingClientRect().height; + var width = this.element_.getBoundingClientRect().width; + // Apply the inner element's size to the container and outline. + this.container_.style.width = width + 'px'; + this.container_.style.height = height + 'px'; + this.outline_.style.width = width + 'px'; + this.outline_.style.height = height + 'px'; + var transitionDuration = this.Constant_.TRANSITION_DURATION_SECONDS * this.Constant_.TRANSITION_DURATION_FRACTION; + // Calculate transition delays for individual menu items, so that they fade + // in one at a time. + var items = this.element_.querySelectorAll('.' + this.CssClasses_.ITEM); + for (var i = 0; i < items.length; i++) { + var itemDelay = null; + if (this.element_.classList.contains(this.CssClasses_.TOP_LEFT) || this.element_.classList.contains(this.CssClasses_.TOP_RIGHT)) { + itemDelay = (height - items[i].offsetTop - items[i].offsetHeight) / height * transitionDuration + 's'; + } else { + itemDelay = items[i].offsetTop / height * transitionDuration + 's'; + } + items[i].style.transitionDelay = itemDelay; + } + // Apply the initial clip to the text before we start animating. + this.applyClip_(height, width); + // Wait for the next frame, turn on animation, and apply the final clip. + // Also make it visible. This triggers the transitions. + window.requestAnimationFrame(function () { + this.element_.classList.add(this.CssClasses_.IS_ANIMATING); + this.element_.style.clip = 'rect(0 ' + width + 'px ' + height + 'px 0)'; + this.container_.classList.add(this.CssClasses_.IS_VISIBLE); + }.bind(this)); + // Clean up after the animation is complete. + this.addAnimationEndListener_(); + // Add a click listener to the document, to close the menu. + var callback = function (e) { + // Check to see if the document is processing the same event that + // displayed the menu in the first place. If so, do nothing. + // Also check to see if the menu is in the process of closing itself, and + // do nothing in that case. + // Also check if the clicked element is a menu item + // if so, do nothing. + if (e !== evt && !this.closing_ && e.target.parentNode !== this.element_) { + document.removeEventListener('click', callback); + this.hide(); + } + }.bind(this); + document.addEventListener('click', callback); + } +}; +MaterialMenu.prototype['show'] = MaterialMenu.prototype.show; +/** + * Hides the menu. + * + * @public + */ +MaterialMenu.prototype.hide = function () { + if (this.element_ && this.container_ && this.outline_) { + var items = this.element_.querySelectorAll('.' + this.CssClasses_.ITEM); + // Remove all transition delays; menu items fade out concurrently. + for (var i = 0; i < items.length; i++) { + items[i].style.transitionDelay = null; + } + // Measure the inner element. + var rect = this.element_.getBoundingClientRect(); + var height = rect.height; + var width = rect.width; + // Turn on animation, and apply the final clip. Also make invisible. + // This triggers the transitions. + this.element_.classList.add(this.CssClasses_.IS_ANIMATING); + this.applyClip_(height, width); + this.container_.classList.remove(this.CssClasses_.IS_VISIBLE); + // Clean up after the animation is complete. + this.addAnimationEndListener_(); + } +}; +MaterialMenu.prototype['hide'] = MaterialMenu.prototype.hide; +/** + * Displays or hides the menu, depending on current state. + * + * @public + */ +MaterialMenu.prototype.toggle = function (evt) { + if (this.container_.classList.contains(this.CssClasses_.IS_VISIBLE)) { + this.hide(); + } else { + this.show(evt); + } +}; +MaterialMenu.prototype['toggle'] = MaterialMenu.prototype.toggle; +/** + * Downgrade the component. + * + * @private + */ +MaterialMenu.prototype.mdlDowngrade_ = function () { + var items = this.element_.querySelectorAll('.' + this.CssClasses_.ITEM); + for (var i = 0; i < items.length; i++) { + items[i].removeEventListener('click', this.boundItemClick_); + items[i].removeEventListener('keydown', this.boundItemKeydown_); + } +}; +/** + * Public alias for the downgrade method. + * + * @public + */ +MaterialMenu.prototype.mdlDowngrade = MaterialMenu.prototype.mdlDowngrade_; +MaterialMenu.prototype['mdlDowngrade'] = MaterialMenu.prototype.mdlDowngrade; +// The component registers itself. It can assume componentHandler is available +// in the global scope. +componentHandler.register({ + constructor: MaterialMenu, + classAsString: 'MaterialMenu', + cssClass: 'mdl-js-menu', + widget: true +}); +/** + * @license + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * Class constructor for Progress MDL component. + * Implements MDL component design pattern defined at: + * https://github.com/jasonmayes/mdl-component-design-pattern + * + * @constructor + * @param {HTMLElement} element The element that will be upgraded. + */ +var MaterialProgress = function MaterialProgress(element) { + this.element_ = element; + // Initialize instance. + this.init(); +}; +window['MaterialProgress'] = MaterialProgress; +/** + * Store constants in one place so they can be updated easily. + * + * @enum {string | number} + * @private + */ +MaterialProgress.prototype.Constant_ = {}; +/** + * Store strings for class names defined by this component that are used in + * JavaScript. This allows us to simply change it in one place should we + * decide to modify at a later date. + * + * @enum {string} + * @private + */ +MaterialProgress.prototype.CssClasses_ = { INDETERMINATE_CLASS: 'mdl-progress__indeterminate' }; +/** + * Set the current progress of the progressbar. + * + * @param {number} p Percentage of the progress (0-100) + * @public + */ +MaterialProgress.prototype.setProgress = function (p) { + if (this.element_.classList.contains(this.CssClasses_.INDETERMINATE_CLASS)) { + return; + } + this.progressbar_.style.width = p + '%'; +}; +MaterialProgress.prototype['setProgress'] = MaterialProgress.prototype.setProgress; +/** + * Set the current progress of the buffer. + * + * @param {number} p Percentage of the buffer (0-100) + * @public + */ +MaterialProgress.prototype.setBuffer = function (p) { + this.bufferbar_.style.width = p + '%'; + this.auxbar_.style.width = 100 - p + '%'; +}; +MaterialProgress.prototype['setBuffer'] = MaterialProgress.prototype.setBuffer; +/** + * Initialize element. + */ +MaterialProgress.prototype.init = function () { + if (this.element_) { + var el = document.createElement('div'); + el.className = 'progressbar bar bar1'; + this.element_.appendChild(el); + this.progressbar_ = el; + el = document.createElement('div'); + el.className = 'bufferbar bar bar2'; + this.element_.appendChild(el); + this.bufferbar_ = el; + el = document.createElement('div'); + el.className = 'auxbar bar bar3'; + this.element_.appendChild(el); + this.auxbar_ = el; + this.progressbar_.style.width = '0%'; + this.bufferbar_.style.width = '100%'; + this.auxbar_.style.width = '0%'; + this.element_.classList.add('is-upgraded'); + } +}; +/** + * Downgrade the component + * + * @private + */ +MaterialProgress.prototype.mdlDowngrade_ = function () { + while (this.element_.firstChild) { + this.element_.removeChild(this.element_.firstChild); + } +}; +/** + * Public alias for the downgrade method. + * + * @public + */ +MaterialProgress.prototype.mdlDowngrade = MaterialProgress.prototype.mdlDowngrade_; +MaterialProgress.prototype['mdlDowngrade'] = MaterialProgress.prototype.mdlDowngrade; +// The component registers itself. It can assume componentHandler is available +// in the global scope. +componentHandler.register({ + constructor: MaterialProgress, + classAsString: 'MaterialProgress', + cssClass: 'mdl-js-progress', + widget: true +}); +/** + * @license + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * Class constructor for Radio MDL component. + * Implements MDL component design pattern defined at: + * https://github.com/jasonmayes/mdl-component-design-pattern + * + * @constructor + * @param {HTMLElement} element The element that will be upgraded. + */ +var MaterialRadio = function MaterialRadio(element) { + this.element_ = element; + // Initialize instance. + this.init(); +}; +window['MaterialRadio'] = MaterialRadio; +/** + * Store constants in one place so they can be updated easily. + * + * @enum {string | number} + * @private + */ +MaterialRadio.prototype.Constant_ = { TINY_TIMEOUT: 0.001 }; +/** + * Store strings for class names defined by this component that are used in + * JavaScript. This allows us to simply change it in one place should we + * decide to modify at a later date. + * + * @enum {string} + * @private + */ +MaterialRadio.prototype.CssClasses_ = { + IS_FOCUSED: 'is-focused', + IS_DISABLED: 'is-disabled', + IS_CHECKED: 'is-checked', + IS_UPGRADED: 'is-upgraded', + JS_RADIO: 'mdl-js-radio', + RADIO_BTN: 'mdl-radio__button', + RADIO_OUTER_CIRCLE: 'mdl-radio__outer-circle', + RADIO_INNER_CIRCLE: 'mdl-radio__inner-circle', + RIPPLE_EFFECT: 'mdl-js-ripple-effect', + RIPPLE_IGNORE_EVENTS: 'mdl-js-ripple-effect--ignore-events', + RIPPLE_CONTAINER: 'mdl-radio__ripple-container', + RIPPLE_CENTER: 'mdl-ripple--center', + RIPPLE: 'mdl-ripple' +}; +/** + * Handle change of state. + * + * @param {Event} event The event that fired. + * @private + */ +MaterialRadio.prototype.onChange_ = function (event) { + // Since other radio buttons don't get change events, we need to look for + // them to update their classes. + var radios = document.getElementsByClassName(this.CssClasses_.JS_RADIO); + for (var i = 0; i < radios.length; i++) { + var button = radios[i].querySelector('.' + this.CssClasses_.RADIO_BTN); + // Different name == different group, so no point updating those. + if (button.getAttribute('name') === this.btnElement_.getAttribute('name')) { + radios[i]['MaterialRadio'].updateClasses_(); + } + } +}; +/** + * Handle focus. + * + * @param {Event} event The event that fired. + * @private + */ +MaterialRadio.prototype.onFocus_ = function (event) { + this.element_.classList.add(this.CssClasses_.IS_FOCUSED); +}; +/** + * Handle lost focus. + * + * @param {Event} event The event that fired. + * @private + */ +MaterialRadio.prototype.onBlur_ = function (event) { + this.element_.classList.remove(this.CssClasses_.IS_FOCUSED); +}; +/** + * Handle mouseup. + * + * @param {Event} event The event that fired. + * @private + */ +MaterialRadio.prototype.onMouseup_ = function (event) { + this.blur_(); +}; +/** + * Update classes. + * + * @private + */ +MaterialRadio.prototype.updateClasses_ = function () { + this.checkDisabled(); + this.checkToggleState(); +}; +/** + * Add blur. + * + * @private + */ +MaterialRadio.prototype.blur_ = function () { + // TODO: figure out why there's a focus event being fired after our blur, + // so that we can avoid this hack. + window.setTimeout(function () { + this.btnElement_.blur(); + }.bind(this), this.Constant_.TINY_TIMEOUT); +}; +// Public methods. +/** + * Check the components disabled state. + * + * @public + */ +MaterialRadio.prototype.checkDisabled = function () { + if (this.btnElement_.disabled) { + this.element_.classList.add(this.CssClasses_.IS_DISABLED); + } else { + this.element_.classList.remove(this.CssClasses_.IS_DISABLED); + } +}; +MaterialRadio.prototype['checkDisabled'] = MaterialRadio.prototype.checkDisabled; +/** + * Check the components toggled state. + * + * @public + */ +MaterialRadio.prototype.checkToggleState = function () { + if (this.btnElement_.checked) { + this.element_.classList.add(this.CssClasses_.IS_CHECKED); + } else { + this.element_.classList.remove(this.CssClasses_.IS_CHECKED); + } +}; +MaterialRadio.prototype['checkToggleState'] = MaterialRadio.prototype.checkToggleState; +/** + * Disable radio. + * + * @public + */ +MaterialRadio.prototype.disable = function () { + this.btnElement_.disabled = true; + this.updateClasses_(); +}; +MaterialRadio.prototype['disable'] = MaterialRadio.prototype.disable; +/** + * Enable radio. + * + * @public + */ +MaterialRadio.prototype.enable = function () { + this.btnElement_.disabled = false; + this.updateClasses_(); +}; +MaterialRadio.prototype['enable'] = MaterialRadio.prototype.enable; +/** + * Check radio. + * + * @public + */ +MaterialRadio.prototype.check = function () { + this.btnElement_.checked = true; + this.updateClasses_(); +}; +MaterialRadio.prototype['check'] = MaterialRadio.prototype.check; +/** + * Uncheck radio. + * + * @public + */ +MaterialRadio.prototype.uncheck = function () { + this.btnElement_.checked = false; + this.updateClasses_(); +}; +MaterialRadio.prototype['uncheck'] = MaterialRadio.prototype.uncheck; +/** + * Initialize element. + */ +MaterialRadio.prototype.init = function () { + if (this.element_) { + this.btnElement_ = this.element_.querySelector('.' + this.CssClasses_.RADIO_BTN); + this.boundChangeHandler_ = this.onChange_.bind(this); + this.boundFocusHandler_ = this.onChange_.bind(this); + this.boundBlurHandler_ = this.onBlur_.bind(this); + this.boundMouseUpHandler_ = this.onMouseup_.bind(this); + var outerCircle = document.createElement('span'); + outerCircle.classList.add(this.CssClasses_.RADIO_OUTER_CIRCLE); + var innerCircle = document.createElement('span'); + innerCircle.classList.add(this.CssClasses_.RADIO_INNER_CIRCLE); + this.element_.appendChild(outerCircle); + this.element_.appendChild(innerCircle); + var rippleContainer; + if (this.element_.classList.contains(this.CssClasses_.RIPPLE_EFFECT)) { + this.element_.classList.add(this.CssClasses_.RIPPLE_IGNORE_EVENTS); + rippleContainer = document.createElement('span'); + rippleContainer.classList.add(this.CssClasses_.RIPPLE_CONTAINER); + rippleContainer.classList.add(this.CssClasses_.RIPPLE_EFFECT); + rippleContainer.classList.add(this.CssClasses_.RIPPLE_CENTER); + rippleContainer.addEventListener('mouseup', this.boundMouseUpHandler_); + var ripple = document.createElement('span'); + ripple.classList.add(this.CssClasses_.RIPPLE); + rippleContainer.appendChild(ripple); + this.element_.appendChild(rippleContainer); + } + this.btnElement_.addEventListener('change', this.boundChangeHandler_); + this.btnElement_.addEventListener('focus', this.boundFocusHandler_); + this.btnElement_.addEventListener('blur', this.boundBlurHandler_); + this.element_.addEventListener('mouseup', this.boundMouseUpHandler_); + this.updateClasses_(); + this.element_.classList.add(this.CssClasses_.IS_UPGRADED); + } +}; +/** + * Downgrade the element. + * + * @private + */ +MaterialRadio.prototype.mdlDowngrade_ = function () { + var rippleContainer = this.element_.querySelector('.' + this.CssClasses_.RIPPLE_CONTAINER); + this.btnElement_.removeEventListener('change', this.boundChangeHandler_); + this.btnElement_.removeEventListener('focus', this.boundFocusHandler_); + this.btnElement_.removeEventListener('blur', this.boundBlurHandler_); + this.element_.removeEventListener('mouseup', this.boundMouseUpHandler_); + if (rippleContainer) { + rippleContainer.removeEventListener('mouseup', this.boundMouseUpHandler_); + this.element_.removeChild(rippleContainer); + } +}; +/** + * Public alias for the downgrade method. + * + * @public + */ +MaterialRadio.prototype.mdlDowngrade = MaterialRadio.prototype.mdlDowngrade_; +MaterialRadio.prototype['mdlDowngrade'] = MaterialRadio.prototype.mdlDowngrade; +// The component registers itself. It can assume componentHandler is available +// in the global scope. +componentHandler.register({ + constructor: MaterialRadio, + classAsString: 'MaterialRadio', + cssClass: 'mdl-js-radio', + widget: true +}); +/** + * @license + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * Class constructor for Slider MDL component. + * Implements MDL component design pattern defined at: + * https://github.com/jasonmayes/mdl-component-design-pattern + * + * @constructor + * @param {HTMLElement} element The element that will be upgraded. + */ +var MaterialSlider = function MaterialSlider(element) { + this.element_ = element; + // Browser feature detection. + this.isIE_ = window.navigator.msPointerEnabled; + // Initialize instance. + this.init(); +}; +window['MaterialSlider'] = MaterialSlider; +/** + * Store constants in one place so they can be updated easily. + * + * @enum {string | number} + * @private + */ +MaterialSlider.prototype.Constant_ = {}; +/** + * Store strings for class names defined by this component that are used in + * JavaScript. This allows us to simply change it in one place should we + * decide to modify at a later date. + * + * @enum {string} + * @private + */ +MaterialSlider.prototype.CssClasses_ = { + IE_CONTAINER: 'mdl-slider__ie-container', + SLIDER_CONTAINER: 'mdl-slider__container', + BACKGROUND_FLEX: 'mdl-slider__background-flex', + BACKGROUND_LOWER: 'mdl-slider__background-lower', + BACKGROUND_UPPER: 'mdl-slider__background-upper', + IS_LOWEST_VALUE: 'is-lowest-value', + IS_UPGRADED: 'is-upgraded' +}; +/** + * Handle input on element. + * + * @param {Event} event The event that fired. + * @private + */ +MaterialSlider.prototype.onInput_ = function (event) { + this.updateValueStyles_(); +}; +/** + * Handle change on element. + * + * @param {Event} event The event that fired. + * @private + */ +MaterialSlider.prototype.onChange_ = function (event) { + this.updateValueStyles_(); +}; +/** + * Handle mouseup on element. + * + * @param {Event} event The event that fired. + * @private + */ +MaterialSlider.prototype.onMouseUp_ = function (event) { + event.target.blur(); +}; +/** + * Handle mousedown on container element. + * This handler is purpose is to not require the use to click + * exactly on the 2px slider element, as FireFox seems to be very + * strict about this. + * + * @param {Event} event The event that fired. + * @private + * @suppress {missingProperties} + */ +MaterialSlider.prototype.onContainerMouseDown_ = function (event) { + // If this click is not on the parent element (but rather some child) + // ignore. It may still bubble up. + if (event.target !== this.element_.parentElement) { + return; + } + // Discard the original event and create a new event that + // is on the slider element. + event.preventDefault(); + var newEvent = new MouseEvent('mousedown', { + target: event.target, + buttons: event.buttons, + clientX: event.clientX, + clientY: this.element_.getBoundingClientRect().y + }); + this.element_.dispatchEvent(newEvent); +}; +/** + * Handle updating of values. + * + * @private + */ +MaterialSlider.prototype.updateValueStyles_ = function () { + // Calculate and apply percentages to div structure behind slider. + var fraction = (this.element_.value - this.element_.min) / (this.element_.max - this.element_.min); + if (fraction === 0) { + this.element_.classList.add(this.CssClasses_.IS_LOWEST_VALUE); + } else { + this.element_.classList.remove(this.CssClasses_.IS_LOWEST_VALUE); + } + if (!this.isIE_) { + this.backgroundLower_.style.flex = fraction; + this.backgroundLower_.style.webkitFlex = fraction; + this.backgroundUpper_.style.flex = 1 - fraction; + this.backgroundUpper_.style.webkitFlex = 1 - fraction; + } +}; +// Public methods. +/** + * Disable slider. + * + * @public + */ +MaterialSlider.prototype.disable = function () { + this.element_.disabled = true; +}; +MaterialSlider.prototype['disable'] = MaterialSlider.prototype.disable; +/** + * Enable slider. + * + * @public + */ +MaterialSlider.prototype.enable = function () { + this.element_.disabled = false; +}; +MaterialSlider.prototype['enable'] = MaterialSlider.prototype.enable; +/** + * Update slider value. + * + * @param {number} value The value to which to set the control (optional). + * @public + */ +MaterialSlider.prototype.change = function (value) { + if (typeof value !== 'undefined') { + this.element_.value = value; + } + this.updateValueStyles_(); +}; +MaterialSlider.prototype['change'] = MaterialSlider.prototype.change; +/** + * Initialize element. + */ +MaterialSlider.prototype.init = function () { + if (this.element_) { + if (this.isIE_) { + // Since we need to specify a very large height in IE due to + // implementation limitations, we add a parent here that trims it down to + // a reasonable size. + var containerIE = document.createElement('div'); + containerIE.classList.add(this.CssClasses_.IE_CONTAINER); + this.element_.parentElement.insertBefore(containerIE, this.element_); + this.element_.parentElement.removeChild(this.element_); + containerIE.appendChild(this.element_); + } else { + // For non-IE browsers, we need a div structure that sits behind the + // slider and allows us to style the left and right sides of it with + // different colors. + var container = document.createElement('div'); + container.classList.add(this.CssClasses_.SLIDER_CONTAINER); + this.element_.parentElement.insertBefore(container, this.element_); + this.element_.parentElement.removeChild(this.element_); + container.appendChild(this.element_); + var backgroundFlex = document.createElement('div'); + backgroundFlex.classList.add(this.CssClasses_.BACKGROUND_FLEX); + container.appendChild(backgroundFlex); + this.backgroundLower_ = document.createElement('div'); + this.backgroundLower_.classList.add(this.CssClasses_.BACKGROUND_LOWER); + backgroundFlex.appendChild(this.backgroundLower_); + this.backgroundUpper_ = document.createElement('div'); + this.backgroundUpper_.classList.add(this.CssClasses_.BACKGROUND_UPPER); + backgroundFlex.appendChild(this.backgroundUpper_); + } + this.boundInputHandler = this.onInput_.bind(this); + this.boundChangeHandler = this.onChange_.bind(this); + this.boundMouseUpHandler = this.onMouseUp_.bind(this); + this.boundContainerMouseDownHandler = this.onContainerMouseDown_.bind(this); + this.element_.addEventListener('input', this.boundInputHandler); + this.element_.addEventListener('change', this.boundChangeHandler); + this.element_.addEventListener('mouseup', this.boundMouseUpHandler); + this.element_.parentElement.addEventListener('mousedown', this.boundContainerMouseDownHandler); + this.updateValueStyles_(); + this.element_.classList.add(this.CssClasses_.IS_UPGRADED); + } +}; +/** + * Downgrade the component + * + * @private + */ +MaterialSlider.prototype.mdlDowngrade_ = function () { + this.element_.removeEventListener('input', this.boundInputHandler); + this.element_.removeEventListener('change', this.boundChangeHandler); + this.element_.removeEventListener('mouseup', this.boundMouseUpHandler); + this.element_.parentElement.removeEventListener('mousedown', this.boundContainerMouseDownHandler); +}; +/** + * Public alias for the downgrade method. + * + * @public + */ +MaterialSlider.prototype.mdlDowngrade = MaterialSlider.prototype.mdlDowngrade_; +MaterialSlider.prototype['mdlDowngrade'] = MaterialSlider.prototype.mdlDowngrade; +// The component registers itself. It can assume componentHandler is available +// in the global scope. +componentHandler.register({ + constructor: MaterialSlider, + classAsString: 'MaterialSlider', + cssClass: 'mdl-js-slider', + widget: true +}); +/** + * @license + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * Class constructor for Spinner MDL component. + * Implements MDL component design pattern defined at: + * https://github.com/jasonmayes/mdl-component-design-pattern + * + * @param {HTMLElement} element The element that will be upgraded. + * @constructor + */ +var MaterialSpinner = function MaterialSpinner(element) { + this.element_ = element; + // Initialize instance. + this.init(); +}; +window['MaterialSpinner'] = MaterialSpinner; +/** + * Store constants in one place so they can be updated easily. + * + * @enum {string | number} + * @private + */ +MaterialSpinner.prototype.Constant_ = { MDL_SPINNER_LAYER_COUNT: 4 }; +/** + * Store strings for class names defined by this component that are used in + * JavaScript. This allows us to simply change it in one place should we + * decide to modify at a later date. + * + * @enum {string} + * @private + */ +MaterialSpinner.prototype.CssClasses_ = { + MDL_SPINNER_LAYER: 'mdl-spinner__layer', + MDL_SPINNER_CIRCLE_CLIPPER: 'mdl-spinner__circle-clipper', + MDL_SPINNER_CIRCLE: 'mdl-spinner__circle', + MDL_SPINNER_GAP_PATCH: 'mdl-spinner__gap-patch', + MDL_SPINNER_LEFT: 'mdl-spinner__left', + MDL_SPINNER_RIGHT: 'mdl-spinner__right' +}; +/** + * Auxiliary method to create a spinner layer. + * + * @param {number} index Index of the layer to be created. + * @public + */ +MaterialSpinner.prototype.createLayer = function (index) { + var layer = document.createElement('div'); + layer.classList.add(this.CssClasses_.MDL_SPINNER_LAYER); + layer.classList.add(this.CssClasses_.MDL_SPINNER_LAYER + '-' + index); + var leftClipper = document.createElement('div'); + leftClipper.classList.add(this.CssClasses_.MDL_SPINNER_CIRCLE_CLIPPER); + leftClipper.classList.add(this.CssClasses_.MDL_SPINNER_LEFT); + var gapPatch = document.createElement('div'); + gapPatch.classList.add(this.CssClasses_.MDL_SPINNER_GAP_PATCH); + var rightClipper = document.createElement('div'); + rightClipper.classList.add(this.CssClasses_.MDL_SPINNER_CIRCLE_CLIPPER); + rightClipper.classList.add(this.CssClasses_.MDL_SPINNER_RIGHT); + var circleOwners = [ + leftClipper, + gapPatch, + rightClipper + ]; + for (var i = 0; i < circleOwners.length; i++) { + var circle = document.createElement('div'); + circle.classList.add(this.CssClasses_.MDL_SPINNER_CIRCLE); + circleOwners[i].appendChild(circle); + } + layer.appendChild(leftClipper); + layer.appendChild(gapPatch); + layer.appendChild(rightClipper); + this.element_.appendChild(layer); +}; +MaterialSpinner.prototype['createLayer'] = MaterialSpinner.prototype.createLayer; +/** + * Stops the spinner animation. + * Public method for users who need to stop the spinner for any reason. + * + * @public + */ +MaterialSpinner.prototype.stop = function () { + this.element_.classList.remove('is-active'); +}; +MaterialSpinner.prototype['stop'] = MaterialSpinner.prototype.stop; +/** + * Starts the spinner animation. + * Public method for users who need to manually start the spinner for any reason + * (instead of just adding the 'is-active' class to their markup). + * + * @public + */ +MaterialSpinner.prototype.start = function () { + this.element_.classList.add('is-active'); +}; +MaterialSpinner.prototype['start'] = MaterialSpinner.prototype.start; +/** + * Initialize element. + */ +MaterialSpinner.prototype.init = function () { + if (this.element_) { + for (var i = 1; i <= this.Constant_.MDL_SPINNER_LAYER_COUNT; i++) { + this.createLayer(i); + } + this.element_.classList.add('is-upgraded'); + } +}; +// The component registers itself. It can assume componentHandler is available +// in the global scope. +componentHandler.register({ + constructor: MaterialSpinner, + classAsString: 'MaterialSpinner', + cssClass: 'mdl-js-spinner', + widget: true +}); +/** + * @license + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * Class constructor for Checkbox MDL component. + * Implements MDL component design pattern defined at: + * https://github.com/jasonmayes/mdl-component-design-pattern + * + * @constructor + * @param {HTMLElement} element The element that will be upgraded. + */ +var MaterialSwitch = function MaterialSwitch(element) { + this.element_ = element; + // Initialize instance. + this.init(); +}; +window['MaterialSwitch'] = MaterialSwitch; +/** + * Store constants in one place so they can be updated easily. + * + * @enum {string | number} + * @private + */ +MaterialSwitch.prototype.Constant_ = { TINY_TIMEOUT: 0.001 }; +/** + * Store strings for class names defined by this component that are used in + * JavaScript. This allows us to simply change it in one place should we + * decide to modify at a later date. + * + * @enum {string} + * @private + */ +MaterialSwitch.prototype.CssClasses_ = { + INPUT: 'mdl-switch__input', + TRACK: 'mdl-switch__track', + THUMB: 'mdl-switch__thumb', + FOCUS_HELPER: 'mdl-switch__focus-helper', + RIPPLE_EFFECT: 'mdl-js-ripple-effect', + RIPPLE_IGNORE_EVENTS: 'mdl-js-ripple-effect--ignore-events', + RIPPLE_CONTAINER: 'mdl-switch__ripple-container', + RIPPLE_CENTER: 'mdl-ripple--center', + RIPPLE: 'mdl-ripple', + IS_FOCUSED: 'is-focused', + IS_DISABLED: 'is-disabled', + IS_CHECKED: 'is-checked' +}; +/** + * Handle change of state. + * + * @param {Event} event The event that fired. + * @private + */ +MaterialSwitch.prototype.onChange_ = function (event) { + this.updateClasses_(); +}; +/** + * Handle focus of element. + * + * @param {Event} event The event that fired. + * @private + */ +MaterialSwitch.prototype.onFocus_ = function (event) { + this.element_.classList.add(this.CssClasses_.IS_FOCUSED); +}; +/** + * Handle lost focus of element. + * + * @param {Event} event The event that fired. + * @private + */ +MaterialSwitch.prototype.onBlur_ = function (event) { + this.element_.classList.remove(this.CssClasses_.IS_FOCUSED); +}; +/** + * Handle mouseup. + * + * @param {Event} event The event that fired. + * @private + */ +MaterialSwitch.prototype.onMouseUp_ = function (event) { + this.blur_(); +}; +/** + * Handle class updates. + * + * @private + */ +MaterialSwitch.prototype.updateClasses_ = function () { + this.checkDisabled(); + this.checkToggleState(); +}; +/** + * Add blur. + * + * @private + */ +MaterialSwitch.prototype.blur_ = function () { + // TODO: figure out why there's a focus event being fired after our blur, + // so that we can avoid this hack. + window.setTimeout(function () { + this.inputElement_.blur(); + }.bind(this), this.Constant_.TINY_TIMEOUT); +}; +// Public methods. +/** + * Check the components disabled state. + * + * @public + */ +MaterialSwitch.prototype.checkDisabled = function () { + if (this.inputElement_.disabled) { + this.element_.classList.add(this.CssClasses_.IS_DISABLED); + } else { + this.element_.classList.remove(this.CssClasses_.IS_DISABLED); + } +}; +MaterialSwitch.prototype['checkDisabled'] = MaterialSwitch.prototype.checkDisabled; +/** + * Check the components toggled state. + * + * @public + */ +MaterialSwitch.prototype.checkToggleState = function () { + if (this.inputElement_.checked) { + this.element_.classList.add(this.CssClasses_.IS_CHECKED); + } else { + this.element_.classList.remove(this.CssClasses_.IS_CHECKED); + } +}; +MaterialSwitch.prototype['checkToggleState'] = MaterialSwitch.prototype.checkToggleState; +/** + * Disable switch. + * + * @public + */ +MaterialSwitch.prototype.disable = function () { + this.inputElement_.disabled = true; + this.updateClasses_(); +}; +MaterialSwitch.prototype['disable'] = MaterialSwitch.prototype.disable; +/** + * Enable switch. + * + * @public + */ +MaterialSwitch.prototype.enable = function () { + this.inputElement_.disabled = false; + this.updateClasses_(); +}; +MaterialSwitch.prototype['enable'] = MaterialSwitch.prototype.enable; +/** + * Activate switch. + * + * @public + */ +MaterialSwitch.prototype.on = function () { + this.inputElement_.checked = true; + this.updateClasses_(); +}; +MaterialSwitch.prototype['on'] = MaterialSwitch.prototype.on; +/** + * Deactivate switch. + * + * @public + */ +MaterialSwitch.prototype.off = function () { + this.inputElement_.checked = false; + this.updateClasses_(); +}; +MaterialSwitch.prototype['off'] = MaterialSwitch.prototype.off; +/** + * Initialize element. + */ +MaterialSwitch.prototype.init = function () { + if (this.element_) { + this.inputElement_ = this.element_.querySelector('.' + this.CssClasses_.INPUT); + var track = document.createElement('div'); + track.classList.add(this.CssClasses_.TRACK); + var thumb = document.createElement('div'); + thumb.classList.add(this.CssClasses_.THUMB); + var focusHelper = document.createElement('span'); + focusHelper.classList.add(this.CssClasses_.FOCUS_HELPER); + thumb.appendChild(focusHelper); + this.element_.appendChild(track); + this.element_.appendChild(thumb); + this.boundMouseUpHandler = this.onMouseUp_.bind(this); + if (this.element_.classList.contains(this.CssClasses_.RIPPLE_EFFECT)) { + this.element_.classList.add(this.CssClasses_.RIPPLE_IGNORE_EVENTS); + this.rippleContainerElement_ = document.createElement('span'); + this.rippleContainerElement_.classList.add(this.CssClasses_.RIPPLE_CONTAINER); + this.rippleContainerElement_.classList.add(this.CssClasses_.RIPPLE_EFFECT); + this.rippleContainerElement_.classList.add(this.CssClasses_.RIPPLE_CENTER); + this.rippleContainerElement_.addEventListener('mouseup', this.boundMouseUpHandler); + var ripple = document.createElement('span'); + ripple.classList.add(this.CssClasses_.RIPPLE); + this.rippleContainerElement_.appendChild(ripple); + this.element_.appendChild(this.rippleContainerElement_); + } + this.boundChangeHandler = this.onChange_.bind(this); + this.boundFocusHandler = this.onFocus_.bind(this); + this.boundBlurHandler = this.onBlur_.bind(this); + this.inputElement_.addEventListener('change', this.boundChangeHandler); + this.inputElement_.addEventListener('focus', this.boundFocusHandler); + this.inputElement_.addEventListener('blur', this.boundBlurHandler); + this.element_.addEventListener('mouseup', this.boundMouseUpHandler); + this.updateClasses_(); + this.element_.classList.add('is-upgraded'); + } +}; +/** + * Downgrade the component. + * + * @private + */ +MaterialSwitch.prototype.mdlDowngrade_ = function () { + if (this.rippleContainerElement_) { + this.rippleContainerElement_.removeEventListener('mouseup', this.boundMouseUpHandler); + } + this.inputElement_.removeEventListener('change', this.boundChangeHandler); + this.inputElement_.removeEventListener('focus', this.boundFocusHandler); + this.inputElement_.removeEventListener('blur', this.boundBlurHandler); + this.element_.removeEventListener('mouseup', this.boundMouseUpHandler); +}; +/** + * Public alias for the downgrade method. + * + * @public + */ +MaterialSwitch.prototype.mdlDowngrade = MaterialSwitch.prototype.mdlDowngrade_; +MaterialSwitch.prototype['mdlDowngrade'] = MaterialSwitch.prototype.mdlDowngrade; +// The component registers itself. It can assume componentHandler is available +// in the global scope. +componentHandler.register({ + constructor: MaterialSwitch, + classAsString: 'MaterialSwitch', + cssClass: 'mdl-js-switch', + widget: true +}); +/** + * @license + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * Class constructor for Tabs MDL component. + * Implements MDL component design pattern defined at: + * https://github.com/jasonmayes/mdl-component-design-pattern + * + * @constructor + * @param {HTMLElement} element The element that will be upgraded. + */ +var MaterialTabs = function MaterialTabs(element) { + // Stores the HTML element. + this.element_ = element; + // Initialize instance. + this.init(); +}; +window['MaterialTabs'] = MaterialTabs; +/** + * Store constants in one place so they can be updated easily. + * + * @enum {string} + * @private + */ +MaterialTabs.prototype.Constant_ = {}; +/** + * Store strings for class names defined by this component that are used in + * JavaScript. This allows us to simply change it in one place should we + * decide to modify at a later date. + * + * @enum {string} + * @private + */ +MaterialTabs.prototype.CssClasses_ = { + TAB_CLASS: 'mdl-tabs__tab', + PANEL_CLASS: 'mdl-tabs__panel', + ACTIVE_CLASS: 'is-active', + UPGRADED_CLASS: 'is-upgraded', + MDL_JS_RIPPLE_EFFECT: 'mdl-js-ripple-effect', + MDL_RIPPLE_CONTAINER: 'mdl-tabs__ripple-container', + MDL_RIPPLE: 'mdl-ripple', + MDL_JS_RIPPLE_EFFECT_IGNORE_EVENTS: 'mdl-js-ripple-effect--ignore-events' +}; +/** + * Handle clicks to a tabs component + * + * @private + */ +MaterialTabs.prototype.initTabs_ = function () { + if (this.element_.classList.contains(this.CssClasses_.MDL_JS_RIPPLE_EFFECT)) { + this.element_.classList.add(this.CssClasses_.MDL_JS_RIPPLE_EFFECT_IGNORE_EVENTS); + } + // Select element tabs, document panels + this.tabs_ = this.element_.querySelectorAll('.' + this.CssClasses_.TAB_CLASS); + this.panels_ = this.element_.querySelectorAll('.' + this.CssClasses_.PANEL_CLASS); + // Create new tabs for each tab element + for (var i = 0; i < this.tabs_.length; i++) { + new MaterialTab(this.tabs_[i], this); + } + this.element_.classList.add(this.CssClasses_.UPGRADED_CLASS); +}; +/** + * Reset tab state, dropping active classes + * + * @private + */ +MaterialTabs.prototype.resetTabState_ = function () { + for (var k = 0; k < this.tabs_.length; k++) { + this.tabs_[k].classList.remove(this.CssClasses_.ACTIVE_CLASS); + } +}; +/** + * Reset panel state, droping active classes + * + * @private + */ +MaterialTabs.prototype.resetPanelState_ = function () { + for (var j = 0; j < this.panels_.length; j++) { + this.panels_[j].classList.remove(this.CssClasses_.ACTIVE_CLASS); + } +}; +/** + * Initialize element. + */ +MaterialTabs.prototype.init = function () { + if (this.element_) { + this.initTabs_(); + } +}; +/** + * Constructor for an individual tab. + * + * @constructor + * @param {HTMLElement} tab The HTML element for the tab. + * @param {MaterialTabs} ctx The MaterialTabs object that owns the tab. + */ +function MaterialTab(tab, ctx) { + if (tab) { + if (ctx.element_.classList.contains(ctx.CssClasses_.MDL_JS_RIPPLE_EFFECT)) { + var rippleContainer = document.createElement('span'); + rippleContainer.classList.add(ctx.CssClasses_.MDL_RIPPLE_CONTAINER); + rippleContainer.classList.add(ctx.CssClasses_.MDL_JS_RIPPLE_EFFECT); + var ripple = document.createElement('span'); + ripple.classList.add(ctx.CssClasses_.MDL_RIPPLE); + rippleContainer.appendChild(ripple); + tab.appendChild(rippleContainer); + } + tab.addEventListener('click', function (e) { + e.preventDefault(); + var href = tab.href.split('#')[1]; + var panel = ctx.element_.querySelector('#' + href); + ctx.resetTabState_(); + ctx.resetPanelState_(); + tab.classList.add(ctx.CssClasses_.ACTIVE_CLASS); + panel.classList.add(ctx.CssClasses_.ACTIVE_CLASS); + }); + } +} +// The component registers itself. It can assume componentHandler is available +// in the global scope. +componentHandler.register({ + constructor: MaterialTabs, + classAsString: 'MaterialTabs', + cssClass: 'mdl-js-tabs' +}); +/** + * @license + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * Class constructor for Textfield MDL component. + * Implements MDL component design pattern defined at: + * https://github.com/jasonmayes/mdl-component-design-pattern + * + * @constructor + * @param {HTMLElement} element The element that will be upgraded. + */ +var MaterialTextfield = function MaterialTextfield(element) { + this.element_ = element; + this.maxRows = this.Constant_.NO_MAX_ROWS; + // Initialize instance. + this.init(); +}; +window['MaterialTextfield'] = MaterialTextfield; +/** + * Store constants in one place so they can be updated easily. + * + * @enum {string | number} + * @private + */ +MaterialTextfield.prototype.Constant_ = { + NO_MAX_ROWS: -1, + MAX_ROWS_ATTRIBUTE: 'maxrows' +}; +/** + * Store strings for class names defined by this component that are used in + * JavaScript. This allows us to simply change it in one place should we + * decide to modify at a later date. + * + * @enum {string} + * @private + */ +MaterialTextfield.prototype.CssClasses_ = { + LABEL: 'mdl-textfield__label', + INPUT: 'mdl-textfield__input', + IS_DIRTY: 'is-dirty', + IS_FOCUSED: 'is-focused', + IS_DISABLED: 'is-disabled', + IS_INVALID: 'is-invalid', + IS_UPGRADED: 'is-upgraded' +}; +/** + * Handle input being entered. + * + * @param {Event} event The event that fired. + * @private + */ +MaterialTextfield.prototype.onKeyDown_ = function (event) { + var currentRowCount = event.target.value.split('\n').length; + if (event.keyCode === 13) { + if (currentRowCount >= this.maxRows) { + event.preventDefault(); + } + } +}; +/** + * Handle focus. + * + * @param {Event} event The event that fired. + * @private + */ +MaterialTextfield.prototype.onFocus_ = function (event) { + this.element_.classList.add(this.CssClasses_.IS_FOCUSED); +}; +/** + * Handle lost focus. + * + * @param {Event} event The event that fired. + * @private + */ +MaterialTextfield.prototype.onBlur_ = function (event) { + this.element_.classList.remove(this.CssClasses_.IS_FOCUSED); +}; +/** + * Handle class updates. + * + * @private + */ +MaterialTextfield.prototype.updateClasses_ = function () { + this.checkDisabled(); + this.checkValidity(); + this.checkDirty(); +}; +// Public methods. +/** + * Check the disabled state and update field accordingly. + * + * @public + */ +MaterialTextfield.prototype.checkDisabled = function () { + if (this.input_.disabled) { + this.element_.classList.add(this.CssClasses_.IS_DISABLED); + } else { + this.element_.classList.remove(this.CssClasses_.IS_DISABLED); + } +}; +MaterialTextfield.prototype['checkDisabled'] = MaterialTextfield.prototype.checkDisabled; +/** + * Check the validity state and update field accordingly. + * + * @public + */ +MaterialTextfield.prototype.checkValidity = function () { + if (this.input_.validity) { + if (this.input_.validity.valid) { + this.element_.classList.remove(this.CssClasses_.IS_INVALID); + } else { + this.element_.classList.add(this.CssClasses_.IS_INVALID); + } + } +}; +MaterialTextfield.prototype['checkValidity'] = MaterialTextfield.prototype.checkValidity; +/** + * Check the dirty state and update field accordingly. + * + * @public + */ +MaterialTextfield.prototype.checkDirty = function () { + if (this.input_.value && this.input_.value.length > 0) { + this.element_.classList.add(this.CssClasses_.IS_DIRTY); + } else { + this.element_.classList.remove(this.CssClasses_.IS_DIRTY); + } +}; +MaterialTextfield.prototype['checkDirty'] = MaterialTextfield.prototype.checkDirty; +/** + * Disable text field. + * + * @public + */ +MaterialTextfield.prototype.disable = function () { + this.input_.disabled = true; + this.updateClasses_(); +}; +MaterialTextfield.prototype['disable'] = MaterialTextfield.prototype.disable; +/** + * Enable text field. + * + * @public + */ +MaterialTextfield.prototype.enable = function () { + this.input_.disabled = false; + this.updateClasses_(); +}; +MaterialTextfield.prototype['enable'] = MaterialTextfield.prototype.enable; +/** + * Update text field value. + * + * @param {string} value The value to which to set the control (optional). + * @public + */ +MaterialTextfield.prototype.change = function (value) { + this.input_.value = value || ''; + this.updateClasses_(); +}; +MaterialTextfield.prototype['change'] = MaterialTextfield.prototype.change; +/** + * Initialize element. + */ +MaterialTextfield.prototype.init = function () { + if (this.element_) { + this.label_ = this.element_.querySelector('.' + this.CssClasses_.LABEL); + this.input_ = this.element_.querySelector('.' + this.CssClasses_.INPUT); + if (this.input_) { + if (this.input_.hasAttribute(this.Constant_.MAX_ROWS_ATTRIBUTE)) { + this.maxRows = parseInt(this.input_.getAttribute(this.Constant_.MAX_ROWS_ATTRIBUTE), 10); + if (isNaN(this.maxRows)) { + this.maxRows = this.Constant_.NO_MAX_ROWS; + } + } + this.boundUpdateClassesHandler = this.updateClasses_.bind(this); + this.boundFocusHandler = this.onFocus_.bind(this); + this.boundBlurHandler = this.onBlur_.bind(this); + this.input_.addEventListener('input', this.boundUpdateClassesHandler); + this.input_.addEventListener('focus', this.boundFocusHandler); + this.input_.addEventListener('blur', this.boundBlurHandler); + if (this.maxRows !== this.Constant_.NO_MAX_ROWS) { + // TODO: This should handle pasting multi line text. + // Currently doesn't. + this.boundKeyDownHandler = this.onKeyDown_.bind(this); + this.input_.addEventListener('keydown', this.boundKeyDownHandler); + } + var invalid = this.element_.classList.contains(this.CssClasses_.IS_INVALID); + this.updateClasses_(); + this.element_.classList.add(this.CssClasses_.IS_UPGRADED); + if (invalid) { + this.element_.classList.add(this.CssClasses_.IS_INVALID); + } + } + } +}; +/** + * Downgrade the component + * + * @private + */ +MaterialTextfield.prototype.mdlDowngrade_ = function () { + this.input_.removeEventListener('input', this.boundUpdateClassesHandler); + this.input_.removeEventListener('focus', this.boundFocusHandler); + this.input_.removeEventListener('blur', this.boundBlurHandler); + if (this.boundKeyDownHandler) { + this.input_.removeEventListener('keydown', this.boundKeyDownHandler); + } +}; +/** + * Public alias for the downgrade method. + * + * @public + */ +MaterialTextfield.prototype.mdlDowngrade = MaterialTextfield.prototype.mdlDowngrade_; +MaterialTextfield.prototype['mdlDowngrade'] = MaterialTextfield.prototype.mdlDowngrade; +// The component registers itself. It can assume componentHandler is available +// in the global scope. +componentHandler.register({ + constructor: MaterialTextfield, + classAsString: 'MaterialTextfield', + cssClass: 'mdl-js-textfield', + widget: true +}); +/** + * @license + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * Class constructor for Tooltip MDL component. + * Implements MDL component design pattern defined at: + * https://github.com/jasonmayes/mdl-component-design-pattern + * + * @constructor + * @param {HTMLElement} element The element that will be upgraded. + */ +var MaterialTooltip = function MaterialTooltip(element) { + this.element_ = element; + // Initialize instance. + this.init(); +}; +window['MaterialTooltip'] = MaterialTooltip; +/** + * Store constants in one place so they can be updated easily. + * + * @enum {string | number} + * @private + */ +MaterialTooltip.prototype.Constant_ = {}; +/** + * Store strings for class names defined by this component that are used in + * JavaScript. This allows us to simply change it in one place should we + * decide to modify at a later date. + * + * @enum {string} + * @private + */ +MaterialTooltip.prototype.CssClasses_ = { IS_ACTIVE: 'is-active' }; +/** + * Handle mouseenter for tooltip. + * + * @param {Event} event The event that fired. + * @private + */ +MaterialTooltip.prototype.handleMouseEnter_ = function (event) { + event.stopPropagation(); + var props = event.target.getBoundingClientRect(); + var left = props.left + props.width / 2; + var marginLeft = -1 * (this.element_.offsetWidth / 2); + if (left + marginLeft < 0) { + this.element_.style.left = 0; + this.element_.style.marginLeft = 0; + } else { + this.element_.style.left = left + 'px'; + this.element_.style.marginLeft = marginLeft + 'px'; + } + this.element_.style.top = props.top + props.height + 10 + 'px'; + this.element_.classList.add(this.CssClasses_.IS_ACTIVE); + window.addEventListener('scroll', this.boundMouseLeaveHandler, false); + window.addEventListener('touchmove', this.boundMouseLeaveHandler, false); +}; +/** + * Handle mouseleave for tooltip. + * + * @param {Event} event The event that fired. + * @private + */ +MaterialTooltip.prototype.handleMouseLeave_ = function (event) { + event.stopPropagation(); + this.element_.classList.remove(this.CssClasses_.IS_ACTIVE); + window.removeEventListener('scroll', this.boundMouseLeaveHandler); + window.removeEventListener('touchmove', this.boundMouseLeaveHandler, false); +}; +/** + * Initialize element. + */ +MaterialTooltip.prototype.init = function () { + if (this.element_) { + var forElId = this.element_.getAttribute('for'); + if (forElId) { + this.forElement_ = document.getElementById(forElId); + } + if (this.forElement_) { + // Tabindex needs to be set for `blur` events to be emitted + if (!this.forElement_.hasAttribute('tabindex')) { + this.forElement_.setAttribute('tabindex', '0'); + } + this.boundMouseEnterHandler = this.handleMouseEnter_.bind(this); + this.boundMouseLeaveHandler = this.handleMouseLeave_.bind(this); + this.forElement_.addEventListener('mouseenter', this.boundMouseEnterHandler, false); + this.forElement_.addEventListener('click', this.boundMouseEnterHandler, false); + this.forElement_.addEventListener('blur', this.boundMouseLeaveHandler); + this.forElement_.addEventListener('touchstart', this.boundMouseEnterHandler, false); + this.forElement_.addEventListener('mouseleave', this.boundMouseLeaveHandler); + } + } +}; +/** + * Downgrade the component + * + * @private + */ +MaterialTooltip.prototype.mdlDowngrade_ = function () { + if (this.forElement_) { + this.forElement_.removeEventListener('mouseenter', this.boundMouseEnterHandler, false); + this.forElement_.removeEventListener('click', this.boundMouseEnterHandler, false); + this.forElement_.removeEventListener('touchstart', this.boundMouseEnterHandler, false); + this.forElement_.removeEventListener('mouseleave', this.boundMouseLeaveHandler); + } +}; +/** + * Public alias for the downgrade method. + * + * @public + */ +MaterialTooltip.prototype.mdlDowngrade = MaterialTooltip.prototype.mdlDowngrade_; +MaterialTooltip.prototype['mdlDowngrade'] = MaterialTooltip.prototype.mdlDowngrade; +// The component registers itself. It can assume componentHandler is available +// in the global scope. +componentHandler.register({ + constructor: MaterialTooltip, + classAsString: 'MaterialTooltip', + cssClass: 'mdl-tooltip' +}); +/** + * @license + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * Class constructor for Layout MDL component. + * Implements MDL component design pattern defined at: + * https://github.com/jasonmayes/mdl-component-design-pattern + * + * @constructor + * @param {HTMLElement} element The element that will be upgraded. + */ +var MaterialLayout = function MaterialLayout(element) { + this.element_ = element; + // Initialize instance. + this.init(); +}; +window['MaterialLayout'] = MaterialLayout; +/** + * Store constants in one place so they can be updated easily. + * + * @enum {string | number} + * @private + */ +MaterialLayout.prototype.Constant_ = { + MAX_WIDTH: '(max-width: 1024px)', + TAB_SCROLL_PIXELS: 100, + MENU_ICON: 'menu', + CHEVRON_LEFT: 'chevron_left', + CHEVRON_RIGHT: 'chevron_right' +}; +/** + * Modes. + * + * @enum {number} + * @private + */ +MaterialLayout.prototype.Mode_ = { + STANDARD: 0, + SEAMED: 1, + WATERFALL: 2, + SCROLL: 3 +}; +/** + * Store strings for class names defined by this component that are used in + * JavaScript. This allows us to simply change it in one place should we + * decide to modify at a later date. + * + * @enum {string} + * @private + */ +MaterialLayout.prototype.CssClasses_ = { + CONTAINER: 'mdl-layout__container', + HEADER: 'mdl-layout__header', + DRAWER: 'mdl-layout__drawer', + CONTENT: 'mdl-layout__content', + DRAWER_BTN: 'mdl-layout__drawer-button', + ICON: 'material-icons', + JS_RIPPLE_EFFECT: 'mdl-js-ripple-effect', + RIPPLE_CONTAINER: 'mdl-layout__tab-ripple-container', + RIPPLE: 'mdl-ripple', + RIPPLE_IGNORE_EVENTS: 'mdl-js-ripple-effect--ignore-events', + HEADER_SEAMED: 'mdl-layout__header--seamed', + HEADER_WATERFALL: 'mdl-layout__header--waterfall', + HEADER_SCROLL: 'mdl-layout__header--scroll', + FIXED_HEADER: 'mdl-layout--fixed-header', + OBFUSCATOR: 'mdl-layout__obfuscator', + TAB_BAR: 'mdl-layout__tab-bar', + TAB_CONTAINER: 'mdl-layout__tab-bar-container', + TAB: 'mdl-layout__tab', + TAB_BAR_BUTTON: 'mdl-layout__tab-bar-button', + TAB_BAR_LEFT_BUTTON: 'mdl-layout__tab-bar-left-button', + TAB_BAR_RIGHT_BUTTON: 'mdl-layout__tab-bar-right-button', + PANEL: 'mdl-layout__tab-panel', + HAS_DRAWER: 'has-drawer', + HAS_TABS: 'has-tabs', + HAS_SCROLLING_HEADER: 'has-scrolling-header', + CASTING_SHADOW: 'is-casting-shadow', + IS_COMPACT: 'is-compact', + IS_SMALL_SCREEN: 'is-small-screen', + IS_DRAWER_OPEN: 'is-visible', + IS_ACTIVE: 'is-active', + IS_UPGRADED: 'is-upgraded', + IS_ANIMATING: 'is-animating', + ON_LARGE_SCREEN: 'mdl-layout--large-screen-only', + ON_SMALL_SCREEN: 'mdl-layout--small-screen-only' +}; +/** + * Handles scrolling on the content. + * + * @private + */ +MaterialLayout.prototype.contentScrollHandler_ = function () { + if (this.header_.classList.contains(this.CssClasses_.IS_ANIMATING)) { + return; + } + if (this.content_.scrollTop > 0 && !this.header_.classList.contains(this.CssClasses_.IS_COMPACT)) { + this.header_.classList.add(this.CssClasses_.CASTING_SHADOW); + this.header_.classList.add(this.CssClasses_.IS_COMPACT); + this.header_.classList.add(this.CssClasses_.IS_ANIMATING); + } else if (this.content_.scrollTop <= 0 && this.header_.classList.contains(this.CssClasses_.IS_COMPACT)) { + this.header_.classList.remove(this.CssClasses_.CASTING_SHADOW); + this.header_.classList.remove(this.CssClasses_.IS_COMPACT); + this.header_.classList.add(this.CssClasses_.IS_ANIMATING); + } +}; +/** + * Handles changes in screen size. + * + * @private + */ +MaterialLayout.prototype.screenSizeHandler_ = function () { + if (this.screenSizeMediaQuery_.matches) { + this.element_.classList.add(this.CssClasses_.IS_SMALL_SCREEN); + } else { + this.element_.classList.remove(this.CssClasses_.IS_SMALL_SCREEN); + // Collapse drawer (if any) when moving to a large screen size. + if (this.drawer_) { + this.drawer_.classList.remove(this.CssClasses_.IS_DRAWER_OPEN); + this.obfuscator_.classList.remove(this.CssClasses_.IS_DRAWER_OPEN); + } + } +}; +/** + * Handles toggling of the drawer. + * + * @private + */ +MaterialLayout.prototype.drawerToggleHandler_ = function () { + this.drawer_.classList.toggle(this.CssClasses_.IS_DRAWER_OPEN); + this.obfuscator_.classList.toggle(this.CssClasses_.IS_DRAWER_OPEN); +}; +/** + * Handles (un)setting the `is-animating` class + * + * @private + */ +MaterialLayout.prototype.headerTransitionEndHandler_ = function () { + this.header_.classList.remove(this.CssClasses_.IS_ANIMATING); +}; +/** + * Handles expanding the header on click + * + * @private + */ +MaterialLayout.prototype.headerClickHandler_ = function () { + if (this.header_.classList.contains(this.CssClasses_.IS_COMPACT)) { + this.header_.classList.remove(this.CssClasses_.IS_COMPACT); + this.header_.classList.add(this.CssClasses_.IS_ANIMATING); + } +}; +/** + * Reset tab state, dropping active classes + * + * @private + */ +MaterialLayout.prototype.resetTabState_ = function (tabBar) { + for (var k = 0; k < tabBar.length; k++) { + tabBar[k].classList.remove(this.CssClasses_.IS_ACTIVE); + } +}; +/** + * Reset panel state, droping active classes + * + * @private + */ +MaterialLayout.prototype.resetPanelState_ = function (panels) { + for (var j = 0; j < panels.length; j++) { + panels[j].classList.remove(this.CssClasses_.IS_ACTIVE); + } +}; +/** + * Initialize element. + */ +MaterialLayout.prototype.init = function () { + if (this.element_) { + var container = document.createElement('div'); + container.classList.add(this.CssClasses_.CONTAINER); + this.element_.parentElement.insertBefore(container, this.element_); + this.element_.parentElement.removeChild(this.element_); + container.appendChild(this.element_); + var directChildren = this.element_.childNodes; + var numChildren = directChildren.length; + for (var c = 0; c < numChildren; c++) { + var child = directChildren[c]; + if (child.classList && child.classList.contains(this.CssClasses_.HEADER)) { + this.header_ = child; + } + if (child.classList && child.classList.contains(this.CssClasses_.DRAWER)) { + this.drawer_ = child; + } + if (child.classList && child.classList.contains(this.CssClasses_.CONTENT)) { + this.content_ = child; + } + } + if (this.header_) { + this.tabBar_ = this.header_.querySelector('.' + this.CssClasses_.TAB_BAR); + } + var mode = this.Mode_.STANDARD; + if (this.header_) { + if (this.header_.classList.contains(this.CssClasses_.HEADER_SEAMED)) { + mode = this.Mode_.SEAMED; + } else if (this.header_.classList.contains(this.CssClasses_.HEADER_WATERFALL)) { + mode = this.Mode_.WATERFALL; + this.header_.addEventListener('transitionend', this.headerTransitionEndHandler_.bind(this)); + this.header_.addEventListener('click', this.headerClickHandler_.bind(this)); + } else if (this.header_.classList.contains(this.CssClasses_.HEADER_SCROLL)) { + mode = this.Mode_.SCROLL; + container.classList.add(this.CssClasses_.HAS_SCROLLING_HEADER); + } + if (mode === this.Mode_.STANDARD) { + this.header_.classList.add(this.CssClasses_.CASTING_SHADOW); + if (this.tabBar_) { + this.tabBar_.classList.add(this.CssClasses_.CASTING_SHADOW); + } + } else if (mode === this.Mode_.SEAMED || mode === this.Mode_.SCROLL) { + this.header_.classList.remove(this.CssClasses_.CASTING_SHADOW); + if (this.tabBar_) { + this.tabBar_.classList.remove(this.CssClasses_.CASTING_SHADOW); + } + } else if (mode === this.Mode_.WATERFALL) { + // Add and remove shadows depending on scroll position. + // Also add/remove auxiliary class for styling of the compact version of + // the header. + this.content_.addEventListener('scroll', this.contentScrollHandler_.bind(this)); + this.contentScrollHandler_(); + } + } + // Add drawer toggling button to our layout, if we have an openable drawer. + if (this.drawer_) { + var drawerButton = this.element_.querySelector('.' + this.CssClasses_.DRAWER_BTN); + if (!drawerButton) { + drawerButton = document.createElement('div'); + drawerButton.classList.add(this.CssClasses_.DRAWER_BTN); + var drawerButtonIcon = document.createElement('i'); + drawerButtonIcon.classList.add(this.CssClasses_.ICON); + drawerButtonIcon.textContent = this.Constant_.MENU_ICON; + drawerButton.appendChild(drawerButtonIcon); + } + if (this.drawer_.classList.contains(this.CssClasses_.ON_LARGE_SCREEN)) { + //If drawer has ON_LARGE_SCREEN class then add it to the drawer toggle button as well. + drawerButton.classList.add(this.CssClasses_.ON_LARGE_SCREEN); + } else if (this.drawer_.classList.contains(this.CssClasses_.ON_SMALL_SCREEN)) { + //If drawer has ON_SMALL_SCREEN class then add it to the drawer toggle button as well. + drawerButton.classList.add(this.CssClasses_.ON_SMALL_SCREEN); + } + drawerButton.addEventListener('click', this.drawerToggleHandler_.bind(this)); + // Add a class if the layout has a drawer, for altering the left padding. + // Adds the HAS_DRAWER to the elements since this.header_ may or may + // not be present. + this.element_.classList.add(this.CssClasses_.HAS_DRAWER); + // If we have a fixed header, add the button to the header rather than + // the layout. + if (this.element_.classList.contains(this.CssClasses_.FIXED_HEADER)) { + this.header_.insertBefore(drawerButton, this.header_.firstChild); + } else { + this.element_.insertBefore(drawerButton, this.content_); + } + var obfuscator = document.createElement('div'); + obfuscator.classList.add(this.CssClasses_.OBFUSCATOR); + this.element_.appendChild(obfuscator); + obfuscator.addEventListener('click', this.drawerToggleHandler_.bind(this)); + this.obfuscator_ = obfuscator; + } + // Keep an eye on screen size, and add/remove auxiliary class for styling + // of small screens. + this.screenSizeMediaQuery_ = window.matchMedia(this.Constant_.MAX_WIDTH); + this.screenSizeMediaQuery_.addListener(this.screenSizeHandler_.bind(this)); + this.screenSizeHandler_(); + // Initialize tabs, if any. + if (this.header_ && this.tabBar_) { + this.element_.classList.add(this.CssClasses_.HAS_TABS); + var tabContainer = document.createElement('div'); + tabContainer.classList.add(this.CssClasses_.TAB_CONTAINER); + this.header_.insertBefore(tabContainer, this.tabBar_); + this.header_.removeChild(this.tabBar_); + var leftButton = document.createElement('div'); + leftButton.classList.add(this.CssClasses_.TAB_BAR_BUTTON); + leftButton.classList.add(this.CssClasses_.TAB_BAR_LEFT_BUTTON); + var leftButtonIcon = document.createElement('i'); + leftButtonIcon.classList.add(this.CssClasses_.ICON); + leftButtonIcon.textContent = this.Constant_.CHEVRON_LEFT; + leftButton.appendChild(leftButtonIcon); + leftButton.addEventListener('click', function () { + this.tabBar_.scrollLeft -= this.Constant_.TAB_SCROLL_PIXELS; + }.bind(this)); + var rightButton = document.createElement('div'); + rightButton.classList.add(this.CssClasses_.TAB_BAR_BUTTON); + rightButton.classList.add(this.CssClasses_.TAB_BAR_RIGHT_BUTTON); + var rightButtonIcon = document.createElement('i'); + rightButtonIcon.classList.add(this.CssClasses_.ICON); + rightButtonIcon.textContent = this.Constant_.CHEVRON_RIGHT; + rightButton.appendChild(rightButtonIcon); + rightButton.addEventListener('click', function () { + this.tabBar_.scrollLeft += this.Constant_.TAB_SCROLL_PIXELS; + }.bind(this)); + tabContainer.appendChild(leftButton); + tabContainer.appendChild(this.tabBar_); + tabContainer.appendChild(rightButton); + // Add and remove buttons depending on scroll position. + var tabScrollHandler = function () { + if (this.tabBar_.scrollLeft > 0) { + leftButton.classList.add(this.CssClasses_.IS_ACTIVE); + } else { + leftButton.classList.remove(this.CssClasses_.IS_ACTIVE); + } + if (this.tabBar_.scrollLeft < this.tabBar_.scrollWidth - this.tabBar_.offsetWidth) { + rightButton.classList.add(this.CssClasses_.IS_ACTIVE); + } else { + rightButton.classList.remove(this.CssClasses_.IS_ACTIVE); + } + }.bind(this); + this.tabBar_.addEventListener('scroll', tabScrollHandler); + tabScrollHandler(); + if (this.tabBar_.classList.contains(this.CssClasses_.JS_RIPPLE_EFFECT)) { + this.tabBar_.classList.add(this.CssClasses_.RIPPLE_IGNORE_EVENTS); + } + // Select element tabs, document panels + var tabs = this.tabBar_.querySelectorAll('.' + this.CssClasses_.TAB); + var panels = this.content_.querySelectorAll('.' + this.CssClasses_.PANEL); + // Create new tabs for each tab element + for (var i = 0; i < tabs.length; i++) { + new MaterialLayoutTab(tabs[i], tabs, panels, this); + } + } + this.element_.classList.add(this.CssClasses_.IS_UPGRADED); + } +}; +/** + * Constructor for an individual tab. + * + * @constructor + * @param {HTMLElement} tab The HTML element for the tab. + * @param {!Array} tabs Array with HTML elements for all tabs. + * @param {!Array} panels Array with HTML elements for all panels. + * @param {MaterialLayout} layout The MaterialLayout object that owns the tab. + */ +function MaterialLayoutTab(tab, tabs, panels, layout) { + if (layout.tabBar_.classList.contains(layout.CssClasses_.JS_RIPPLE_EFFECT)) { + var rippleContainer = document.createElement('span'); + rippleContainer.classList.add(layout.CssClasses_.RIPPLE_CONTAINER); + rippleContainer.classList.add(layout.CssClasses_.JS_RIPPLE_EFFECT); + var ripple = document.createElement('span'); + ripple.classList.add(layout.CssClasses_.RIPPLE); + rippleContainer.appendChild(ripple); + tab.appendChild(rippleContainer); + } + tab.addEventListener('click', function (e) { + e.preventDefault(); + var href = tab.href.split('#')[1]; + var panel = layout.content_.querySelector('#' + href); + layout.resetTabState_(tabs); + layout.resetPanelState_(panels); + tab.classList.add(layout.CssClasses_.IS_ACTIVE); + panel.classList.add(layout.CssClasses_.IS_ACTIVE); + }); +} +// The component registers itself. It can assume componentHandler is available +// in the global scope. +componentHandler.register({ + constructor: MaterialLayout, + classAsString: 'MaterialLayout', + cssClass: 'mdl-js-layout' +}); +/** + * @license + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * Class constructor for Data Table Card MDL component. + * Implements MDL component design pattern defined at: + * https://github.com/jasonmayes/mdl-component-design-pattern + * + * @constructor + * @param {HTMLElement} element The element that will be upgraded. + */ +var MaterialDataTable = function MaterialDataTable(element) { + this.element_ = element; + // Initialize instance. + this.init(); +}; +window['MaterialDataTable'] = MaterialDataTable; +/** + * Store constants in one place so they can be updated easily. + * + * @enum {string | number} + * @private + */ +MaterialDataTable.prototype.Constant_ = {}; +/** + * Store strings for class names defined by this component that are used in + * JavaScript. This allows us to simply change it in one place should we + * decide to modify at a later date. + * + * @enum {string} + * @private + */ +MaterialDataTable.prototype.CssClasses_ = { + DATA_TABLE: 'mdl-data-table', + SELECTABLE: 'mdl-data-table--selectable', + SELECT_ELEMENT: 'mdl-data-table__select', + IS_SELECTED: 'is-selected', + IS_UPGRADED: 'is-upgraded' +}; +/** + * Generates and returns a function that toggles the selection state of a + * single row (or multiple rows). + * + * @param {Element} checkbox Checkbox that toggles the selection state. + * @param {HTMLElement} row Row to toggle when checkbox changes. + * @param {(Array|NodeList)=} opt_rows Rows to toggle when checkbox changes. + * @private + */ +MaterialDataTable.prototype.selectRow_ = function (checkbox, row, opt_rows) { + if (row) { + return function () { + if (checkbox.checked) { + row.classList.add(this.CssClasses_.IS_SELECTED); + } else { + row.classList.remove(this.CssClasses_.IS_SELECTED); + } + }.bind(this); + } + if (opt_rows) { + return function () { + var i; + var el; + if (checkbox.checked) { + for (i = 0; i < opt_rows.length; i++) { + el = opt_rows[i].querySelector('td').querySelector('.mdl-checkbox'); + el['MaterialCheckbox'].check(); + opt_rows[i].classList.add(this.CssClasses_.IS_SELECTED); + } + } else { + for (i = 0; i < opt_rows.length; i++) { + el = opt_rows[i].querySelector('td').querySelector('.mdl-checkbox'); + el['MaterialCheckbox'].uncheck(); + opt_rows[i].classList.remove(this.CssClasses_.IS_SELECTED); + } + } + }.bind(this); + } +}; +/** + * Creates a checkbox for a single or or multiple rows and hooks up the + * event handling. + * + * @param {HTMLElement} row Row to toggle when checkbox changes. + * @param {(Array|NodeList)=} opt_rows Rows to toggle when checkbox changes. + * @private + */ +MaterialDataTable.prototype.createCheckbox_ = function (row, opt_rows) { + var label = document.createElement('label'); + var labelClasses = [ + 'mdl-checkbox', + 'mdl-js-checkbox', + 'mdl-js-ripple-effect', + this.CssClasses_.SELECT_ELEMENT + ]; + label.className = labelClasses.join(' '); + var checkbox = document.createElement('input'); + checkbox.type = 'checkbox'; + checkbox.classList.add('mdl-checkbox__input'); + checkbox.addEventListener('change', this.selectRow_(checkbox, row, opt_rows)); + label.appendChild(checkbox); + componentHandler.upgradeElement(label, 'MaterialCheckbox'); + return label; +}; +/** + * Initialize element. + */ +MaterialDataTable.prototype.init = function () { + if (this.element_) { + var firstHeader = this.element_.querySelector('th'); + var rows = this.element_.querySelector('tbody').querySelectorAll('tr'); + if (this.element_.classList.contains(this.CssClasses_.SELECTABLE)) { + var th = document.createElement('th'); + var headerCheckbox = this.createCheckbox_(null, rows); + th.appendChild(headerCheckbox); + firstHeader.parentElement.insertBefore(th, firstHeader); + for (var i = 0; i < rows.length; i++) { + var firstCell = rows[i].querySelector('td'); + if (firstCell) { + var td = document.createElement('td'); + var rowCheckbox = this.createCheckbox_(rows[i]); + td.appendChild(rowCheckbox); + rows[i].insertBefore(td, firstCell); + } + } + } + this.element_.classList.add(this.CssClasses_.IS_UPGRADED); + } +}; +// The component registers itself. It can assume componentHandler is available +// in the global scope. +componentHandler.register({ + constructor: MaterialDataTable, + classAsString: 'MaterialDataTable', + cssClass: 'mdl-js-data-table' +}); +/** + * @license + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * Class constructor for Ripple MDL component. + * Implements MDL component design pattern defined at: + * https://github.com/jasonmayes/mdl-component-design-pattern + * + * @constructor + * @param {HTMLElement} element The element that will be upgraded. + */ +var MaterialRipple = function MaterialRipple(element) { + this.element_ = element; + // Initialize instance. + this.init(); +}; +window['MaterialRipple'] = MaterialRipple; +/** + * Store constants in one place so they can be updated easily. + * + * @enum {string | number} + * @private + */ +MaterialRipple.prototype.Constant_ = { + INITIAL_SCALE: 'scale(0.0001, 0.0001)', + INITIAL_SIZE: '1px', + INITIAL_OPACITY: '0.4', + FINAL_OPACITY: '0', + FINAL_SCALE: '' +}; +/** + * Store strings for class names defined by this component that are used in + * JavaScript. This allows us to simply change it in one place should we + * decide to modify at a later date. + * + * @enum {string} + * @private + */ +MaterialRipple.prototype.CssClasses_ = { + RIPPLE_CENTER: 'mdl-ripple--center', + RIPPLE_EFFECT_IGNORE_EVENTS: 'mdl-js-ripple-effect--ignore-events', + RIPPLE: 'mdl-ripple', + IS_ANIMATING: 'is-animating', + IS_VISIBLE: 'is-visible' +}; +/** + * Handle mouse / finger down on element. + * + * @param {Event} event The event that fired. + * @private + */ +MaterialRipple.prototype.downHandler_ = function (event) { + if (!this.rippleElement_.style.width && !this.rippleElement_.style.height) { + var rect = this.element_.getBoundingClientRect(); + this.boundHeight = rect.height; + this.boundWidth = rect.width; + this.rippleSize_ = Math.sqrt(rect.width * rect.width + rect.height * rect.height) * 2 + 2; + this.rippleElement_.style.width = this.rippleSize_ + 'px'; + this.rippleElement_.style.height = this.rippleSize_ + 'px'; + } + this.rippleElement_.classList.add(this.CssClasses_.IS_VISIBLE); + if (event.type === 'mousedown' && this.ignoringMouseDown_) { + this.ignoringMouseDown_ = false; + } else { + if (event.type === 'touchstart') { + this.ignoringMouseDown_ = true; + } + var frameCount = this.getFrameCount(); + if (frameCount > 0) { + return; + } + this.setFrameCount(1); + var bound = event.currentTarget.getBoundingClientRect(); + var x; + var y; + // Check if we are handling a keyboard click. + if (event.clientX === 0 && event.clientY === 0) { + x = Math.round(bound.width / 2); + y = Math.round(bound.height / 2); + } else { + var clientX = event.clientX ? event.clientX : event.touches[0].clientX; + var clientY = event.clientY ? event.clientY : event.touches[0].clientY; + x = Math.round(clientX - bound.left); + y = Math.round(clientY - bound.top); + } + this.setRippleXY(x, y); + this.setRippleStyles(true); + window.requestAnimationFrame(this.animFrameHandler.bind(this)); + } +}; +/** + * Handle mouse / finger up on element. + * + * @param {Event} event The event that fired. + * @private + */ +MaterialRipple.prototype.upHandler_ = function (event) { + // Don't fire for the artificial "mouseup" generated by a double-click. + if (event && event.detail !== 2) { + this.rippleElement_.classList.remove(this.CssClasses_.IS_VISIBLE); + } + // Allow a repaint to occur before removing this class, so the animation + // shows for tap events, which seem to trigger a mouseup too soon after + // mousedown. + window.setTimeout(function () { + this.rippleElement_.classList.remove(this.CssClasses_.IS_VISIBLE); + }.bind(this), 0); +}; +/** + * Initialize element. + */ +MaterialRipple.prototype.init = function () { + if (this.element_) { + var recentering = this.element_.classList.contains(this.CssClasses_.RIPPLE_CENTER); + if (!this.element_.classList.contains(this.CssClasses_.RIPPLE_EFFECT_IGNORE_EVENTS)) { + this.rippleElement_ = this.element_.querySelector('.' + this.CssClasses_.RIPPLE); + this.frameCount_ = 0; + this.rippleSize_ = 0; + this.x_ = 0; + this.y_ = 0; + // Touch start produces a compat mouse down event, which would cause a + // second ripples. To avoid that, we use this property to ignore the first + // mouse down after a touch start. + this.ignoringMouseDown_ = false; + this.boundDownHandler = this.downHandler_.bind(this); + this.element_.addEventListener('mousedown', this.boundDownHandler); + this.element_.addEventListener('touchstart', this.boundDownHandler); + this.boundUpHandler = this.upHandler_.bind(this); + this.element_.addEventListener('mouseup', this.boundUpHandler); + this.element_.addEventListener('mouseleave', this.boundUpHandler); + this.element_.addEventListener('touchend', this.boundUpHandler); + this.element_.addEventListener('blur', this.boundUpHandler); + /** + * Getter for frameCount_. + * @return {number} the frame count. + */ + this.getFrameCount = function () { + return this.frameCount_; + }; + /** + * Setter for frameCount_. + * @param {number} fC the frame count. + */ + this.setFrameCount = function (fC) { + this.frameCount_ = fC; + }; + /** + * Getter for rippleElement_. + * @return {Element} the ripple element. + */ + this.getRippleElement = function () { + return this.rippleElement_; + }; + /** + * Sets the ripple X and Y coordinates. + * @param {number} newX the new X coordinate + * @param {number} newY the new Y coordinate + */ + this.setRippleXY = function (newX, newY) { + this.x_ = newX; + this.y_ = newY; + }; + /** + * Sets the ripple styles. + * @param {boolean} start whether or not this is the start frame. + */ + this.setRippleStyles = function (start) { + if (this.rippleElement_ !== null) { + var transformString; + var scale; + var size; + var offset = 'translate(' + this.x_ + 'px, ' + this.y_ + 'px)'; + if (start) { + scale = this.Constant_.INITIAL_SCALE; + size = this.Constant_.INITIAL_SIZE; + } else { + scale = this.Constant_.FINAL_SCALE; + size = this.rippleSize_ + 'px'; + if (recentering) { + offset = 'translate(' + this.boundWidth / 2 + 'px, ' + this.boundHeight / 2 + 'px)'; + } + } + transformString = 'translate(-50%, -50%) ' + offset + scale; + this.rippleElement_.style.webkitTransform = transformString; + this.rippleElement_.style.msTransform = transformString; + this.rippleElement_.style.transform = transformString; + if (start) { + this.rippleElement_.classList.remove(this.CssClasses_.IS_ANIMATING); + } else { + this.rippleElement_.classList.add(this.CssClasses_.IS_ANIMATING); + } + } + }; + /** + * Handles an animation frame. + */ + this.animFrameHandler = function () { + if (this.frameCount_-- > 0) { + window.requestAnimationFrame(this.animFrameHandler.bind(this)); + } else { + this.setRippleStyles(false); + } + }; + } + } +}; +/** + * Downgrade the component + * + * @private + */ +MaterialRipple.prototype.mdlDowngrade_ = function () { + this.element_.removeEventListener('mousedown', this.boundDownHandler); + this.element_.removeEventListener('touchstart', this.boundDownHandler); + this.element_.removeEventListener('mouseup', this.boundUpHandler); + this.element_.removeEventListener('mouseleave', this.boundUpHandler); + this.element_.removeEventListener('touchend', this.boundUpHandler); + this.element_.removeEventListener('blur', this.boundUpHandler); +}; +/** + * Public alias for the downgrade method. + * + * @public + */ +MaterialRipple.prototype.mdlDowngrade = MaterialRipple.prototype.mdlDowngrade_; +MaterialRipple.prototype['mdlDowngrade'] = MaterialRipple.prototype.mdlDowngrade; +// The component registers itself. It can assume componentHandler is available +// in the global scope. +componentHandler.register({ + constructor: MaterialRipple, + classAsString: 'MaterialRipple', + cssClass: 'mdl-js-ripple-effect', + widget: false +}); +}()); diff --git a/examples/scalajs-play-core-react/server/src/main/assets/material/material.min.css b/examples/scalajs-play-core-react/server/src/main/assets/material/material.min.css new file mode 100644 index 0000000..7a2fb0a --- /dev/null +++ b/examples/scalajs-play-core-react/server/src/main/assets/material/material.min.css @@ -0,0 +1,9 @@ +/** + * material-design-lite - Material Design Components in CSS, JS and HTML + * @version v1.0.6 + * @license Apache-2.0 + * @copyright 2015 Google, Inc. + * @link https://github.com/google/material-design-lite + */ +@charset "UTF-8";html{color:rgba(0,0,0,.87)}::-moz-selection{background:#b3d4fc;text-shadow:none}::selection{background:#b3d4fc;text-shadow:none}hr{display:block;height:1px;border:0;border-top:1px solid #ccc;margin:1em 0;padding:0}audio,canvas,iframe,img,svg,video{vertical-align:middle}fieldset{border:0;margin:0;padding:0}textarea{resize:vertical}.browserupgrade{margin:.2em 0;background:#ccc;color:#000;padding:.2em 0}.hidden{display:none!important}.visuallyhidden{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.visuallyhidden.focusable:active,.visuallyhidden.focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}.invisible{visibility:hidden}.clearfix:before,.clearfix:after{content:" ";display:table}.clearfix:after{clear:both}@media print{*,*:before,*:after,*:first-letter,*:first-line{background:0 0!important;color:#000!important;box-shadow:none!important;text-shadow:none!important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href)")"}abbr[title]:after{content:" (" attr(title)")"}a[href^="#"]:after,a[href^="javascript:"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100%!important}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}}a,.mdl-accordion,.mdl-button,.mdl-card,.mdl-checkbox,.mdl-dropdown-menu,.mdl-icon-toggle,.mdl-item,.mdl-radio,.mdl-slider,.mdl-switch,.mdl-tabs__tab{-webkit-tap-highlight-color:transparent;-webkit-tap-highlight-color:rgba(255,255,255,0)}html{width:100%;height:100%;-ms-touch-action:manipulation;touch-action:manipulation}body{width:100%;min-height:100%;margin:0}main{display:block}*[hidden]{display:none!important}html,body{font-family:"Helvetica","Arial",sans-serif;font-size:14px;font-weight:400;line-height:20px}h1,h2,h3,h4,h5,h6,p{padding:0}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small{font-family:"Roboto","Helvetica","Arial",sans-serif;font-weight:400;line-height:1.35;letter-spacing:-.02em;opacity:.54;font-size:.6em}h1{font-size:56px;line-height:1.35;letter-spacing:-.02em;margin:24px 0}h1,h2{font-family:"Roboto","Helvetica","Arial",sans-serif;font-weight:400}h2{font-size:45px;line-height:48px}h2,h3{margin:24px 0}h3{font-size:34px;line-height:40px}h3,h4{font-family:"Roboto","Helvetica","Arial",sans-serif;font-weight:400}h4{font-size:24px;line-height:32px;-moz-osx-font-smoothing:grayscale;margin:24px 0 16px}h5{font-size:20px;font-weight:500;line-height:1;letter-spacing:.02em}h5,h6{font-family:"Roboto","Helvetica","Arial",sans-serif;margin:24px 0 16px}h6{font-size:16px;letter-spacing:.04em}h6,p{font-weight:400;line-height:24px}p{font-size:14px;letter-spacing:0;margin:0 0 16px}a{color:#ff4081;font-weight:500}blockquote{font-family:"Roboto","Helvetica","Arial",sans-serif;position:relative;font-size:24px;font-weight:300;font-style:italic;line-height:1.35;letter-spacing:.08em}blockquote:before{position:absolute;left:-.5em;content:'“'}blockquote:after{content:'”';margin-left:-.05em}mark{background-color:#f4ff81}dt{font-weight:700}address{font-size:12px;line-height:1;font-style:normal}address,ul,ol{font-weight:400;letter-spacing:0}ul,ol{font-size:14px;line-height:24px}.mdl-typography--display-4,.mdl-typography--display-4-color-contrast{font-family:"Roboto","Helvetica","Arial",sans-serif;font-size:112px;font-weight:300;line-height:1;letter-spacing:-.04em}.mdl-typography--display-4-color-contrast{opacity:.54}.mdl-typography--display-3,.mdl-typography--display-3-color-contrast{font-family:"Roboto","Helvetica","Arial",sans-serif;font-size:56px;font-weight:400;line-height:1.35;letter-spacing:-.02em}.mdl-typography--display-3-color-contrast{opacity:.54}.mdl-typography--display-2,.mdl-typography--display-2-color-contrast{font-family:"Roboto","Helvetica","Arial",sans-serif;font-size:45px;font-weight:400;line-height:48px}.mdl-typography--display-2-color-contrast{opacity:.54}.mdl-typography--display-1,.mdl-typography--display-1-color-contrast{font-family:"Roboto","Helvetica","Arial",sans-serif;font-size:34px;font-weight:400;line-height:40px}.mdl-typography--display-1-color-contrast{opacity:.54}.mdl-typography--headline,.mdl-typography--headline-color-contrast{font-family:"Roboto","Helvetica","Arial",sans-serif;font-size:24px;font-weight:400;line-height:32px;-moz-osx-font-smoothing:grayscale}.mdl-typography--headline-color-contrast{opacity:.87}.mdl-typography--title,.mdl-typography--title-color-contrast{font-family:"Roboto","Helvetica","Arial",sans-serif;font-size:20px;font-weight:500;line-height:1;letter-spacing:.02em}.mdl-typography--title-color-contrast{opacity:.87}.mdl-typography--subhead,.mdl-typography--subhead-color-contrast{font-family:"Roboto","Helvetica","Arial",sans-serif;font-size:16px;font-weight:400;line-height:24px;letter-spacing:.04em}.mdl-typography--subhead-color-contrast{opacity:.87}.mdl-typography--body-2,.mdl-typography--body-2-color-contrast{font-size:14px;font-weight:700;line-height:24px;letter-spacing:0}.mdl-typography--body-2-color-contrast{opacity:.87}.mdl-typography--body-1,.mdl-typography--body-1-color-contrast{font-size:14px;font-weight:400;line-height:24px;letter-spacing:0}.mdl-typography--body-1-color-contrast{opacity:.87}.mdl-typography--body-2-force-preferred-font,.mdl-typography--body-2-force-preferred-font-color-contrast{font-family:"Roboto","Helvetica","Arial",sans-serif;font-size:14px;font-weight:500;line-height:24px;letter-spacing:0}.mdl-typography--body-2-force-preferred-font-color-contrast{opacity:.87}.mdl-typography--body-1-force-preferred-font,.mdl-typography--body-1-force-preferred-font-color-contrast{font-family:"Roboto","Helvetica","Arial",sans-serif;font-size:14px;font-weight:400;line-height:24px;letter-spacing:0}.mdl-typography--body-1-force-preferred-font-color-contrast{opacity:.87}.mdl-typography--caption,.mdl-typography--caption-force-preferred-font{font-size:12px;font-weight:400;line-height:1;letter-spacing:0}.mdl-typography--caption-force-preferred-font{font-family:"Roboto","Helvetica","Arial",sans-serif}.mdl-typography--caption-color-contrast,.mdl-typography--caption-force-preferred-font-color-contrast{font-size:12px;font-weight:400;line-height:1;letter-spacing:0;opacity:.54}.mdl-typography--caption-force-preferred-font-color-contrast,.mdl-typography--menu{font-family:"Roboto","Helvetica","Arial",sans-serif}.mdl-typography--menu{font-size:14px;font-weight:500;line-height:1;letter-spacing:0}.mdl-typography--menu-color-contrast{opacity:.87}.mdl-typography--menu-color-contrast,.mdl-typography--button,.mdl-typography--button-color-contrast{font-family:"Roboto","Helvetica","Arial",sans-serif;font-size:14px;font-weight:500;line-height:1;letter-spacing:0}.mdl-typography--button,.mdl-typography--button-color-contrast{text-transform:uppercase}.mdl-typography--button-color-contrast{opacity:.87}.mdl-typography--text-left{text-align:left}.mdl-typography--text-right{text-align:right}.mdl-typography--text-center{text-align:center}.mdl-typography--text-justify{text-align:justify}.mdl-typography--text-nowrap{white-space:nowrap}.mdl-typography--text-lowercase{text-transform:lowercase}.mdl-typography--text-uppercase{text-transform:uppercase}.mdl-typography--text-capitalize{text-transform:capitalize}.mdl-typography--font-thin{font-weight:200!important}.mdl-typography--font-light{font-weight:300!important}.mdl-typography--font-regular{font-weight:400!important}.mdl-typography--font-medium{font-weight:500!important}.mdl-typography--font-bold{font-weight:700!important}.mdl-typography--font-black{font-weight:900!important}.mdl-color-text--red{color:#f44336 !important}.mdl-color--red{background-color:#f44336 !important}.mdl-color-text--red-50{color:#ffebee !important}.mdl-color--red-50{background-color:#ffebee !important}.mdl-color-text--red-100{color:#ffcdd2 !important}.mdl-color--red-100{background-color:#ffcdd2 !important}.mdl-color-text--red-200{color:#ef9a9a !important}.mdl-color--red-200{background-color:#ef9a9a !important}.mdl-color-text--red-300{color:#e57373 !important}.mdl-color--red-300{background-color:#e57373 !important}.mdl-color-text--red-400{color:#ef5350 !important}.mdl-color--red-400{background-color:#ef5350 !important}.mdl-color-text--red-500{color:#f44336 !important}.mdl-color--red-500{background-color:#f44336 !important}.mdl-color-text--red-600{color:#e53935 !important}.mdl-color--red-600{background-color:#e53935 !important}.mdl-color-text--red-700{color:#d32f2f !important}.mdl-color--red-700{background-color:#d32f2f !important}.mdl-color-text--red-800{color:#c62828 !important}.mdl-color--red-800{background-color:#c62828 !important}.mdl-color-text--red-900{color:#b71c1c !important}.mdl-color--red-900{background-color:#b71c1c !important}.mdl-color-text--red-A100{color:#ff8a80 !important}.mdl-color--red-A100{background-color:#ff8a80 !important}.mdl-color-text--red-A200{color:#ff5252 !important}.mdl-color--red-A200{background-color:#ff5252 !important}.mdl-color-text--red-A400{color:#ff1744 !important}.mdl-color--red-A400{background-color:#ff1744 !important}.mdl-color-text--red-A700{color:#d50000 !important}.mdl-color--red-A700{background-color:#d50000 !important}.mdl-color-text--pink{color:#e91e63 !important}.mdl-color--pink{background-color:#e91e63 !important}.mdl-color-text--pink-50{color:#fce4ec !important}.mdl-color--pink-50{background-color:#fce4ec !important}.mdl-color-text--pink-100{color:#f8bbd0 !important}.mdl-color--pink-100{background-color:#f8bbd0 !important}.mdl-color-text--pink-200{color:#f48fb1 !important}.mdl-color--pink-200{background-color:#f48fb1 !important}.mdl-color-text--pink-300{color:#f06292 !important}.mdl-color--pink-300{background-color:#f06292 !important}.mdl-color-text--pink-400{color:#ec407a !important}.mdl-color--pink-400{background-color:#ec407a !important}.mdl-color-text--pink-500{color:#e91e63 !important}.mdl-color--pink-500{background-color:#e91e63 !important}.mdl-color-text--pink-600{color:#d81b60 !important}.mdl-color--pink-600{background-color:#d81b60 !important}.mdl-color-text--pink-700{color:#c2185b !important}.mdl-color--pink-700{background-color:#c2185b !important}.mdl-color-text--pink-800{color:#ad1457 !important}.mdl-color--pink-800{background-color:#ad1457 !important}.mdl-color-text--pink-900{color:#880e4f !important}.mdl-color--pink-900{background-color:#880e4f !important}.mdl-color-text--pink-A100{color:#ff80ab !important}.mdl-color--pink-A100{background-color:#ff80ab !important}.mdl-color-text--pink-A200{color:#ff4081 !important}.mdl-color--pink-A200{background-color:#ff4081 !important}.mdl-color-text--pink-A400{color:#f50057 !important}.mdl-color--pink-A400{background-color:#f50057 !important}.mdl-color-text--pink-A700{color:#c51162 !important}.mdl-color--pink-A700{background-color:#c51162 !important}.mdl-color-text--purple{color:#9c27b0 !important}.mdl-color--purple{background-color:#9c27b0 !important}.mdl-color-text--purple-50{color:#f3e5f5 !important}.mdl-color--purple-50{background-color:#f3e5f5 !important}.mdl-color-text--purple-100{color:#e1bee7 !important}.mdl-color--purple-100{background-color:#e1bee7 !important}.mdl-color-text--purple-200{color:#ce93d8 !important}.mdl-color--purple-200{background-color:#ce93d8 !important}.mdl-color-text--purple-300{color:#ba68c8 !important}.mdl-color--purple-300{background-color:#ba68c8 !important}.mdl-color-text--purple-400{color:#ab47bc !important}.mdl-color--purple-400{background-color:#ab47bc !important}.mdl-color-text--purple-500{color:#9c27b0 !important}.mdl-color--purple-500{background-color:#9c27b0 !important}.mdl-color-text--purple-600{color:#8e24aa !important}.mdl-color--purple-600{background-color:#8e24aa !important}.mdl-color-text--purple-700{color:#7b1fa2 !important}.mdl-color--purple-700{background-color:#7b1fa2 !important}.mdl-color-text--purple-800{color:#6a1b9a !important}.mdl-color--purple-800{background-color:#6a1b9a !important}.mdl-color-text--purple-900{color:#4a148c !important}.mdl-color--purple-900{background-color:#4a148c !important}.mdl-color-text--purple-A100{color:#ea80fc !important}.mdl-color--purple-A100{background-color:#ea80fc !important}.mdl-color-text--purple-A200{color:#e040fb !important}.mdl-color--purple-A200{background-color:#e040fb !important}.mdl-color-text--purple-A400{color:#d500f9 !important}.mdl-color--purple-A400{background-color:#d500f9 !important}.mdl-color-text--purple-A700{color:#a0f !important}.mdl-color--purple-A700{background-color:#a0f !important}.mdl-color-text--deep-purple{color:#673ab7 !important}.mdl-color--deep-purple{background-color:#673ab7 !important}.mdl-color-text--deep-purple-50{color:#ede7f6 !important}.mdl-color--deep-purple-50{background-color:#ede7f6 !important}.mdl-color-text--deep-purple-100{color:#d1c4e9 !important}.mdl-color--deep-purple-100{background-color:#d1c4e9 !important}.mdl-color-text--deep-purple-200{color:#b39ddb !important}.mdl-color--deep-purple-200{background-color:#b39ddb !important}.mdl-color-text--deep-purple-300{color:#9575cd !important}.mdl-color--deep-purple-300{background-color:#9575cd !important}.mdl-color-text--deep-purple-400{color:#7e57c2 !important}.mdl-color--deep-purple-400{background-color:#7e57c2 !important}.mdl-color-text--deep-purple-500{color:#673ab7 !important}.mdl-color--deep-purple-500{background-color:#673ab7 !important}.mdl-color-text--deep-purple-600{color:#5e35b1 !important}.mdl-color--deep-purple-600{background-color:#5e35b1 !important}.mdl-color-text--deep-purple-700{color:#512da8 !important}.mdl-color--deep-purple-700{background-color:#512da8 !important}.mdl-color-text--deep-purple-800{color:#4527a0 !important}.mdl-color--deep-purple-800{background-color:#4527a0 !important}.mdl-color-text--deep-purple-900{color:#311b92 !important}.mdl-color--deep-purple-900{background-color:#311b92 !important}.mdl-color-text--deep-purple-A100{color:#b388ff !important}.mdl-color--deep-purple-A100{background-color:#b388ff !important}.mdl-color-text--deep-purple-A200{color:#7c4dff !important}.mdl-color--deep-purple-A200{background-color:#7c4dff !important}.mdl-color-text--deep-purple-A400{color:#651fff !important}.mdl-color--deep-purple-A400{background-color:#651fff !important}.mdl-color-text--deep-purple-A700{color:#6200ea !important}.mdl-color--deep-purple-A700{background-color:#6200ea !important}.mdl-color-text--indigo{color:#3f51b5 !important}.mdl-color--indigo{background-color:#3f51b5 !important}.mdl-color-text--indigo-50{color:#e8eaf6 !important}.mdl-color--indigo-50{background-color:#e8eaf6 !important}.mdl-color-text--indigo-100{color:#c5cae9 !important}.mdl-color--indigo-100{background-color:#c5cae9 !important}.mdl-color-text--indigo-200{color:#9fa8da !important}.mdl-color--indigo-200{background-color:#9fa8da !important}.mdl-color-text--indigo-300{color:#7986cb !important}.mdl-color--indigo-300{background-color:#7986cb !important}.mdl-color-text--indigo-400{color:#5c6bc0 !important}.mdl-color--indigo-400{background-color:#5c6bc0 !important}.mdl-color-text--indigo-500{color:#3f51b5 !important}.mdl-color--indigo-500{background-color:#3f51b5 !important}.mdl-color-text--indigo-600{color:#3949ab !important}.mdl-color--indigo-600{background-color:#3949ab !important}.mdl-color-text--indigo-700{color:#303f9f !important}.mdl-color--indigo-700{background-color:#303f9f !important}.mdl-color-text--indigo-800{color:#283593 !important}.mdl-color--indigo-800{background-color:#283593 !important}.mdl-color-text--indigo-900{color:#1a237e !important}.mdl-color--indigo-900{background-color:#1a237e !important}.mdl-color-text--indigo-A100{color:#8c9eff !important}.mdl-color--indigo-A100{background-color:#8c9eff !important}.mdl-color-text--indigo-A200{color:#536dfe !important}.mdl-color--indigo-A200{background-color:#536dfe !important}.mdl-color-text--indigo-A400{color:#3d5afe !important}.mdl-color--indigo-A400{background-color:#3d5afe !important}.mdl-color-text--indigo-A700{color:#304ffe !important}.mdl-color--indigo-A700{background-color:#304ffe !important}.mdl-color-text--blue{color:#2196f3 !important}.mdl-color--blue{background-color:#2196f3 !important}.mdl-color-text--blue-50{color:#e3f2fd !important}.mdl-color--blue-50{background-color:#e3f2fd !important}.mdl-color-text--blue-100{color:#bbdefb !important}.mdl-color--blue-100{background-color:#bbdefb !important}.mdl-color-text--blue-200{color:#90caf9 !important}.mdl-color--blue-200{background-color:#90caf9 !important}.mdl-color-text--blue-300{color:#64b5f6 !important}.mdl-color--blue-300{background-color:#64b5f6 !important}.mdl-color-text--blue-400{color:#42a5f5 !important}.mdl-color--blue-400{background-color:#42a5f5 !important}.mdl-color-text--blue-500{color:#2196f3 !important}.mdl-color--blue-500{background-color:#2196f3 !important}.mdl-color-text--blue-600{color:#1e88e5 !important}.mdl-color--blue-600{background-color:#1e88e5 !important}.mdl-color-text--blue-700{color:#1976d2 !important}.mdl-color--blue-700{background-color:#1976d2 !important}.mdl-color-text--blue-800{color:#1565c0 !important}.mdl-color--blue-800{background-color:#1565c0 !important}.mdl-color-text--blue-900{color:#0d47a1 !important}.mdl-color--blue-900{background-color:#0d47a1 !important}.mdl-color-text--blue-A100{color:#82b1ff !important}.mdl-color--blue-A100{background-color:#82b1ff !important}.mdl-color-text--blue-A200{color:#448aff !important}.mdl-color--blue-A200{background-color:#448aff !important}.mdl-color-text--blue-A400{color:#2979ff !important}.mdl-color--blue-A400{background-color:#2979ff !important}.mdl-color-text--blue-A700{color:#2962ff !important}.mdl-color--blue-A700{background-color:#2962ff !important}.mdl-color-text--light-blue{color:#03a9f4 !important}.mdl-color--light-blue{background-color:#03a9f4 !important}.mdl-color-text--light-blue-50{color:#e1f5fe !important}.mdl-color--light-blue-50{background-color:#e1f5fe !important}.mdl-color-text--light-blue-100{color:#b3e5fc !important}.mdl-color--light-blue-100{background-color:#b3e5fc !important}.mdl-color-text--light-blue-200{color:#81d4fa !important}.mdl-color--light-blue-200{background-color:#81d4fa !important}.mdl-color-text--light-blue-300{color:#4fc3f7 !important}.mdl-color--light-blue-300{background-color:#4fc3f7 !important}.mdl-color-text--light-blue-400{color:#29b6f6 !important}.mdl-color--light-blue-400{background-color:#29b6f6 !important}.mdl-color-text--light-blue-500{color:#03a9f4 !important}.mdl-color--light-blue-500{background-color:#03a9f4 !important}.mdl-color-text--light-blue-600{color:#039be5 !important}.mdl-color--light-blue-600{background-color:#039be5 !important}.mdl-color-text--light-blue-700{color:#0288d1 !important}.mdl-color--light-blue-700{background-color:#0288d1 !important}.mdl-color-text--light-blue-800{color:#0277bd !important}.mdl-color--light-blue-800{background-color:#0277bd !important}.mdl-color-text--light-blue-900{color:#01579b !important}.mdl-color--light-blue-900{background-color:#01579b !important}.mdl-color-text--light-blue-A100{color:#80d8ff !important}.mdl-color--light-blue-A100{background-color:#80d8ff !important}.mdl-color-text--light-blue-A200{color:#40c4ff !important}.mdl-color--light-blue-A200{background-color:#40c4ff !important}.mdl-color-text--light-blue-A400{color:#00b0ff !important}.mdl-color--light-blue-A400{background-color:#00b0ff !important}.mdl-color-text--light-blue-A700{color:#0091ea !important}.mdl-color--light-blue-A700{background-color:#0091ea !important}.mdl-color-text--cyan{color:#00bcd4 !important}.mdl-color--cyan{background-color:#00bcd4 !important}.mdl-color-text--cyan-50{color:#e0f7fa !important}.mdl-color--cyan-50{background-color:#e0f7fa !important}.mdl-color-text--cyan-100{color:#b2ebf2 !important}.mdl-color--cyan-100{background-color:#b2ebf2 !important}.mdl-color-text--cyan-200{color:#80deea !important}.mdl-color--cyan-200{background-color:#80deea !important}.mdl-color-text--cyan-300{color:#4dd0e1 !important}.mdl-color--cyan-300{background-color:#4dd0e1 !important}.mdl-color-text--cyan-400{color:#26c6da !important}.mdl-color--cyan-400{background-color:#26c6da !important}.mdl-color-text--cyan-500{color:#00bcd4 !important}.mdl-color--cyan-500{background-color:#00bcd4 !important}.mdl-color-text--cyan-600{color:#00acc1 !important}.mdl-color--cyan-600{background-color:#00acc1 !important}.mdl-color-text--cyan-700{color:#0097a7 !important}.mdl-color--cyan-700{background-color:#0097a7 !important}.mdl-color-text--cyan-800{color:#00838f !important}.mdl-color--cyan-800{background-color:#00838f !important}.mdl-color-text--cyan-900{color:#006064 !important}.mdl-color--cyan-900{background-color:#006064 !important}.mdl-color-text--cyan-A100{color:#84ffff !important}.mdl-color--cyan-A100{background-color:#84ffff !important}.mdl-color-text--cyan-A200{color:#18ffff !important}.mdl-color--cyan-A200{background-color:#18ffff !important}.mdl-color-text--cyan-A400{color:#00e5ff !important}.mdl-color--cyan-A400{background-color:#00e5ff !important}.mdl-color-text--cyan-A700{color:#00b8d4 !important}.mdl-color--cyan-A700{background-color:#00b8d4 !important}.mdl-color-text--teal{color:#009688 !important}.mdl-color--teal{background-color:#009688 !important}.mdl-color-text--teal-50{color:#e0f2f1 !important}.mdl-color--teal-50{background-color:#e0f2f1 !important}.mdl-color-text--teal-100{color:#b2dfdb !important}.mdl-color--teal-100{background-color:#b2dfdb !important}.mdl-color-text--teal-200{color:#80cbc4 !important}.mdl-color--teal-200{background-color:#80cbc4 !important}.mdl-color-text--teal-300{color:#4db6ac !important}.mdl-color--teal-300{background-color:#4db6ac !important}.mdl-color-text--teal-400{color:#26a69a !important}.mdl-color--teal-400{background-color:#26a69a !important}.mdl-color-text--teal-500{color:#009688 !important}.mdl-color--teal-500{background-color:#009688 !important}.mdl-color-text--teal-600{color:#00897b !important}.mdl-color--teal-600{background-color:#00897b !important}.mdl-color-text--teal-700{color:#00796b !important}.mdl-color--teal-700{background-color:#00796b !important}.mdl-color-text--teal-800{color:#00695c !important}.mdl-color--teal-800{background-color:#00695c !important}.mdl-color-text--teal-900{color:#004d40 !important}.mdl-color--teal-900{background-color:#004d40 !important}.mdl-color-text--teal-A100{color:#a7ffeb !important}.mdl-color--teal-A100{background-color:#a7ffeb !important}.mdl-color-text--teal-A200{color:#64ffda !important}.mdl-color--teal-A200{background-color:#64ffda !important}.mdl-color-text--teal-A400{color:#1de9b6 !important}.mdl-color--teal-A400{background-color:#1de9b6 !important}.mdl-color-text--teal-A700{color:#00bfa5 !important}.mdl-color--teal-A700{background-color:#00bfa5 !important}.mdl-color-text--green{color:#4caf50 !important}.mdl-color--green{background-color:#4caf50 !important}.mdl-color-text--green-50{color:#e8f5e9 !important}.mdl-color--green-50{background-color:#e8f5e9 !important}.mdl-color-text--green-100{color:#c8e6c9 !important}.mdl-color--green-100{background-color:#c8e6c9 !important}.mdl-color-text--green-200{color:#a5d6a7 !important}.mdl-color--green-200{background-color:#a5d6a7 !important}.mdl-color-text--green-300{color:#81c784 !important}.mdl-color--green-300{background-color:#81c784 !important}.mdl-color-text--green-400{color:#66bb6a !important}.mdl-color--green-400{background-color:#66bb6a !important}.mdl-color-text--green-500{color:#4caf50 !important}.mdl-color--green-500{background-color:#4caf50 !important}.mdl-color-text--green-600{color:#43a047 !important}.mdl-color--green-600{background-color:#43a047 !important}.mdl-color-text--green-700{color:#388e3c !important}.mdl-color--green-700{background-color:#388e3c !important}.mdl-color-text--green-800{color:#2e7d32 !important}.mdl-color--green-800{background-color:#2e7d32 !important}.mdl-color-text--green-900{color:#1b5e20 !important}.mdl-color--green-900{background-color:#1b5e20 !important}.mdl-color-text--green-A100{color:#b9f6ca !important}.mdl-color--green-A100{background-color:#b9f6ca !important}.mdl-color-text--green-A200{color:#69f0ae !important}.mdl-color--green-A200{background-color:#69f0ae !important}.mdl-color-text--green-A400{color:#00e676 !important}.mdl-color--green-A400{background-color:#00e676 !important}.mdl-color-text--green-A700{color:#00c853 !important}.mdl-color--green-A700{background-color:#00c853 !important}.mdl-color-text--light-green{color:#8bc34a !important}.mdl-color--light-green{background-color:#8bc34a !important}.mdl-color-text--light-green-50{color:#f1f8e9 !important}.mdl-color--light-green-50{background-color:#f1f8e9 !important}.mdl-color-text--light-green-100{color:#dcedc8 !important}.mdl-color--light-green-100{background-color:#dcedc8 !important}.mdl-color-text--light-green-200{color:#c5e1a5 !important}.mdl-color--light-green-200{background-color:#c5e1a5 !important}.mdl-color-text--light-green-300{color:#aed581 !important}.mdl-color--light-green-300{background-color:#aed581 !important}.mdl-color-text--light-green-400{color:#9ccc65 !important}.mdl-color--light-green-400{background-color:#9ccc65 !important}.mdl-color-text--light-green-500{color:#8bc34a !important}.mdl-color--light-green-500{background-color:#8bc34a !important}.mdl-color-text--light-green-600{color:#7cb342 !important}.mdl-color--light-green-600{background-color:#7cb342 !important}.mdl-color-text--light-green-700{color:#689f38 !important}.mdl-color--light-green-700{background-color:#689f38 !important}.mdl-color-text--light-green-800{color:#558b2f !important}.mdl-color--light-green-800{background-color:#558b2f !important}.mdl-color-text--light-green-900{color:#33691e !important}.mdl-color--light-green-900{background-color:#33691e !important}.mdl-color-text--light-green-A100{color:#ccff90 !important}.mdl-color--light-green-A100{background-color:#ccff90 !important}.mdl-color-text--light-green-A200{color:#b2ff59 !important}.mdl-color--light-green-A200{background-color:#b2ff59 !important}.mdl-color-text--light-green-A400{color:#76ff03 !important}.mdl-color--light-green-A400{background-color:#76ff03 !important}.mdl-color-text--light-green-A700{color:#64dd17 !important}.mdl-color--light-green-A700{background-color:#64dd17 !important}.mdl-color-text--lime{color:#cddc39 !important}.mdl-color--lime{background-color:#cddc39 !important}.mdl-color-text--lime-50{color:#f9fbe7 !important}.mdl-color--lime-50{background-color:#f9fbe7 !important}.mdl-color-text--lime-100{color:#f0f4c3 !important}.mdl-color--lime-100{background-color:#f0f4c3 !important}.mdl-color-text--lime-200{color:#e6ee9c !important}.mdl-color--lime-200{background-color:#e6ee9c !important}.mdl-color-text--lime-300{color:#dce775 !important}.mdl-color--lime-300{background-color:#dce775 !important}.mdl-color-text--lime-400{color:#d4e157 !important}.mdl-color--lime-400{background-color:#d4e157 !important}.mdl-color-text--lime-500{color:#cddc39 !important}.mdl-color--lime-500{background-color:#cddc39 !important}.mdl-color-text--lime-600{color:#c0ca33 !important}.mdl-color--lime-600{background-color:#c0ca33 !important}.mdl-color-text--lime-700{color:#afb42b !important}.mdl-color--lime-700{background-color:#afb42b !important}.mdl-color-text--lime-800{color:#9e9d24 !important}.mdl-color--lime-800{background-color:#9e9d24 !important}.mdl-color-text--lime-900{color:#827717 !important}.mdl-color--lime-900{background-color:#827717 !important}.mdl-color-text--lime-A100{color:#f4ff81 !important}.mdl-color--lime-A100{background-color:#f4ff81 !important}.mdl-color-text--lime-A200{color:#eeff41 !important}.mdl-color--lime-A200{background-color:#eeff41 !important}.mdl-color-text--lime-A400{color:#c6ff00 !important}.mdl-color--lime-A400{background-color:#c6ff00 !important}.mdl-color-text--lime-A700{color:#aeea00 !important}.mdl-color--lime-A700{background-color:#aeea00 !important}.mdl-color-text--yellow{color:#ffeb3b !important}.mdl-color--yellow{background-color:#ffeb3b !important}.mdl-color-text--yellow-50{color:#fffde7 !important}.mdl-color--yellow-50{background-color:#fffde7 !important}.mdl-color-text--yellow-100{color:#fff9c4 !important}.mdl-color--yellow-100{background-color:#fff9c4 !important}.mdl-color-text--yellow-200{color:#fff59d !important}.mdl-color--yellow-200{background-color:#fff59d !important}.mdl-color-text--yellow-300{color:#fff176 !important}.mdl-color--yellow-300{background-color:#fff176 !important}.mdl-color-text--yellow-400{color:#ffee58 !important}.mdl-color--yellow-400{background-color:#ffee58 !important}.mdl-color-text--yellow-500{color:#ffeb3b !important}.mdl-color--yellow-500{background-color:#ffeb3b !important}.mdl-color-text--yellow-600{color:#fdd835 !important}.mdl-color--yellow-600{background-color:#fdd835 !important}.mdl-color-text--yellow-700{color:#fbc02d !important}.mdl-color--yellow-700{background-color:#fbc02d !important}.mdl-color-text--yellow-800{color:#f9a825 !important}.mdl-color--yellow-800{background-color:#f9a825 !important}.mdl-color-text--yellow-900{color:#f57f17 !important}.mdl-color--yellow-900{background-color:#f57f17 !important}.mdl-color-text--yellow-A100{color:#ffff8d !important}.mdl-color--yellow-A100{background-color:#ffff8d !important}.mdl-color-text--yellow-A200{color:#ff0 !important}.mdl-color--yellow-A200{background-color:#ff0 !important}.mdl-color-text--yellow-A400{color:#ffea00 !important}.mdl-color--yellow-A400{background-color:#ffea00 !important}.mdl-color-text--yellow-A700{color:#ffd600 !important}.mdl-color--yellow-A700{background-color:#ffd600 !important}.mdl-color-text--amber{color:#ffc107 !important}.mdl-color--amber{background-color:#ffc107 !important}.mdl-color-text--amber-50{color:#fff8e1 !important}.mdl-color--amber-50{background-color:#fff8e1 !important}.mdl-color-text--amber-100{color:#ffecb3 !important}.mdl-color--amber-100{background-color:#ffecb3 !important}.mdl-color-text--amber-200{color:#ffe082 !important}.mdl-color--amber-200{background-color:#ffe082 !important}.mdl-color-text--amber-300{color:#ffd54f !important}.mdl-color--amber-300{background-color:#ffd54f !important}.mdl-color-text--amber-400{color:#ffca28 !important}.mdl-color--amber-400{background-color:#ffca28 !important}.mdl-color-text--amber-500{color:#ffc107 !important}.mdl-color--amber-500{background-color:#ffc107 !important}.mdl-color-text--amber-600{color:#ffb300 !important}.mdl-color--amber-600{background-color:#ffb300 !important}.mdl-color-text--amber-700{color:#ffa000 !important}.mdl-color--amber-700{background-color:#ffa000 !important}.mdl-color-text--amber-800{color:#ff8f00 !important}.mdl-color--amber-800{background-color:#ff8f00 !important}.mdl-color-text--amber-900{color:#ff6f00 !important}.mdl-color--amber-900{background-color:#ff6f00 !important}.mdl-color-text--amber-A100{color:#ffe57f !important}.mdl-color--amber-A100{background-color:#ffe57f !important}.mdl-color-text--amber-A200{color:#ffd740 !important}.mdl-color--amber-A200{background-color:#ffd740 !important}.mdl-color-text--amber-A400{color:#ffc400 !important}.mdl-color--amber-A400{background-color:#ffc400 !important}.mdl-color-text--amber-A700{color:#ffab00 !important}.mdl-color--amber-A700{background-color:#ffab00 !important}.mdl-color-text--orange{color:#ff9800 !important}.mdl-color--orange{background-color:#ff9800 !important}.mdl-color-text--orange-50{color:#fff3e0 !important}.mdl-color--orange-50{background-color:#fff3e0 !important}.mdl-color-text--orange-100{color:#ffe0b2 !important}.mdl-color--orange-100{background-color:#ffe0b2 !important}.mdl-color-text--orange-200{color:#ffcc80 !important}.mdl-color--orange-200{background-color:#ffcc80 !important}.mdl-color-text--orange-300{color:#ffb74d !important}.mdl-color--orange-300{background-color:#ffb74d !important}.mdl-color-text--orange-400{color:#ffa726 !important}.mdl-color--orange-400{background-color:#ffa726 !important}.mdl-color-text--orange-500{color:#ff9800 !important}.mdl-color--orange-500{background-color:#ff9800 !important}.mdl-color-text--orange-600{color:#fb8c00 !important}.mdl-color--orange-600{background-color:#fb8c00 !important}.mdl-color-text--orange-700{color:#f57c00 !important}.mdl-color--orange-700{background-color:#f57c00 !important}.mdl-color-text--orange-800{color:#ef6c00 !important}.mdl-color--orange-800{background-color:#ef6c00 !important}.mdl-color-text--orange-900{color:#e65100 !important}.mdl-color--orange-900{background-color:#e65100 !important}.mdl-color-text--orange-A100{color:#ffd180 !important}.mdl-color--orange-A100{background-color:#ffd180 !important}.mdl-color-text--orange-A200{color:#ffab40 !important}.mdl-color--orange-A200{background-color:#ffab40 !important}.mdl-color-text--orange-A400{color:#ff9100 !important}.mdl-color--orange-A400{background-color:#ff9100 !important}.mdl-color-text--orange-A700{color:#ff6d00 !important}.mdl-color--orange-A700{background-color:#ff6d00 !important}.mdl-color-text--deep-orange{color:#ff5722 !important}.mdl-color--deep-orange{background-color:#ff5722 !important}.mdl-color-text--deep-orange-50{color:#fbe9e7 !important}.mdl-color--deep-orange-50{background-color:#fbe9e7 !important}.mdl-color-text--deep-orange-100{color:#ffccbc !important}.mdl-color--deep-orange-100{background-color:#ffccbc !important}.mdl-color-text--deep-orange-200{color:#ffab91 !important}.mdl-color--deep-orange-200{background-color:#ffab91 !important}.mdl-color-text--deep-orange-300{color:#ff8a65 !important}.mdl-color--deep-orange-300{background-color:#ff8a65 !important}.mdl-color-text--deep-orange-400{color:#ff7043 !important}.mdl-color--deep-orange-400{background-color:#ff7043 !important}.mdl-color-text--deep-orange-500{color:#ff5722 !important}.mdl-color--deep-orange-500{background-color:#ff5722 !important}.mdl-color-text--deep-orange-600{color:#f4511e !important}.mdl-color--deep-orange-600{background-color:#f4511e !important}.mdl-color-text--deep-orange-700{color:#e64a19 !important}.mdl-color--deep-orange-700{background-color:#e64a19 !important}.mdl-color-text--deep-orange-800{color:#d84315 !important}.mdl-color--deep-orange-800{background-color:#d84315 !important}.mdl-color-text--deep-orange-900{color:#bf360c !important}.mdl-color--deep-orange-900{background-color:#bf360c !important}.mdl-color-text--deep-orange-A100{color:#ff9e80 !important}.mdl-color--deep-orange-A100{background-color:#ff9e80 !important}.mdl-color-text--deep-orange-A200{color:#ff6e40 !important}.mdl-color--deep-orange-A200{background-color:#ff6e40 !important}.mdl-color-text--deep-orange-A400{color:#ff3d00 !important}.mdl-color--deep-orange-A400{background-color:#ff3d00 !important}.mdl-color-text--deep-orange-A700{color:#dd2c00 !important}.mdl-color--deep-orange-A700{background-color:#dd2c00 !important}.mdl-color-text--brown{color:#795548 !important}.mdl-color--brown{background-color:#795548 !important}.mdl-color-text--brown-50{color:#efebe9 !important}.mdl-color--brown-50{background-color:#efebe9 !important}.mdl-color-text--brown-100{color:#d7ccc8 !important}.mdl-color--brown-100{background-color:#d7ccc8 !important}.mdl-color-text--brown-200{color:#bcaaa4 !important}.mdl-color--brown-200{background-color:#bcaaa4 !important}.mdl-color-text--brown-300{color:#a1887f !important}.mdl-color--brown-300{background-color:#a1887f !important}.mdl-color-text--brown-400{color:#8d6e63 !important}.mdl-color--brown-400{background-color:#8d6e63 !important}.mdl-color-text--brown-500{color:#795548 !important}.mdl-color--brown-500{background-color:#795548 !important}.mdl-color-text--brown-600{color:#6d4c41 !important}.mdl-color--brown-600{background-color:#6d4c41 !important}.mdl-color-text--brown-700{color:#5d4037 !important}.mdl-color--brown-700{background-color:#5d4037 !important}.mdl-color-text--brown-800{color:#4e342e !important}.mdl-color--brown-800{background-color:#4e342e !important}.mdl-color-text--brown-900{color:#3e2723 !important}.mdl-color--brown-900{background-color:#3e2723 !important}.mdl-color-text--grey{color:#9e9e9e !important}.mdl-color--grey{background-color:#9e9e9e !important}.mdl-color-text--grey-50{color:#fafafa !important}.mdl-color--grey-50{background-color:#fafafa !important}.mdl-color-text--grey-100{color:#f5f5f5 !important}.mdl-color--grey-100{background-color:#f5f5f5 !important}.mdl-color-text--grey-200{color:#eee !important}.mdl-color--grey-200{background-color:#eee !important}.mdl-color-text--grey-300{color:#e0e0e0 !important}.mdl-color--grey-300{background-color:#e0e0e0 !important}.mdl-color-text--grey-400{color:#bdbdbd !important}.mdl-color--grey-400{background-color:#bdbdbd !important}.mdl-color-text--grey-500{color:#9e9e9e !important}.mdl-color--grey-500{background-color:#9e9e9e !important}.mdl-color-text--grey-600{color:#757575 !important}.mdl-color--grey-600{background-color:#757575 !important}.mdl-color-text--grey-700{color:#616161 !important}.mdl-color--grey-700{background-color:#616161 !important}.mdl-color-text--grey-800{color:#424242 !important}.mdl-color--grey-800{background-color:#424242 !important}.mdl-color-text--grey-900{color:#212121 !important}.mdl-color--grey-900{background-color:#212121 !important}.mdl-color-text--blue-grey{color:#607d8b !important}.mdl-color--blue-grey{background-color:#607d8b !important}.mdl-color-text--blue-grey-50{color:#eceff1 !important}.mdl-color--blue-grey-50{background-color:#eceff1 !important}.mdl-color-text--blue-grey-100{color:#cfd8dc !important}.mdl-color--blue-grey-100{background-color:#cfd8dc !important}.mdl-color-text--blue-grey-200{color:#b0bec5 !important}.mdl-color--blue-grey-200{background-color:#b0bec5 !important}.mdl-color-text--blue-grey-300{color:#90a4ae !important}.mdl-color--blue-grey-300{background-color:#90a4ae !important}.mdl-color-text--blue-grey-400{color:#78909c !important}.mdl-color--blue-grey-400{background-color:#78909c !important}.mdl-color-text--blue-grey-500{color:#607d8b !important}.mdl-color--blue-grey-500{background-color:#607d8b !important}.mdl-color-text--blue-grey-600{color:#546e7a !important}.mdl-color--blue-grey-600{background-color:#546e7a !important}.mdl-color-text--blue-grey-700{color:#455a64 !important}.mdl-color--blue-grey-700{background-color:#455a64 !important}.mdl-color-text--blue-grey-800{color:#37474f !important}.mdl-color--blue-grey-800{background-color:#37474f !important}.mdl-color-text--blue-grey-900{color:#263238 !important}.mdl-color--blue-grey-900{background-color:#263238 !important}.mdl-color--black{background-color:#000 !important}.mdl-color-text--black{color:#000 !important}.mdl-color--white{background-color:#fff !important}.mdl-color-text--white{color:#fff !important}.mdl-color--primary{background-color:#3f51b5 !important}.mdl-color--primary-contrast{background-color:#fff !important}.mdl-color--primary-dark{background-color:#303f9f !important}.mdl-color--accent{background-color:#ff4081 !important}.mdl-color--accent-contrast{background-color:#fff !important}.mdl-color-text--primary{color:#3f51b5 !important}.mdl-color-text--primary-contrast{color:#fff !important}.mdl-color-text--primary-dark{color:#303f9f !important}.mdl-color-text--accent{color:#ff4081 !important}.mdl-color-text--accent-contrast{color:#fff !important}.mdl-ripple{background:#000;border-radius:50%;height:50px;left:0;opacity:0;pointer-events:none;position:absolute;top:0;-webkit-transform:translate(-50%,-50%);-ms-transform:translate(-50%,-50%);transform:translate(-50%,-50%);width:50px;overflow:hidden}.mdl-ripple.is-animating{-webkit-transition:-webkit-transform .3s cubic-bezier(0,0,.2,1),width .3s cubic-bezier(0,0,.2,1),height .3s cubic-bezier(0,0,.2,1),opacity .6s cubic-bezier(0,0,.2,1);transition:transform .3s cubic-bezier(0,0,.2,1),width .3s cubic-bezier(0,0,.2,1),height .3s cubic-bezier(0,0,.2,1),opacity .6s cubic-bezier(0,0,.2,1)}.mdl-ripple.is-visible{opacity:.3}.mdl-animation--default,.mdl-animation--fast-out-slow-in{-webkit-transition-timing-function:cubic-bezier(.4,0,.2,1);transition-timing-function:cubic-bezier(.4,0,.2,1)}.mdl-animation--linear-out-slow-in{-webkit-transition-timing-function:cubic-bezier(0,0,.2,1);transition-timing-function:cubic-bezier(0,0,.2,1)}.mdl-animation--fast-out-linear-in{-webkit-transition-timing-function:cubic-bezier(.4,0,1,1);transition-timing-function:cubic-bezier(.4,0,1,1)}.mdl-badge{position:relative;white-space:nowrap;margin-right:24px}.mdl-badge:not([data-badge]){margin-right:auto}.mdl-badge[data-badge]:after{content:attr(data-badge);display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row;-webkit-flex-wrap:wrap;-ms-flex-wrap:wrap;flex-wrap:wrap;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;-webkit-align-content:center;-ms-flex-line-pack:center;align-content:center;-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;position:absolute;top:-11px;right:-24px;font-family:"Roboto","Helvetica","Arial",sans-serif;font-weight:600;font-size:12px;width:22px;height:22px;border-radius:50%;background:#ff4081;color:#fff}.mdl-button .mdl-badge[data-badge]:after{top:-10px;right:-5px}.mdl-badge.mdl-badge--no-background[data-badge]:after{color:#ff4081;background:#fff;box-shadow:0 0 1px gray}.mdl-button{background:0 0;border:none;border-radius:2px;color:#000;position:relative;height:36px;min-width:64px;padding:0 16px;display:inline-block;font-family:"Roboto","Helvetica","Arial",sans-serif;font-size:14px;font-weight:500;text-transform:uppercase;letter-spacing:0;overflow:hidden;will-change:box-shadow,transform;-webkit-transition:box-shadow .2s cubic-bezier(.4,0,1,1),background-color .2s cubic-bezier(.4,0,.2,1),color .2s cubic-bezier(.4,0,.2,1);transition:box-shadow .2s cubic-bezier(.4,0,1,1),background-color .2s cubic-bezier(.4,0,.2,1),color .2s cubic-bezier(.4,0,.2,1);outline:none;cursor:pointer;text-decoration:none;text-align:center;line-height:36px;vertical-align:middle}.mdl-button::-moz-focus-inner{border:0}.mdl-button:hover{background-color:rgba(158,158,158,.2)}.mdl-button:focus:not(:active){background-color:rgba(0,0,0,.12)}.mdl-button:active{background-color:rgba(158,158,158,.4)}.mdl-button.mdl-button--colored{color:#3f51b5}.mdl-button.mdl-button--colored:focus:not(:active){background-color:rgba(0,0,0,.12)}input.mdl-button[type="submit"]{-webkit-appearance:none}.mdl-button--raised{background:rgba(158,158,158,.2);box-shadow:0 2px 2px 0 rgba(0,0,0,.14),0 3px 1px -2px rgba(0,0,0,.2),0 1px 5px 0 rgba(0,0,0,.12)}.mdl-button--raised:active{box-shadow:0 4px 5px 0 rgba(0,0,0,.14),0 1px 10px 0 rgba(0,0,0,.12),0 2px 4px -1px rgba(0,0,0,.2);background-color:rgba(158,158,158,.4)}.mdl-button--raised:focus:not(:active){box-shadow:0 0 8px rgba(0,0,0,.18),0 8px 16px rgba(0,0,0,.36);background-color:rgba(158,158,158,.4)}.mdl-button--raised.mdl-button--colored{background:#3f51b5;color:#fff}.mdl-button--raised.mdl-button--colored:hover{background-color:#3f51b5}.mdl-button--raised.mdl-button--colored:active{background-color:#3f51b5}.mdl-button--raised.mdl-button--colored:focus:not(:active){background-color:#3f51b5}.mdl-button--raised.mdl-button--colored .mdl-ripple{background:#fff}.mdl-button--fab{border-radius:50%;font-size:24px;height:56px;margin:auto;min-width:56px;width:56px;padding:0;overflow:hidden;background:rgba(158,158,158,.2);box-shadow:0 1px 1.5px 0 rgba(0,0,0,.12),0 1px 1px 0 rgba(0,0,0,.24);position:relative;line-height:normal}.mdl-button--fab .material-icons{position:absolute;top:50%;left:50%;-webkit-transform:translate(-12px,-12px);-ms-transform:translate(-12px,-12px);transform:translate(-12px,-12px);line-height:24px;width:24px}.mdl-button--fab.mdl-button--mini-fab{height:40px;min-width:40px;width:40px}.mdl-button--fab .mdl-button__ripple-container{border-radius:50%;-webkit-mask-image:-webkit-radial-gradient(circle,#fff,#000)}.mdl-button--fab:active{box-shadow:0 4px 5px 0 rgba(0,0,0,.14),0 1px 10px 0 rgba(0,0,0,.12),0 2px 4px -1px rgba(0,0,0,.2);background-color:rgba(158,158,158,.4)}.mdl-button--fab:focus:not(:active){box-shadow:0 0 8px rgba(0,0,0,.18),0 8px 16px rgba(0,0,0,.36);background-color:rgba(158,158,158,.4)}.mdl-button--fab.mdl-button--colored{background:#ff4081;color:#fff}.mdl-button--fab.mdl-button--colored:hover{background-color:#ff4081}.mdl-button--fab.mdl-button--colored:focus:not(:active){background-color:#ff4081}.mdl-button--fab.mdl-button--colored:active{background-color:#ff4081}.mdl-button--fab.mdl-button--colored .mdl-ripple{background:#fff}.mdl-button--icon{border-radius:50%;font-size:24px;height:32px;margin-left:0;margin-right:0;min-width:32px;width:32px;padding:0;overflow:hidden;color:inherit;line-height:normal}.mdl-button--icon .material-icons{position:absolute;top:50%;left:50%;-webkit-transform:translate(-12px,-12px);-ms-transform:translate(-12px,-12px);transform:translate(-12px,-12px);line-height:24px;width:24px}.mdl-button--icon.mdl-button--mini-icon{height:24px;min-width:24px;width:24px}.mdl-button--icon.mdl-button--mini-icon .material-icons{top:0;left:0}.mdl-button--icon .mdl-button__ripple-container{border-radius:50%;-webkit-mask-image:-webkit-radial-gradient(circle,#fff,#000)}.mdl-button__ripple-container{display:block;height:100%;left:0;position:absolute;top:0;width:100%;z-index:0;overflow:hidden}.mdl-button[disabled] .mdl-button__ripple-container .mdl-ripple,.mdl-button.mdl-button--disabled .mdl-button__ripple-container .mdl-ripple{background-color:transparent}.mdl-button--primary.mdl-button--primary{color:#3f51b5}.mdl-button--primary.mdl-button--primary .mdl-ripple{background:#fff}.mdl-button--primary.mdl-button--primary.mdl-button--raised,.mdl-button--primary.mdl-button--primary.mdl-button--fab{color:#fff;background-color:#3f51b5}.mdl-button--accent.mdl-button--accent{color:#ff4081}.mdl-button--accent.mdl-button--accent .mdl-ripple{background:#fff}.mdl-button--accent.mdl-button--accent.mdl-button--raised,.mdl-button--accent.mdl-button--accent.mdl-button--fab{color:#fff;background-color:#ff4081}.mdl-button[disabled][disabled],.mdl-button.mdl-button--disabled.mdl-button--disabled{color:rgba(0,0,0,.26);cursor:default;background-color:transparent}.mdl-button--fab[disabled][disabled],.mdl-button--fab.mdl-button--disabled.mdl-button--disabled,.mdl-button--raised[disabled][disabled],.mdl-button--raised.mdl-button--disabled.mdl-button--disabled{background-color:rgba(0,0,0,.12);color:rgba(0,0,0,.26);box-shadow:0 2px 2px 0 rgba(0,0,0,.14),0 3px 1px -2px rgba(0,0,0,.2),0 1px 5px 0 rgba(0,0,0,.12)}.mdl-button--colored[disabled][disabled],.mdl-button--colored.mdl-button--disabled.mdl-button--disabled{color:rgba(0,0,0,.26)}.mdl-button .material-icons{vertical-align:middle}.mdl-card{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;font-size:16px;font-weight:400;min-height:200px;overflow:hidden;width:330px;z-index:1;position:relative;background:#fff;border-radius:2px;box-sizing:border-box}.mdl-card__media{background-color:#ff4081;background-repeat:repeat;background-position:50% 50%;background-size:cover;background-origin:padding-box;background-attachment:scroll;box-sizing:border-box}.mdl-card__title{-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;color:#000;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-pack:stretch;-webkit-justify-content:stretch;-ms-flex-pack:stretch;justify-content:stretch;line-height:normal;padding:16px;-webkit-perspective-origin:165px 56px;perspective-origin:165px 56px;-webkit-transform-origin:165px 56px;-ms-transform-origin:165px 56px;transform-origin:165px 56px;box-sizing:border-box}.mdl-card__title.mdl-card--border{border-bottom:1px solid rgba(0,0,0,.1)}.mdl-card__title-text{-webkit-align-self:flex-end;-ms-flex-item-align:end;align-self:flex-end;color:inherit;display:-webkit-flex;display:-ms-flexbox;display:flex;font-size:24px;font-weight:300;line-height:normal;overflow:hidden;-webkit-transform-origin:149px 48px;-ms-transform-origin:149px 48px;transform-origin:149px 48px;margin:0}.mdl-card__subtitle-text{font-size:14px;color:rgba(0,0,0,.54);margin:0}.mdl-card__supporting-text{color:rgba(0,0,0,.54);font-size:13px;line-height:18px;overflow:hidden;padding:16px;width:90%}.mdl-card__actions{font-size:16px;line-height:normal;width:100%;background-color:transparent;padding:8px;box-sizing:border-box}.mdl-card__actions.mdl-card--border{border-top:1px solid rgba(0,0,0,.1)}.mdl-card--expand{-webkit-box-flex:1;-webkit-flex-grow:1;-ms-flex-positive:1;flex-grow:1}.mdl-card__menu{position:absolute;right:16px;top:16px}.mdl-checkbox{position:relative;z-index:1;vertical-align:middle;display:inline-block;box-sizing:border-box;width:100%;height:24px;margin:0;padding:0}.mdl-checkbox.is-upgraded{padding-left:24px}.mdl-checkbox__input{line-height:24px}.mdl-checkbox.is-upgraded .mdl-checkbox__input{position:absolute;width:0;height:0;margin:0;padding:0;opacity:0;-ms-appearance:none;-moz-appearance:none;-webkit-appearance:none;appearance:none;border:none}.mdl-checkbox__box-outline{position:absolute;top:3px;left:0;display:inline-block;box-sizing:border-box;width:16px;height:16px;margin:0;cursor:pointer;overflow:hidden;border:2px solid rgba(0,0,0,.54);border-radius:2px;z-index:2}.mdl-checkbox.is-checked .mdl-checkbox__box-outline{border:2px solid #3f51b5}.mdl-checkbox.is-disabled .mdl-checkbox__box-outline{border:2px solid rgba(0,0,0,.26);cursor:auto}.mdl-checkbox__focus-helper{position:absolute;top:3px;left:0;display:inline-block;box-sizing:border-box;width:16px;height:16px;border-radius:50%;background-color:transparent}.mdl-checkbox.is-focused .mdl-checkbox__focus-helper{box-shadow:0 0 0 8px rgba(0,0,0,.1);background-color:rgba(0,0,0,.1)}.mdl-checkbox.is-focused.is-checked .mdl-checkbox__focus-helper{box-shadow:0 0 0 8px rgba(63,81,181,.26);background-color:rgba(63,81,181,.26)}.mdl-checkbox__tick-outline{position:absolute;top:0;left:0;height:100%;width:100%;-webkit-mask:url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcKICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICB4bWxuczpjYz0iaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbnMjIgogICB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiCiAgIHhtbG5zOnN2Zz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgdmVyc2lvbj0iMS4xIgogICB2aWV3Qm94PSIwIDAgMSAxIgogICBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWluWU1pbiBtZWV0Ij4KICA8ZGVmcz4KICAgIDxjbGlwUGF0aCBpZD0iY2xpcCI+CiAgICAgIDxwYXRoCiAgICAgICAgIGQ9Ik0gMCwwIDAsMSAxLDEgMSwwIDAsMCB6IE0gMC44NTM0Mzc1LDAuMTY3MTg3NSAwLjk1OTY4NzUsMC4yNzMxMjUgMC40MjkzNzUsMC44MDM0Mzc1IDAuMzIzMTI1LDAuOTA5Njg3NSAwLjIxNzE4NzUsMC44MDM0Mzc1IDAuMDQwMzEyNSwwLjYyNjg3NSAwLjE0NjU2MjUsMC41MjA2MjUgMC4zMjMxMjUsMC42OTc1IDAuODUzNDM3NSwwLjE2NzE4NzUgeiIKICAgICAgICAgc3R5bGU9ImZpbGw6I2ZmZmZmZjtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZSIgLz4KICAgIDwvY2xpcFBhdGg+CiAgICA8bWFzayBpZD0ibWFzayIgbWFza1VuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgbWFza0NvbnRlbnRVbml0cz0ib2JqZWN0Qm91bmRpbmdCb3giPgogICAgICA8cGF0aAogICAgICAgICBkPSJNIDAsMCAwLDEgMSwxIDEsMCAwLDAgeiBNIDAuODUzNDM3NSwwLjE2NzE4NzUgMC45NTk2ODc1LDAuMjczMTI1IDAuNDI5Mzc1LDAuODAzNDM3NSAwLjMyMzEyNSwwLjkwOTY4NzUgMC4yMTcxODc1LDAuODAzNDM3NSAwLjA0MDMxMjUsMC42MjY4NzUgMC4xNDY1NjI1LDAuNTIwNjI1IDAuMzIzMTI1LDAuNjk3NSAwLjg1MzQzNzUsMC4xNjcxODc1IHoiCiAgICAgICAgIHN0eWxlPSJmaWxsOiNmZmZmZmY7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmUiIC8+CiAgICA8L21hc2s+CiAgPC9kZWZzPgogIDxyZWN0CiAgICAgd2lkdGg9IjEiCiAgICAgaGVpZ2h0PSIxIgogICAgIHg9IjAiCiAgICAgeT0iMCIKICAgICBjbGlwLXBhdGg9InVybCgjY2xpcCkiCiAgICAgc3R5bGU9ImZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZSIgLz4KPC9zdmc+Cg==");mask:url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcKICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICB4bWxuczpjYz0iaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbnMjIgogICB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiCiAgIHhtbG5zOnN2Zz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgdmVyc2lvbj0iMS4xIgogICB2aWV3Qm94PSIwIDAgMSAxIgogICBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWluWU1pbiBtZWV0Ij4KICA8ZGVmcz4KICAgIDxjbGlwUGF0aCBpZD0iY2xpcCI+CiAgICAgIDxwYXRoCiAgICAgICAgIGQ9Ik0gMCwwIDAsMSAxLDEgMSwwIDAsMCB6IE0gMC44NTM0Mzc1LDAuMTY3MTg3NSAwLjk1OTY4NzUsMC4yNzMxMjUgMC40MjkzNzUsMC44MDM0Mzc1IDAuMzIzMTI1LDAuOTA5Njg3NSAwLjIxNzE4NzUsMC44MDM0Mzc1IDAuMDQwMzEyNSwwLjYyNjg3NSAwLjE0NjU2MjUsMC41MjA2MjUgMC4zMjMxMjUsMC42OTc1IDAuODUzNDM3NSwwLjE2NzE4NzUgeiIKICAgICAgICAgc3R5bGU9ImZpbGw6I2ZmZmZmZjtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZSIgLz4KICAgIDwvY2xpcFBhdGg+CiAgICA8bWFzayBpZD0ibWFzayIgbWFza1VuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgbWFza0NvbnRlbnRVbml0cz0ib2JqZWN0Qm91bmRpbmdCb3giPgogICAgICA8cGF0aAogICAgICAgICBkPSJNIDAsMCAwLDEgMSwxIDEsMCAwLDAgeiBNIDAuODUzNDM3NSwwLjE2NzE4NzUgMC45NTk2ODc1LDAuMjczMTI1IDAuNDI5Mzc1LDAuODAzNDM3NSAwLjMyMzEyNSwwLjkwOTY4NzUgMC4yMTcxODc1LDAuODAzNDM3NSAwLjA0MDMxMjUsMC42MjY4NzUgMC4xNDY1NjI1LDAuNTIwNjI1IDAuMzIzMTI1LDAuNjk3NSAwLjg1MzQzNzUsMC4xNjcxODc1IHoiCiAgICAgICAgIHN0eWxlPSJmaWxsOiNmZmZmZmY7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmUiIC8+CiAgICA8L21hc2s+CiAgPC9kZWZzPgogIDxyZWN0CiAgICAgd2lkdGg9IjEiCiAgICAgaGVpZ2h0PSIxIgogICAgIHg9IjAiCiAgICAgeT0iMCIKICAgICBjbGlwLXBhdGg9InVybCgjY2xpcCkiCiAgICAgc3R5bGU9ImZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZSIgLz4KPC9zdmc+Cg==");background:0 0;-webkit-transition-duration:.28s;transition-duration:.28s;-webkit-transition-timing-function:cubic-bezier(.4,0,.2,1);transition-timing-function:cubic-bezier(.4,0,.2,1);-webkit-transition-property:background;transition-property:background}.mdl-checkbox.is-checked .mdl-checkbox__tick-outline{background:#3f51b5 url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcKICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICB4bWxuczpjYz0iaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbnMjIgogICB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiCiAgIHhtbG5zOnN2Zz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgdmVyc2lvbj0iMS4xIgogICB2aWV3Qm94PSIwIDAgMSAxIgogICBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWluWU1pbiBtZWV0Ij4KICA8cGF0aAogICAgIGQ9Ik0gMC4wNDAzODA1OSwwLjYyNjc3NjcgMC4xNDY0NDY2MSwwLjUyMDcxMDY4IDAuNDI5Mjg5MzIsMC44MDM1NTMzOSAwLjMyMzIyMzMsMC45MDk2MTk0MSB6IE0gMC4yMTcxNTcyOSwwLjgwMzU1MzM5IDAuODUzNTUzMzksMC4xNjcxNTcyOSAwLjk1OTYxOTQxLDAuMjczMjIzMyAwLjMyMzIyMzMsMC45MDk2MTk0MSB6IgogICAgIGlkPSJyZWN0Mzc4MCIKICAgICBzdHlsZT0iZmlsbDojZmZmZmZmO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lIiAvPgo8L3N2Zz4K")}.mdl-checkbox.is-checked.is-disabled .mdl-checkbox__tick-outline{background:rgba(0,0,0,.26)url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcKICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICB4bWxuczpjYz0iaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbnMjIgogICB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiCiAgIHhtbG5zOnN2Zz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgdmVyc2lvbj0iMS4xIgogICB2aWV3Qm94PSIwIDAgMSAxIgogICBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWluWU1pbiBtZWV0Ij4KICA8cGF0aAogICAgIGQ9Ik0gMC4wNDAzODA1OSwwLjYyNjc3NjcgMC4xNDY0NDY2MSwwLjUyMDcxMDY4IDAuNDI5Mjg5MzIsMC44MDM1NTMzOSAwLjMyMzIyMzMsMC45MDk2MTk0MSB6IE0gMC4yMTcxNTcyOSwwLjgwMzU1MzM5IDAuODUzNTUzMzksMC4xNjcxNTcyOSAwLjk1OTYxOTQxLDAuMjczMjIzMyAwLjMyMzIyMzMsMC45MDk2MTk0MSB6IgogICAgIGlkPSJyZWN0Mzc4MCIKICAgICBzdHlsZT0iZmlsbDojZmZmZmZmO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lIiAvPgo8L3N2Zz4K")}.mdl-checkbox__label{position:relative;cursor:pointer;font-size:16px;line-height:24px;margin:0}.mdl-checkbox.is-disabled .mdl-checkbox__label{color:rgba(0,0,0,.26);cursor:auto}.mdl-checkbox__ripple-container{position:absolute;z-index:2;top:-6px;left:-10px;box-sizing:border-box;width:36px;height:36px;border-radius:50%;cursor:pointer;overflow:hidden;-webkit-mask-image:-webkit-radial-gradient(circle,#fff,#000)}.mdl-checkbox__ripple-container .mdl-ripple{background:#3f51b5}.mdl-checkbox.is-disabled .mdl-checkbox__ripple-container{cursor:auto}.mdl-checkbox.is-disabled .mdl-checkbox__ripple-container .mdl-ripple{background:0 0}.mdl-data-table{position:relative;border:1px solid rgba(0,0,0,.12);border-collapse:collapse;white-space:nowrap;font-size:13px;background-color:#fff}.mdl-data-table thead{padding-bottom:3px}.mdl-data-table thead .mdl-data-table__select{margin-top:0}.mdl-data-table tbody tr{position:relative;height:48px;-webkit-transition-duration:.28s;transition-duration:.28s;-webkit-transition-timing-function:cubic-bezier(.4,0,.2,1);transition-timing-function:cubic-bezier(.4,0,.2,1);-webkit-transition-property:background-color;transition-property:background-color}.mdl-data-table tbody tr.is-selected{background-color:#e0e0e0}.mdl-data-table tbody tr:hover{background-color:#eee}.mdl-data-table td{text-align:right}.mdl-data-table th{padding:0 18px;text-align:right}.mdl-data-table td:first-of-type,.mdl-data-table th:first-of-type{padding-left:24px}.mdl-data-table td:last-of-type,.mdl-data-table th:last-of-type{padding-right:24px}.mdl-data-table td{position:relative;vertical-align:top;height:48px;border-top:1px solid rgba(0,0,0,.12);border-bottom:1px solid rgba(0,0,0,.12);padding:12px 18px 0;box-sizing:border-box}.mdl-data-table td .mdl-data-table__select{vertical-align:top;position:absolute;left:24px}.mdl-data-table th{position:relative;vertical-align:bottom;text-overflow:ellipsis;font-weight:700;line-height:24px;letter-spacing:0;height:48px;font-size:12px;color:rgba(0,0,0,.54);padding-bottom:8px;box-sizing:border-box}.mdl-data-table th .mdl-data-table__select{position:absolute;bottom:8px;left:24px}.mdl-data-table__select{width:16px}.mdl-data-table__cell--non-numeric.mdl-data-table__cell--non-numeric{text-align:left}.mdl-mega-footer{padding:16px 40px;color:#9e9e9e;background-color:#424242}.mdl-mega-footer--top-section:after,.mdl-mega-footer--middle-section:after,.mdl-mega-footer--bottom-section:after,.mdl-mega-footer__top-section:after,.mdl-mega-footer__middle-section:after,.mdl-mega-footer__bottom-section:after{content:'';display:block;clear:both}.mdl-mega-footer--left-section,.mdl-mega-footer__left-section,.mdl-mega-footer--right-section,.mdl-mega-footer__right-section{margin-bottom:16px}.mdl-mega-footer--right-section a,.mdl-mega-footer__right-section a{display:block;margin-bottom:16px;color:inherit;text-decoration:none}@media screen and (min-width:760px){.mdl-mega-footer--left-section,.mdl-mega-footer__left-section{float:left}.mdl-mega-footer--right-section,.mdl-mega-footer__right-section{float:right}.mdl-mega-footer--right-section a,.mdl-mega-footer__right-section a{display:inline-block;margin-left:16px;line-height:36px;vertical-align:middle}}.mdl-mega-footer--social-btn,.mdl-mega-footer__social-btn{width:36px;height:36px;padding:0;margin:0;background-color:#9e9e9e;border:none}.mdl-mega-footer--drop-down-section,.mdl-mega-footer__drop-down-section{display:block;position:relative}@media screen and (min-width:760px){.mdl-mega-footer--drop-down-section,.mdl-mega-footer__drop-down-section{width:33%}.mdl-mega-footer--drop-down-section:nth-child(1),.mdl-mega-footer--drop-down-section:nth-child(2),.mdl-mega-footer__drop-down-section:nth-child(1),.mdl-mega-footer__drop-down-section:nth-child(2){float:left}.mdl-mega-footer--drop-down-section:nth-child(3),.mdl-mega-footer__drop-down-section:nth-child(3){float:right}.mdl-mega-footer--drop-down-section:nth-child(3):after,.mdl-mega-footer__drop-down-section:nth-child(3):after{clear:right}.mdl-mega-footer--drop-down-section:nth-child(4),.mdl-mega-footer__drop-down-section:nth-child(4){clear:right;float:right}.mdl-mega-footer--middle-section:after,.mdl-mega-footer__middle-section:after{content:'';display:block;clear:both}.mdl-mega-footer--bottom-section,.mdl-mega-footer__bottom-section{padding-top:0}}@media screen and (min-width:1024px){.mdl-mega-footer--drop-down-section,.mdl-mega-footer--drop-down-section:nth-child(3),.mdl-mega-footer--drop-down-section:nth-child(4),.mdl-mega-footer__drop-down-section,.mdl-mega-footer__drop-down-section:nth-child(3),.mdl-mega-footer__drop-down-section:nth-child(4){width:24%;float:left}}.mdl-mega-footer--heading-checkbox,.mdl-mega-footer__heading-checkbox{position:absolute;width:100%;height:55.8px;padding:32px;margin:-16px 0 0;cursor:pointer;z-index:1;opacity:0}.mdl-mega-footer--heading-checkbox+.mdl-mega-footer--heading:after,.mdl-mega-footer--heading-checkbox+.mdl-mega-footer__heading:after,.mdl-mega-footer__heading-checkbox+.mdl-mega-footer--heading:after,.mdl-mega-footer__heading-checkbox+.mdl-mega-footer__heading:after{font-family:'Material Icons';content:'\E5CE'}.mdl-mega-footer--heading-checkbox:checked~.mdl-mega-footer--link-list,.mdl-mega-footer--heading-checkbox:checked~.mdl-mega-footer__link-list,.mdl-mega-footer--heading-checkbox:checked+.mdl-mega-footer--heading+.mdl-mega-footer--link-list,.mdl-mega-footer--heading-checkbox:checked+.mdl-mega-footer__heading+.mdl-mega-footer__link-list,.mdl-mega-footer__heading-checkbox:checked~.mdl-mega-footer--link-list,.mdl-mega-footer__heading-checkbox:checked~.mdl-mega-footer__link-list,.mdl-mega-footer__heading-checkbox:checked+.mdl-mega-footer--heading+.mdl-mega-footer--link-list,.mdl-mega-footer__heading-checkbox:checked+.mdl-mega-footer__heading+.mdl-mega-footer__link-list{display:none}.mdl-mega-footer--heading-checkbox:checked+.mdl-mega-footer--heading:after,.mdl-mega-footer--heading-checkbox:checked+.mdl-mega-footer__heading:after,.mdl-mega-footer__heading-checkbox:checked+.mdl-mega-footer--heading:after,.mdl-mega-footer__heading-checkbox:checked+.mdl-mega-footer__heading:after{font-family:'Material Icons';content:'\E5CF'}.mdl-mega-footer--heading,.mdl-mega-footer__heading{position:relative;width:100%;padding-right:39.8px;margin-bottom:16px;box-sizing:border-box;font-size:14px;line-height:23.8px;font-weight:500;white-space:nowrap;text-overflow:ellipsis;overflow:hidden;color:#e0e0e0}.mdl-mega-footer--heading:after,.mdl-mega-footer__heading:after{content:'';position:absolute;top:0;right:0;display:block;width:23.8px;height:23.8px;background-size:cover}.mdl-mega-footer--link-list,.mdl-mega-footer__link-list{list-style:none;padding:0;margin:0 0 32px}.mdl-mega-footer--link-list:after,.mdl-mega-footer__link-list:after{clear:both;display:block;content:''}.mdl-mega-footer--link-list li,.mdl-mega-footer__link-list li{font-size:14px;font-weight:400;letter-spacing:0;line-height:20px}.mdl-mega-footer--link-list a,.mdl-mega-footer__link-list a{color:inherit;text-decoration:none;white-space:nowrap}@media screen and (min-width:760px){.mdl-mega-footer--heading-checkbox,.mdl-mega-footer__heading-checkbox{display:none}.mdl-mega-footer--heading-checkbox+.mdl-mega-footer--heading:after,.mdl-mega-footer--heading-checkbox+.mdl-mega-footer__heading:after,.mdl-mega-footer__heading-checkbox+.mdl-mega-footer--heading:after,.mdl-mega-footer__heading-checkbox+.mdl-mega-footer__heading:after{background-image:none}.mdl-mega-footer--heading-checkbox:checked~.mdl-mega-footer--link-list,.mdl-mega-footer--heading-checkbox:checked~.mdl-mega-footer__link-list,.mdl-mega-footer--heading-checkbox:checked+.mdl-mega-footer__heading+.mdl-mega-footer__link-list,.mdl-mega-footer--heading-checkbox:checked+.mdl-mega-footer--heading+.mdl-mega-footer--link-list,.mdl-mega-footer__heading-checkbox:checked~.mdl-mega-footer--link-list,.mdl-mega-footer__heading-checkbox:checked~.mdl-mega-footer__link-list,.mdl-mega-footer__heading-checkbox:checked+.mdl-mega-footer__heading+.mdl-mega-footer__link-list,.mdl-mega-footer__heading-checkbox:checked+.mdl-mega-footer--heading+.mdl-mega-footer--link-list{display:block}.mdl-mega-footer--heading-checkbox:checked+.mdl-mega-footer--heading:after,.mdl-mega-footer--heading-checkbox:checked+.mdl-mega-footer__heading:after,.mdl-mega-footer__heading-checkbox:checked+.mdl-mega-footer--heading:after,.mdl-mega-footer__heading-checkbox:checked+.mdl-mega-footer__heading:after{content:''}}.mdl-mega-footer--bottom-section,.mdl-mega-footer__bottom-section{padding-top:16px;margin-bottom:16px}.mdl-logo{margin-bottom:16px;color:#fff}.mdl-mega-footer--bottom-section .mdl-mega-footer--link-list li,.mdl-mega-footer__bottom-section .mdl-mega-footer__link-list li{float:left;margin-bottom:0;margin-right:16px}@media screen and (min-width:760px){.mdl-logo{float:left;margin-bottom:0;margin-right:16px}}.mdl-mini-footer{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-flow:row wrap;-ms-flex-flow:row wrap;flex-flow:row wrap;-webkit-box-pack:justify;-webkit-justify-content:space-between;-ms-flex-pack:justify;justify-content:space-between;padding:32px 16px;color:#9e9e9e;background-color:#424242}.mdl-mini-footer:after{content:'';display:block}.mdl-mini-footer .mdl-logo{line-height:36px}.mdl-mini-footer--link-list,.mdl-mini-footer__link-list{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-flow:row nowrap;-ms-flex-flow:row nowrap;flex-flow:row nowrap;list-style:none;margin:0;padding:0}.mdl-mini-footer--link-list li,.mdl-mini-footer__link-list li{margin-bottom:0;margin-right:16px}@media screen and (min-width:760px){.mdl-mini-footer--link-list li,.mdl-mini-footer__link-list li{line-height:36px}}.mdl-mini-footer--link-list a,.mdl-mini-footer__link-list a{color:inherit;text-decoration:none;white-space:nowrap}.mdl-mini-footer--left-section,.mdl-mini-footer__left-section{display:inline-block;-webkit-box-ordinal-group:1;-webkit-order:0;-ms-flex-order:0;order:0}.mdl-mini-footer--right-section,.mdl-mini-footer__right-section{display:inline-block;-webkit-box-ordinal-group:2;-webkit-order:1;-ms-flex-order:1;order:1}.mdl-mini-footer--social-btn,.mdl-mini-footer__social-btn{width:36px;height:36px;padding:0;margin:0;background-color:#9e9e9e;border:none}.mdl-icon-toggle{position:relative;z-index:1;vertical-align:middle;display:inline-block;height:32px;margin:0;padding:0}.mdl-icon-toggle__input{line-height:32px}.mdl-icon-toggle.is-upgraded .mdl-icon-toggle__input{position:absolute;width:0;height:0;margin:0;padding:0;opacity:0;-ms-appearance:none;-moz-appearance:none;-webkit-appearance:none;appearance:none;border:none}.mdl-icon-toggle__label{display:inline-block;position:relative;cursor:pointer;height:32px;width:32px;min-width:32px;color:#616161;border-radius:50%;padding:0;margin-left:0;margin-right:0;text-align:center;background-color:transparent;will-change:background-color;-webkit-transition:background-color .2s cubic-bezier(.4,0,.2,1),color .2s cubic-bezier(.4,0,.2,1);transition:background-color .2s cubic-bezier(.4,0,.2,1),color .2s cubic-bezier(.4,0,.2,1)}.mdl-icon-toggle__label.material-icons{line-height:32px;font-size:24px}.mdl-icon-toggle.is-checked .mdl-icon-toggle__label{color:#3f51b5}.mdl-icon-toggle.is-disabled .mdl-icon-toggle__label{color:rgba(0,0,0,.26);cursor:auto;-webkit-transition:none;transition:none}.mdl-icon-toggle.is-focused .mdl-icon-toggle__label{background-color:rgba(0,0,0,.12)}.mdl-icon-toggle.is-focused.is-checked .mdl-icon-toggle__label{background-color:rgba(63,81,181,.26)}.mdl-icon-toggle__ripple-container{position:absolute;z-index:2;top:-2px;left:-2px;box-sizing:border-box;width:36px;height:36px;border-radius:50%;cursor:pointer;overflow:hidden;-webkit-mask-image:-webkit-radial-gradient(circle,#fff,#000)}.mdl-icon-toggle__ripple-container .mdl-ripple{background:#616161}.mdl-icon-toggle.is-disabled .mdl-icon-toggle__ripple-container{cursor:auto}.mdl-icon-toggle.is-disabled .mdl-icon-toggle__ripple-container .mdl-ripple{background:0 0}.mdl-menu__container{display:block;margin:0;padding:0;border:none;position:absolute;overflow:visible;height:0;width:0;visibility:hidden;z-index:-1}.mdl-menu__container.is-visible,.mdl-menu__container.is-animating{z-index:999;visibility:visible}.mdl-menu__outline{display:block;background:#fff;margin:0;padding:0;border:none;border-radius:2px;position:absolute;top:0;left:0;overflow:hidden;opacity:0;-webkit-transform:scale(0);-ms-transform:scale(0);transform:scale(0);-webkit-transform-origin:0 0;-ms-transform-origin:0 0;transform-origin:0 0;box-shadow:0 2px 2px 0 rgba(0,0,0,.14),0 3px 1px -2px rgba(0,0,0,.2),0 1px 5px 0 rgba(0,0,0,.12);will-change:transform;-webkit-transition:-webkit-transform .3s cubic-bezier(.4,0,.2,1),opacity .2s cubic-bezier(.4,0,.2,1);transition:transform .3s cubic-bezier(.4,0,.2,1),opacity .2s cubic-bezier(.4,0,.2,1);z-index:-1}.mdl-menu__container.is-visible .mdl-menu__outline{opacity:1;-webkit-transform:scale(1);-ms-transform:scale(1);transform:scale(1);z-index:999}.mdl-menu__outline.mdl-menu--bottom-right{-webkit-transform-origin:100% 0;-ms-transform-origin:100% 0;transform-origin:100% 0}.mdl-menu__outline.mdl-menu--top-left{-webkit-transform-origin:0 100%;-ms-transform-origin:0 100%;transform-origin:0 100%}.mdl-menu__outline.mdl-menu--top-right{-webkit-transform-origin:100% 100%;-ms-transform-origin:100% 100%;transform-origin:100% 100%}.mdl-menu{position:absolute;list-style:none;top:0;left:0;height:auto;width:auto;min-width:124px;padding:8px 0;margin:0;opacity:0;clip:rect(0 0 0 0);z-index:-1}.mdl-menu__container.is-visible .mdl-menu{opacity:1;z-index:999}.mdl-menu.is-animating{-webkit-transition:opacity .2s cubic-bezier(.4,0,.2,1),clip .3s cubic-bezier(.4,0,.2,1);transition:opacity .2s cubic-bezier(.4,0,.2,1),clip .3s cubic-bezier(.4,0,.2,1)}.mdl-menu.mdl-menu--bottom-right{left:auto;right:0}.mdl-menu.mdl-menu--top-left{top:auto;bottom:0}.mdl-menu.mdl-menu--top-right{top:auto;left:auto;bottom:0;right:0}.mdl-menu.mdl-menu--unaligned{top:auto;left:auto}.mdl-menu__item{display:block;border:none;color:rgba(0,0,0,.87);background-color:transparent;text-align:left;margin:0;padding:0 16px;outline-color:#bdbdbd;position:relative;overflow:hidden;font-size:14px;font-weight:400;letter-spacing:0;text-decoration:none;cursor:pointer;height:48px;line-height:48px;white-space:nowrap;opacity:0;-webkit-transition:opacity .2s cubic-bezier(.4,0,.2,1);transition:opacity .2s cubic-bezier(.4,0,.2,1);-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.mdl-menu__container.is-visible .mdl-menu__item{opacity:1}.mdl-menu__item::-moz-focus-inner{border:0}.mdl-menu__item[disabled]{color:#bdbdbd;background-color:transparent;cursor:auto}.mdl-menu__item[disabled]:hover{background-color:transparent}.mdl-menu__item[disabled]:focus{background-color:transparent}.mdl-menu__item[disabled] .mdl-ripple{background:0 0}.mdl-menu__item:hover{background-color:#eee}.mdl-menu__item:focus{outline:none;background-color:#eee}.mdl-menu__item:active{background-color:#e0e0e0}.mdl-menu__item--ripple-container{display:block;height:100%;left:0;position:absolute;top:0;width:100%;z-index:0;overflow:hidden}.mdl-progress{display:block;position:relative;height:4px;width:500px}.mdl-progress>.bar{display:block;position:absolute;top:0;bottom:0;width:0%;-webkit-transition:width .2s cubic-bezier(.4,0,.2,1);transition:width .2s cubic-bezier(.4,0,.2,1)}.mdl-progress>.progressbar{background-color:#3f51b5;z-index:1;left:0}.mdl-progress>.bufferbar{background-image:-webkit-linear-gradient(left,rgba(255,255,255,.7),rgba(255,255,255,.7)),-webkit-linear-gradient(left,#3f51b5 ,#3f51b5);background-image:linear-gradient(to right,rgba(255,255,255,.7),rgba(255,255,255,.7)),linear-gradient(to right,#3f51b5 ,#3f51b5);z-index:0;left:0}.mdl-progress>.auxbar{right:0}@supports (-webkit-appearance:none){.mdl-progress:not(.mdl-progress__indeterminate):not(.mdl-progress__indeterminate)>.auxbar{background-image:-webkit-linear-gradient(left,rgba(255,255,255,.7),rgba(255,255,255,.7)),-webkit-linear-gradient(left,#3f51b5 ,#3f51b5);background-image:linear-gradient(to right,rgba(255,255,255,.7),rgba(255,255,255,.7)),linear-gradient(to right,#3f51b5 ,#3f51b5);-webkit-mask:url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIj8+Cjxzdmcgd2lkdGg9IjEyIiBoZWlnaHQ9IjQiIHZpZXdQb3J0PSIwIDAgMTIgNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgogIDxlbGxpcHNlIGN4PSIyIiBjeT0iMiIgcng9IjIiIHJ5PSIyIj4KICAgIDxhbmltYXRlIGF0dHJpYnV0ZU5hbWU9ImN4IiBmcm9tPSIyIiB0bz0iLTEwIiBkdXI9IjAuNnMiIHJlcGVhdENvdW50PSJpbmRlZmluaXRlIiAvPgogIDwvZWxsaXBzZT4KICA8ZWxsaXBzZSBjeD0iMTQiIGN5PSIyIiByeD0iMiIgcnk9IjIiIGNsYXNzPSJsb2FkZXIiPgogICAgPGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iY3giIGZyb209IjE0IiB0bz0iMiIgZHVyPSIwLjZzIiByZXBlYXRDb3VudD0iaW5kZWZpbml0ZSIgLz4KICA8L2VsbGlwc2U+Cjwvc3ZnPgo=");mask:url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIj8+Cjxzdmcgd2lkdGg9IjEyIiBoZWlnaHQ9IjQiIHZpZXdQb3J0PSIwIDAgMTIgNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgogIDxlbGxpcHNlIGN4PSIyIiBjeT0iMiIgcng9IjIiIHJ5PSIyIj4KICAgIDxhbmltYXRlIGF0dHJpYnV0ZU5hbWU9ImN4IiBmcm9tPSIyIiB0bz0iLTEwIiBkdXI9IjAuNnMiIHJlcGVhdENvdW50PSJpbmRlZmluaXRlIiAvPgogIDwvZWxsaXBzZT4KICA8ZWxsaXBzZSBjeD0iMTQiIGN5PSIyIiByeD0iMiIgcnk9IjIiIGNsYXNzPSJsb2FkZXIiPgogICAgPGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iY3giIGZyb209IjE0IiB0bz0iMiIgZHVyPSIwLjZzIiByZXBlYXRDb3VudD0iaW5kZWZpbml0ZSIgLz4KICA8L2VsbGlwc2U+Cjwvc3ZnPgo=")}}.mdl-progress:not(.mdl-progress__indeterminate)>.auxbar{background-image:-webkit-linear-gradient(left,rgba(255,255,255,.9),rgba(255,255,255,.9)),-webkit-linear-gradient(left,#3f51b5 ,#3f51b5);background-image:linear-gradient(to right,rgba(255,255,255,.9),rgba(255,255,255,.9)),linear-gradient(to right,#3f51b5 ,#3f51b5)}.mdl-progress.mdl-progress__indeterminate>.bar1{-webkit-animation-name:indeterminate1;animation-name:indeterminate1}.mdl-progress.mdl-progress__indeterminate>.bar1,.mdl-progress.mdl-progress__indeterminate>.bar3{background-color:#3f51b5;-webkit-animation-duration:2s;animation-duration:2s;-webkit-animation-iteration-count:infinite;animation-iteration-count:infinite;-webkit-animation-timing-function:linear;animation-timing-function:linear}.mdl-progress.mdl-progress__indeterminate>.bar3{background-image:none;-webkit-animation-name:indeterminate2;animation-name:indeterminate2}@-webkit-keyframes indeterminate1{0%{left:0%;width:0%}50%{left:25%;width:75%}75%{left:100%;width:0%}}@keyframes indeterminate1{0%{left:0%;width:0%}50%{left:25%;width:75%}75%{left:100%;width:0%}}@-webkit-keyframes indeterminate2{0%,50%{left:0%;width:0%}75%{left:0%;width:25%}100%{left:100%;width:0%}}@keyframes indeterminate2{0%,50%{left:0%;width:0%}75%{left:0%;width:25%}100%{left:100%;width:0%}}.mdl-navigation{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-wrap:nowrap;-ms-flex-wrap:nowrap;flex-wrap:nowrap;box-sizing:border-box}.mdl-navigation__link{color:#424242;text-decoration:none;font-weight:500;font-size:13px;margin:0}.mdl-layout{width:100%;height:100%;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;overflow-y:auto;overflow-x:hidden;position:relative;-webkit-overflow-scrolling:touch}.mdl-layout.is-small-screen .mdl-layout--large-screen-only{display:none}.mdl-layout:not(.is-small-screen) .mdl-layout--small-screen-only{display:none}.mdl-layout__container{position:absolute;width:100%;height:100%}.mdl-layout__title,.mdl-layout-title{display:block;position:relative;font-family:"Roboto","Helvetica","Arial",sans-serif;font-size:20px;line-height:1;letter-spacing:.02em;font-weight:400;box-sizing:border-box}.mdl-layout-spacer{-webkit-box-flex:1;-webkit-flex-grow:1;-ms-flex-positive:1;flex-grow:1}.mdl-layout__drawer{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;-webkit-flex-wrap:nowrap;-ms-flex-wrap:nowrap;flex-wrap:nowrap;width:240px;height:100%;max-height:100%;position:absolute;top:0;left:0;box-shadow:0 2px 2px 0 rgba(0,0,0,.14),0 3px 1px -2px rgba(0,0,0,.2),0 1px 5px 0 rgba(0,0,0,.12);box-sizing:border-box;border-right:1px solid #e0e0e0;background:#fafafa;-webkit-transform:translateX(-250px);-ms-transform:translateX(-250px);transform:translateX(-250px);-webkit-transform-style:preserve-3d;transform-style:preserve-3d;will-change:transform;-webkit-transition-duration:.2s;transition-duration:.2s;-webkit-transition-timing-function:cubic-bezier(.4,0,.2,1);transition-timing-function:cubic-bezier(.4,0,.2,1);-webkit-transition-property:-webkit-transform;transition-property:transform;color:#424242;overflow:visible;overflow-y:auto;z-index:5}.mdl-layout__drawer.is-visible{-webkit-transform:translateX(0);-ms-transform:translateX(0);transform:translateX(0)}.mdl-layout__drawer.is-visible~.mdl-layout__content.mdl-layout__content{overflow:hidden}.mdl-layout__drawer>*{-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0}.mdl-layout__drawer>.mdl-layout__title,.mdl-layout__drawer>.mdl-layout-title{line-height:64px;padding-left:40px}@media screen and (max-width:1024px){.mdl-layout__drawer>.mdl-layout__title,.mdl-layout__drawer>.mdl-layout-title{line-height:56px;padding-left:16px}}.mdl-layout__drawer .mdl-navigation{-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;-webkit-box-align:stretch;-webkit-align-items:stretch;-ms-flex-align:stretch;align-items:stretch;padding-top:16px}.mdl-layout__drawer .mdl-navigation .mdl-navigation__link{display:block;-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0;padding:16px 40px;margin:0;color:#757575}@media screen and (max-width:1024px){.mdl-layout__drawer .mdl-navigation .mdl-navigation__link{padding:16px}}.mdl-layout__drawer .mdl-navigation .mdl-navigation__link:hover{background-color:#e0e0e0}.mdl-layout__drawer .mdl-navigation .mdl-navigation__link--current{background-color:#000;color:#e0e0e0}@media screen and (min-width:1025px){.mdl-layout--fixed-drawer>.mdl-layout__drawer{-webkit-transform:translateX(0);-ms-transform:translateX(0);transform:translateX(0)}}.mdl-layout__drawer-button{display:block;position:absolute;height:48px;width:48px;border:0;-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0;overflow:hidden;text-align:center;cursor:pointer;font-size:26px;line-height:50px;font-family:Helvetica,Arial,sans-serif;margin:10px 12px;top:0;left:0;color:#fff;z-index:4}.mdl-layout__header .mdl-layout__drawer-button{position:absolute;color:#fff;background-color:inherit}@media screen and (max-width:1024px){.mdl-layout__header .mdl-layout__drawer-button{margin:4px}}@media screen and (max-width:1024px){.mdl-layout__drawer-button{margin:4px;color:rgba(0,0,0,.5)}}@media screen and (min-width:1025px){.mdl-layout--fixed-drawer>.mdl-layout__drawer-button{display:none}}.mdl-layout__header{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;-webkit-flex-wrap:nowrap;-ms-flex-wrap:nowrap;flex-wrap:nowrap;-webkit-box-pack:start;-webkit-justify-content:flex-start;-ms-flex-pack:start;justify-content:flex-start;box-sizing:border-box;-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0;width:100%;margin:0;padding:0;border:none;min-height:64px;max-height:1000px;z-index:3;background-color:#3f51b5;color:#fff;box-shadow:0 2px 2px 0 rgba(0,0,0,.14),0 3px 1px -2px rgba(0,0,0,.2),0 1px 5px 0 rgba(0,0,0,.12);-webkit-transition-duration:.2s;transition-duration:.2s;-webkit-transition-timing-function:cubic-bezier(.4,0,.2,1);transition-timing-function:cubic-bezier(.4,0,.2,1);-webkit-transition-property:max-height,box-shadow;transition-property:max-height,box-shadow}@media screen and (max-width:1024px){.mdl-layout__header{min-height:56px}}.mdl-layout--fixed-drawer.is-upgraded:not(.is-small-screen)>.mdl-layout__header{margin-left:240px;width:calc(100% - 240px)}@media screen and (min-width:1025px){.mdl-layout--fixed-drawer>.mdl-layout__header .mdl-layout__header-row{padding-left:40px}}.mdl-layout__header>.mdl-layout-icon{position:absolute;left:40px;top:16px;height:32px;width:32px;overflow:hidden;z-index:3;display:block}@media screen and (max-width:1024px){.mdl-layout__header>.mdl-layout-icon{left:16px;top:12px}}.mdl-layout.has-drawer .mdl-layout__header>.mdl-layout-icon{display:none}.mdl-layout__header.is-compact{max-height:64px}@media screen and (max-width:1024px){.mdl-layout__header.is-compact{max-height:56px}}.mdl-layout__header.is-compact.has-tabs{height:112px}@media screen and (max-width:1024px){.mdl-layout__header.is-compact.has-tabs{min-height:104px}}@media screen and (max-width:1024px){.mdl-layout__header{display:none}.mdl-layout--fixed-header>.mdl-layout__header{display:-webkit-flex;display:-ms-flexbox;display:flex}}.mdl-layout__header--transparent.mdl-layout__header--transparent{background-color:transparent;box-shadow:none}.mdl-layout__header--seamed,.mdl-layout__header--scroll{box-shadow:none}.mdl-layout__header--waterfall{box-shadow:none;overflow:hidden}.mdl-layout__header--waterfall.is-casting-shadow{box-shadow:0 2px 2px 0 rgba(0,0,0,.14),0 3px 1px -2px rgba(0,0,0,.2),0 1px 5px 0 rgba(0,0,0,.12)}.mdl-layout__header-row{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row;-webkit-flex-wrap:nowrap;-ms-flex-wrap:nowrap;flex-wrap:nowrap;-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0;box-sizing:border-box;-webkit-align-self:stretch;-ms-flex-item-align:stretch;align-self:stretch;-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;height:64px;margin:0;padding:0 40px 0 80px}@media screen and (max-width:1024px){.mdl-layout__header-row{height:56px;padding:0 16px 0 72px}}.mdl-layout__header-row>*{-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0}.mdl-layout__header--scroll .mdl-layout__header-row{width:100%}.mdl-layout__header-row .mdl-navigation{margin:0;padding:0;height:64px;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row;-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center}@media screen and (max-width:1024px){.mdl-layout__header-row .mdl-navigation{height:56px}}.mdl-layout__header-row .mdl-navigation__link{display:block;color:#fff;line-height:64px;padding:0 24px}@media screen and (max-width:1024px){.mdl-layout__header-row .mdl-navigation__link{line-height:56px;padding:0 16px}}.mdl-layout__obfuscator{background-color:transparent;position:absolute;top:0;left:0;height:100%;width:100%;z-index:4;visibility:hidden;-webkit-transition-property:background-color;transition-property:background-color;-webkit-transition-duration:.2s;transition-duration:.2s;-webkit-transition-timing-function:cubic-bezier(.4,0,.2,1);transition-timing-function:cubic-bezier(.4,0,.2,1)}.mdl-layout__obfuscator.is-visible{background-color:rgba(0,0,0,.5);visibility:visible}.mdl-layout__content{-ms-flex:0 1 auto;display:inline-block;overflow-y:auto;overflow-x:hidden;-webkit-box-flex:1;-webkit-flex-grow:1;-ms-flex-positive:1;flex-grow:1;z-index:1;-webkit-overflow-scrolling:touch}.mdl-layout--fixed-drawer>.mdl-layout__content{margin-left:240px}.mdl-layout__container.has-scrolling-header .mdl-layout__content{overflow:visible}@media screen and (max-width:1024px){.mdl-layout--fixed-drawer>.mdl-layout__content{margin-left:0}.mdl-layout__container.has-scrolling-header .mdl-layout__content{overflow-y:auto;overflow-x:hidden}}.mdl-layout__tab-bar{height:96px;margin:0;width:calc(100% - 112px);padding:0 0 0 56px;display:-webkit-flex;display:-ms-flexbox;display:flex;background-color:#3f51b5;overflow-y:hidden;overflow-x:scroll}.mdl-layout__tab-bar::-webkit-scrollbar{display:none}@media screen and (max-width:1024px){.mdl-layout__tab-bar{width:calc(100% - 60px);padding:0 0 0 60px}}.mdl-layout--fixed-tabs .mdl-layout__tab-bar{padding:0;overflow:hidden;width:100%}.mdl-layout__tab-bar-container{position:relative;height:48px;width:100%;border:none;margin:0;z-index:2;-webkit-box-flex:0;-webkit-flex-grow:0;-ms-flex-positive:0;flex-grow:0;-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0;overflow:hidden}.mdl-layout__container>.mdl-layout__tab-bar-container{position:absolute;top:0;left:0}.mdl-layout__tab-bar-button{display:inline-block;position:absolute;top:0;height:48px;width:56px;z-index:4;text-align:center;background-color:#3f51b5;color:transparent;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}@media screen and (max-width:1024px){.mdl-layout__tab-bar-button{display:none;width:60px}}.mdl-layout--fixed-tabs .mdl-layout__tab-bar-button{display:none}.mdl-layout__tab-bar-button .material-icons{line-height:48px}.mdl-layout__tab-bar-button.is-active{color:#fff}.mdl-layout__tab-bar-left-button{left:0}.mdl-layout__tab-bar-right-button{right:0}.mdl-layout__tab{margin:0;border:none;padding:0 24px;float:left;position:relative;display:block;-webkit-box-flex:0;-webkit-flex-grow:0;-ms-flex-positive:0;flex-grow:0;-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0;text-decoration:none;height:48px;line-height:48px;text-align:center;font-weight:500;font-size:14px;text-transform:uppercase;color:rgba(255,255,255,.6);overflow:hidden}@media screen and (max-width:1024px){.mdl-layout__tab{padding:0 12px}}.mdl-layout--fixed-tabs .mdl-layout__tab{float:none;-webkit-box-flex:1;-webkit-flex-grow:1;-ms-flex-positive:1;flex-grow:1;padding:0}.mdl-layout.is-upgraded .mdl-layout__tab.is-active{color:#fff}.mdl-layout.is-upgraded .mdl-layout__tab.is-active::after{height:2px;width:100%;display:block;content:" ";bottom:0;left:0;position:absolute;background:#ff4081;-webkit-animation:border-expand .2s cubic-bezier(.4,0,.4,1).01s alternate forwards;animation:border-expand .2s cubic-bezier(.4,0,.4,1).01s alternate forwards;-webkit-transition:all 1s cubic-bezier(.4,0,1,1);transition:all 1s cubic-bezier(.4,0,1,1)}.mdl-layout__tab .mdl-layout__tab-ripple-container{display:block;position:absolute;height:100%;width:100%;left:0;top:0;z-index:1;overflow:hidden}.mdl-layout__tab .mdl-layout__tab-ripple-container .mdl-ripple{background-color:#fff}.mdl-layout__tab-panel{display:block}.mdl-layout.is-upgraded .mdl-layout__tab-panel{display:none}.mdl-layout.is-upgraded .mdl-layout__tab-panel.is-active{display:block}.mdl-radio{position:relative;font-size:16px;line-height:24px;display:inline-block;box-sizing:border-box;margin:0;padding-left:0}.mdl-radio.is-upgraded{padding-left:24px}.mdl-radio__button{line-height:24px}.mdl-radio.is-upgraded .mdl-radio__button{position:absolute;width:0;height:0;margin:0;padding:0;opacity:0;-ms-appearance:none;-moz-appearance:none;-webkit-appearance:none;appearance:none;border:none}.mdl-radio__outer-circle{position:absolute;top:4px;left:0;display:inline-block;box-sizing:border-box;width:16px;height:16px;margin:0;cursor:pointer;border:2px solid rgba(0,0,0,.54);border-radius:50%;z-index:2}.mdl-radio.is-checked .mdl-radio__outer-circle{border:2px solid #3f51b5}.mdl-radio.is-disabled .mdl-radio__outer-circle{border:2px solid rgba(0,0,0,.26);cursor:auto}.mdl-radio__inner-circle{position:absolute;z-index:1;margin:0;top:8px;left:4px;box-sizing:border-box;width:8px;height:8px;cursor:pointer;-webkit-transition-duration:.28s;transition-duration:.28s;-webkit-transition-timing-function:cubic-bezier(.4,0,.2,1);transition-timing-function:cubic-bezier(.4,0,.2,1);-webkit-transition-property:-webkit-transform;transition-property:transform;-webkit-transform:scale3d(0,0,0);transform:scale3d(0,0,0);border-radius:50%;background:#3f51b5}.mdl-radio.is-checked .mdl-radio__inner-circle{-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}.mdl-radio.is-disabled .mdl-radio__inner-circle{background:rgba(0,0,0,.26);cursor:auto}.mdl-radio.is-focused .mdl-radio__inner-circle{box-shadow:0 0 0 10px rgba(0,0,0,.1)}.mdl-radio__label{cursor:pointer}.mdl-radio.is-disabled .mdl-radio__label{color:rgba(0,0,0,.26);cursor:auto}.mdl-radio__ripple-container{position:absolute;z-index:2;top:-9px;left:-13px;box-sizing:border-box;width:42px;height:42px;border-radius:50%;cursor:pointer;overflow:hidden;-webkit-mask-image:-webkit-radial-gradient(circle,#fff,#000)}.mdl-radio__ripple-container .mdl-ripple{background:#3f51b5}.mdl-radio.is-disabled .mdl-radio__ripple-container{cursor:auto}.mdl-radio.is-disabled .mdl-radio__ripple-container .mdl-ripple{background:0 0}_:-ms-input-placeholder,:root .mdl-slider.mdl-slider.is-upgraded{-ms-appearance:none;height:32px;margin:0}.mdl-slider{width:calc(100% - 40px);margin:0 20px}.mdl-slider.is-upgraded{-webkit-appearance:none;-moz-appearance:none;appearance:none;height:2px;background:0 0;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;outline:0;padding:0;color:#3f51b5;-webkit-align-self:center;-ms-flex-item-align:center;align-self:center;z-index:1;cursor:pointer}.mdl-slider.is-upgraded::-moz-focus-outer{border:0}.mdl-slider.is-upgraded::-ms-tooltip{display:none}.mdl-slider.is-upgraded::-webkit-slider-runnable-track{background:0 0}.mdl-slider.is-upgraded::-moz-range-track{background:0 0;border:none}.mdl-slider.is-upgraded::-ms-track{background:0 0;color:transparent;height:2px;width:100%;border:none}.mdl-slider.is-upgraded::-ms-fill-lower{padding:0;background:linear-gradient(to right,transparent,transparent 16px,#3f51b5 16px,#3f51b5 0)}.mdl-slider.is-upgraded::-ms-fill-upper{padding:0;background:linear-gradient(to left,transparent,transparent 16px,rgba(0,0,0,.26)16px,rgba(0,0,0,.26)0)}.mdl-slider.is-upgraded::-webkit-slider-thumb{-webkit-appearance:none;width:12px;height:12px;box-sizing:border-box;border-radius:50%;background:#3f51b5;border:none;-webkit-transition:-webkit-transform .18s cubic-bezier(.4,0,.2,1),border .18s cubic-bezier(.4,0,.2,1),box-shadow .18s cubic-bezier(.4,0,.2,1),background .28s cubic-bezier(.4,0,.2,1);transition:transform .18s cubic-bezier(.4,0,.2,1),border .18s cubic-bezier(.4,0,.2,1),box-shadow .18s cubic-bezier(.4,0,.2,1),background .28s cubic-bezier(.4,0,.2,1)}.mdl-slider.is-upgraded::-moz-range-thumb{-moz-appearance:none;width:12px;height:12px;box-sizing:border-box;border-radius:50%;background-image:none;background:#3f51b5;border:none}.mdl-slider.is-upgraded:focus:not(:active)::-webkit-slider-thumb{box-shadow:0 0 0 10px rgba(63,81,181,.26)}.mdl-slider.is-upgraded:focus:not(:active)::-moz-range-thumb{box-shadow:0 0 0 10px rgba(63,81,181,.26)}.mdl-slider.is-upgraded:active::-webkit-slider-thumb{background-image:none;background:#3f51b5;-webkit-transform:scale(1.5);transform:scale(1.5)}.mdl-slider.is-upgraded:active::-moz-range-thumb{background-image:none;background:#3f51b5;transform:scale(1.5)}.mdl-slider.is-upgraded::-ms-thumb{width:32px;height:32px;border:none;border-radius:50%;background:#3f51b5;-ms-transform:scale(.375);transform:scale(.375);transition:transform .18s cubic-bezier(.4,0,.2,1),background .28s cubic-bezier(.4,0,.2,1)}.mdl-slider.is-upgraded:focus:not(:active)::-ms-thumb{background:radial-gradient(circle closest-side,#3f51b5 0%,#3f51b5 37.5%,rgba(63,81,181,.26)37.5%,rgba(63,81,181,.26)100%);-ms-transform:scale(1);transform:scale(1)}.mdl-slider.is-upgraded:active::-ms-thumb{background:#3f51b5;-ms-transform:scale(.5625);transform:scale(.5625)}.mdl-slider.is-upgraded.is-lowest-value::-webkit-slider-thumb{border:2px solid rgba(0,0,0,.26);background:0 0}.mdl-slider.is-upgraded.is-lowest-value::-moz-range-thumb{border:2px solid rgba(0,0,0,.26);background:0 0}.mdl-slider.is-upgraded.is-lowest-value+.mdl-slider__background-flex>.mdl-slider__background-upper{left:6px}.mdl-slider.is-upgraded.is-lowest-value:focus:not(:active)::-webkit-slider-thumb{box-shadow:0 0 0 10px rgba(0,0,0,.12);background:rgba(0,0,0,.12)}.mdl-slider.is-upgraded.is-lowest-value:focus:not(:active)::-moz-range-thumb{box-shadow:0 0 0 10px rgba(0,0,0,.12);background:rgba(0,0,0,.12)}.mdl-slider.is-upgraded.is-lowest-value:active::-webkit-slider-thumb{border:1.6px solid rgba(0,0,0,.26);-webkit-transform:scale(1.5);transform:scale(1.5)}.mdl-slider.is-upgraded.is-lowest-value:active+.mdl-slider__background-flex>.mdl-slider__background-upper{left:9px}.mdl-slider.is-upgraded.is-lowest-value:active::-moz-range-thumb{border:1.5px solid rgba(0,0,0,.26);transform:scale(1.5)}.mdl-slider.is-upgraded.is-lowest-value::-ms-thumb{background:radial-gradient(circle closest-side,transparent 0%,transparent 66.67%,rgba(0,0,0,.26)66.67%,rgba(0,0,0,.26)100%)}.mdl-slider.is-upgraded.is-lowest-value:focus:not(:active)::-ms-thumb{background:radial-gradient(circle closest-side,rgba(0,0,0,.12)0%,rgba(0,0,0,.12)25%,rgba(0,0,0,.26)25%,rgba(0,0,0,.26)37.5%,rgba(0,0,0,.12)37.5%,rgba(0,0,0,.12)100%);-ms-transform:scale(1);transform:scale(1)}.mdl-slider.is-upgraded.is-lowest-value:active::-ms-thumb{-ms-transform:scale(.5625);transform:scale(.5625);background:radial-gradient(circle closest-side,transparent 0%,transparent 77.78%,rgba(0,0,0,.26)77.78%,rgba(0,0,0,.26)100%)}.mdl-slider.is-upgraded.is-lowest-value::-ms-fill-lower{background:0 0}.mdl-slider.is-upgraded.is-lowest-value::-ms-fill-upper{margin-left:6px}.mdl-slider.is-upgraded.is-lowest-value:active::-ms-fill-upper{margin-left:9px}.mdl-slider.is-upgraded:disabled:focus::-webkit-slider-thumb,.mdl-slider.is-upgraded:disabled:active::-webkit-slider-thumb,.mdl-slider.is-upgraded:disabled::-webkit-slider-thumb{-webkit-transform:scale(.667);transform:scale(.667);background:rgba(0,0,0,.26)}.mdl-slider.is-upgraded:disabled:focus::-moz-range-thumb,.mdl-slider.is-upgraded:disabled:active::-moz-range-thumb,.mdl-slider.is-upgraded:disabled::-moz-range-thumb{transform:scale(.667);background:rgba(0,0,0,.26)}.mdl-slider.is-upgraded:disabled+.mdl-slider__background-flex>.mdl-slider__background-lower{background-color:rgba(0,0,0,.26);left:-6px}.mdl-slider.is-upgraded:disabled+.mdl-slider__background-flex>.mdl-slider__background-upper{left:6px}.mdl-slider.is-upgraded.is-lowest-value:disabled:focus::-webkit-slider-thumb,.mdl-slider.is-upgraded.is-lowest-value:disabled:active::-webkit-slider-thumb,.mdl-slider.is-upgraded.is-lowest-value:disabled::-webkit-slider-thumb{border:3px solid rgba(0,0,0,.26);background:0 0;-webkit-transform:scale(.667);transform:scale(.667)}.mdl-slider.is-upgraded.is-lowest-value:disabled:focus::-moz-range-thumb,.mdl-slider.is-upgraded.is-lowest-value:disabled:active::-moz-range-thumb,.mdl-slider.is-upgraded.is-lowest-value:disabled::-moz-range-thumb{border:3px solid rgba(0,0,0,.26);background:0 0;transform:scale(.667)}.mdl-slider.is-upgraded.is-lowest-value:disabled:active+.mdl-slider__background-flex>.mdl-slider__background-upper{left:6px}.mdl-slider.is-upgraded:disabled:focus::-ms-thumb,.mdl-slider.is-upgraded:disabled:active::-ms-thumb,.mdl-slider.is-upgraded:disabled::-ms-thumb{-ms-transform:scale(.25);transform:scale(.25);background:rgba(0,0,0,.26)}.mdl-slider.is-upgraded.is-lowest-value:disabled:focus::-ms-thumb,.mdl-slider.is-upgraded.is-lowest-value:disabled:active::-ms-thumb,.mdl-slider.is-upgraded.is-lowest-value:disabled::-ms-thumb{-ms-transform:scale(.25);transform:scale(.25);background:radial-gradient(circle closest-side,transparent 0%,transparent 50%,rgba(0,0,0,.26)50%,rgba(0,0,0,.26)100%)}.mdl-slider.is-upgraded:disabled::-ms-fill-lower{margin-right:6px;background:linear-gradient(to right,transparent,transparent 25px,rgba(0,0,0,.26)25px,rgba(0,0,0,.26)0)}.mdl-slider.is-upgraded:disabled::-ms-fill-upper{margin-left:6px}.mdl-slider.is-upgraded.is-lowest-value:disabled:active::-ms-fill-upper{margin-left:6px}.mdl-slider__ie-container{height:18px;overflow:visible;border:none;margin:none;padding:none}.mdl-slider__container{height:18px;position:relative;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row}.mdl-slider__container,.mdl-slider__background-flex{background:0 0;display:-webkit-flex;display:-ms-flexbox;display:flex}.mdl-slider__background-flex{position:absolute;height:2px;width:calc(100% - 52px);top:50%;left:0;margin:0 26px;overflow:hidden;border:0;padding:0;-webkit-transform:translate(0,-1px);-ms-transform:translate(0,-1px);transform:translate(0,-1px)}.mdl-slider__background-lower{background:#3f51b5}.mdl-slider__background-lower,.mdl-slider__background-upper{-webkit-box-flex:0;-webkit-flex:0;-ms-flex:0;flex:0;position:relative;border:0;padding:0}.mdl-slider__background-upper{background:rgba(0,0,0,.26);-webkit-transition:left .18s cubic-bezier(.4,0,.2,1);transition:left .18s cubic-bezier(.4,0,.2,1)}.mdl-spinner{display:inline-block;position:relative;width:28px;height:28px}.mdl-spinner:not(.is-upgraded).is-active:after{content:"Loading..."}.mdl-spinner.is-upgraded.is-active{-webkit-animation:mdl-spinner__container-rotate 1568.23529412ms linear infinite;animation:mdl-spinner__container-rotate 1568.23529412ms linear infinite}@-webkit-keyframes mdl-spinner__container-rotate{to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes mdl-spinner__container-rotate{to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}.mdl-spinner__layer{position:absolute;width:100%;height:100%;opacity:0}.mdl-spinner__layer-1{border-color:#42a5f5}.mdl-spinner--single-color .mdl-spinner__layer-1{border-color:#3f51b5}.mdl-spinner.is-active .mdl-spinner__layer-1{-webkit-animation:mdl-spinner__fill-unfill-rotate 5332ms cubic-bezier(.4,0,.2,1)infinite both,mdl-spinner__layer-1-fade-in-out 5332ms cubic-bezier(.4,0,.2,1)infinite both;animation:mdl-spinner__fill-unfill-rotate 5332ms cubic-bezier(.4,0,.2,1)infinite both,mdl-spinner__layer-1-fade-in-out 5332ms cubic-bezier(.4,0,.2,1)infinite both}.mdl-spinner__layer-2{border-color:#f44336}.mdl-spinner--single-color .mdl-spinner__layer-2{border-color:#3f51b5}.mdl-spinner.is-active .mdl-spinner__layer-2{-webkit-animation:mdl-spinner__fill-unfill-rotate 5332ms cubic-bezier(.4,0,.2,1)infinite both,mdl-spinner__layer-2-fade-in-out 5332ms cubic-bezier(.4,0,.2,1)infinite both;animation:mdl-spinner__fill-unfill-rotate 5332ms cubic-bezier(.4,0,.2,1)infinite both,mdl-spinner__layer-2-fade-in-out 5332ms cubic-bezier(.4,0,.2,1)infinite both}.mdl-spinner__layer-3{border-color:#fdd835}.mdl-spinner--single-color .mdl-spinner__layer-3{border-color:#3f51b5}.mdl-spinner.is-active .mdl-spinner__layer-3{-webkit-animation:mdl-spinner__fill-unfill-rotate 5332ms cubic-bezier(.4,0,.2,1)infinite both,mdl-spinner__layer-3-fade-in-out 5332ms cubic-bezier(.4,0,.2,1)infinite both;animation:mdl-spinner__fill-unfill-rotate 5332ms cubic-bezier(.4,0,.2,1)infinite both,mdl-spinner__layer-3-fade-in-out 5332ms cubic-bezier(.4,0,.2,1)infinite both}.mdl-spinner__layer-4{border-color:#4caf50}.mdl-spinner--single-color .mdl-spinner__layer-4{border-color:#3f51b5}.mdl-spinner.is-active .mdl-spinner__layer-4{-webkit-animation:mdl-spinner__fill-unfill-rotate 5332ms cubic-bezier(.4,0,.2,1)infinite both,mdl-spinner__layer-4-fade-in-out 5332ms cubic-bezier(.4,0,.2,1)infinite both;animation:mdl-spinner__fill-unfill-rotate 5332ms cubic-bezier(.4,0,.2,1)infinite both,mdl-spinner__layer-4-fade-in-out 5332ms cubic-bezier(.4,0,.2,1)infinite both}@-webkit-keyframes mdl-spinner__fill-unfill-rotate{12.5%{-webkit-transform:rotate(135deg);transform:rotate(135deg)}25%{-webkit-transform:rotate(270deg);transform:rotate(270deg)}37.5%{-webkit-transform:rotate(405deg);transform:rotate(405deg)}50%{-webkit-transform:rotate(540deg);transform:rotate(540deg)}62.5%{-webkit-transform:rotate(675deg);transform:rotate(675deg)}75%{-webkit-transform:rotate(810deg);transform:rotate(810deg)}87.5%{-webkit-transform:rotate(945deg);transform:rotate(945deg)}to{-webkit-transform:rotate(1080deg);transform:rotate(1080deg)}}@keyframes mdl-spinner__fill-unfill-rotate{12.5%{-webkit-transform:rotate(135deg);transform:rotate(135deg)}25%{-webkit-transform:rotate(270deg);transform:rotate(270deg)}37.5%{-webkit-transform:rotate(405deg);transform:rotate(405deg)}50%{-webkit-transform:rotate(540deg);transform:rotate(540deg)}62.5%{-webkit-transform:rotate(675deg);transform:rotate(675deg)}75%{-webkit-transform:rotate(810deg);transform:rotate(810deg)}87.5%{-webkit-transform:rotate(945deg);transform:rotate(945deg)}to{-webkit-transform:rotate(1080deg);transform:rotate(1080deg)}}@-webkit-keyframes mdl-spinner__layer-1-fade-in-out{from,25%{opacity:.99}26%,89%{opacity:0}90%,100%{opacity:.99}}@keyframes mdl-spinner__layer-1-fade-in-out{from,25%{opacity:.99}26%,89%{opacity:0}90%,100%{opacity:.99}}@-webkit-keyframes mdl-spinner__layer-2-fade-in-out{from,15%{opacity:0}25%,50%{opacity:.99}51%{opacity:0}}@keyframes mdl-spinner__layer-2-fade-in-out{from,15%{opacity:0}25%,50%{opacity:.99}51%{opacity:0}}@-webkit-keyframes mdl-spinner__layer-3-fade-in-out{from,40%{opacity:0}50%,75%{opacity:.99}76%{opacity:0}}@keyframes mdl-spinner__layer-3-fade-in-out{from,40%{opacity:0}50%,75%{opacity:.99}76%{opacity:0}}@-webkit-keyframes mdl-spinner__layer-4-fade-in-out{from,65%{opacity:0}75%,90%{opacity:.99}100%{opacity:0}}@keyframes mdl-spinner__layer-4-fade-in-out{from,65%{opacity:0}75%,90%{opacity:.99}100%{opacity:0}}.mdl-spinner__gap-patch{position:absolute;box-sizing:border-box;top:0;left:45%;width:10%;height:100%;overflow:hidden;border-color:inherit}.mdl-spinner__gap-patch .mdl-spinner__circle{width:1000%;left:-450%}.mdl-spinner__circle-clipper{display:inline-block;position:relative;width:50%;height:100%;overflow:hidden;border-color:inherit}.mdl-spinner__circle-clipper .mdl-spinner__circle{width:200%}.mdl-spinner__circle{box-sizing:border-box;height:100%;border-width:3px;border-style:solid;border-color:inherit;border-bottom-color:transparent!important;border-radius:50%;-webkit-animation:none;animation:none;position:absolute;top:0;right:0;bottom:0;left:0}.mdl-spinner__left .mdl-spinner__circle{border-right-color:transparent!important;-webkit-transform:rotate(129deg);-ms-transform:rotate(129deg);transform:rotate(129deg)}.mdl-spinner.is-active .mdl-spinner__left .mdl-spinner__circle{-webkit-animation:mdl-spinner__left-spin 1333ms cubic-bezier(.4,0,.2,1)infinite both;animation:mdl-spinner__left-spin 1333ms cubic-bezier(.4,0,.2,1)infinite both}.mdl-spinner__right .mdl-spinner__circle{left:-100%;border-left-color:transparent!important;-webkit-transform:rotate(-129deg);-ms-transform:rotate(-129deg);transform:rotate(-129deg)}.mdl-spinner.is-active .mdl-spinner__right .mdl-spinner__circle{-webkit-animation:mdl-spinner__right-spin 1333ms cubic-bezier(.4,0,.2,1)infinite both;animation:mdl-spinner__right-spin 1333ms cubic-bezier(.4,0,.2,1)infinite both}@-webkit-keyframes mdl-spinner__left-spin{from{-webkit-transform:rotate(130deg);transform:rotate(130deg)}50%{-webkit-transform:rotate(-5deg);transform:rotate(-5deg)}to{-webkit-transform:rotate(130deg);transform:rotate(130deg)}}@keyframes mdl-spinner__left-spin{from{-webkit-transform:rotate(130deg);transform:rotate(130deg)}50%{-webkit-transform:rotate(-5deg);transform:rotate(-5deg)}to{-webkit-transform:rotate(130deg);transform:rotate(130deg)}}@-webkit-keyframes mdl-spinner__right-spin{from{-webkit-transform:rotate(-130deg);transform:rotate(-130deg)}50%{-webkit-transform:rotate(5deg);transform:rotate(5deg)}to{-webkit-transform:rotate(-130deg);transform:rotate(-130deg)}}@keyframes mdl-spinner__right-spin{from{-webkit-transform:rotate(-130deg);transform:rotate(-130deg)}50%{-webkit-transform:rotate(5deg);transform:rotate(5deg)}to{-webkit-transform:rotate(-130deg);transform:rotate(-130deg)}}.mdl-switch{position:relative;z-index:1;vertical-align:middle;display:inline-block;box-sizing:border-box;width:100%;height:24px;margin:0;padding:0;overflow:visible;-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.mdl-switch.is-upgraded{padding-left:28px}.mdl-switch__input{line-height:24px}.mdl-switch.is-upgraded .mdl-switch__input{position:absolute;width:0;height:0;margin:0;padding:0;opacity:0;-ms-appearance:none;-moz-appearance:none;-webkit-appearance:none;appearance:none;border:none}.mdl-switch__track{background:rgba(0,0,0,.26);position:absolute;left:0;top:5px;height:14px;width:36px;border-radius:14px;cursor:pointer}.mdl-switch.is-checked .mdl-switch__track{background:rgba(63,81,181,.5)}.mdl-switch.is-disabled .mdl-switch__track{background:rgba(0,0,0,.12);cursor:auto}.mdl-switch__thumb{background:#fafafa;position:absolute;left:0;top:2px;height:20px;width:20px;border-radius:50%;cursor:pointer;box-shadow:0 2px 2px 0 rgba(0,0,0,.14),0 3px 1px -2px rgba(0,0,0,.2),0 1px 5px 0 rgba(0,0,0,.12);-webkit-transition-duration:.28s;transition-duration:.28s;-webkit-transition-timing-function:cubic-bezier(.4,0,.2,1);transition-timing-function:cubic-bezier(.4,0,.2,1);-webkit-transition-property:left;transition-property:left}.mdl-switch.is-checked .mdl-switch__thumb{background:#3f51b5;left:16px;box-shadow:0 3px 4px 0 rgba(0,0,0,.14),0 3px 3px -2px rgba(0,0,0,.2),0 1px 8px 0 rgba(0,0,0,.12)}.mdl-switch.is-disabled .mdl-switch__thumb{background:#bdbdbd;cursor:auto}.mdl-switch__focus-helper{position:absolute;top:50%;left:50%;-webkit-transform:translate(-4px,-4px);-ms-transform:translate(-4px,-4px);transform:translate(-4px,-4px);display:inline-block;box-sizing:border-box;width:8px;height:8px;border-radius:50%;background-color:transparent}.mdl-switch.is-focused .mdl-switch__focus-helper{box-shadow:0 0 0 20px rgba(0,0,0,.1);background-color:rgba(0,0,0,.1)}.mdl-switch.is-focused.is-checked .mdl-switch__focus-helper{box-shadow:0 0 0 20px rgba(63,81,181,.26);background-color:rgba(63,81,181,.26)}.mdl-switch__label{position:relative;cursor:pointer;font-size:16px;line-height:24px;margin:0;left:24px}.mdl-switch.is-disabled .mdl-switch__label{color:#bdbdbd;cursor:auto}.mdl-switch__ripple-container{position:absolute;z-index:2;top:-12px;left:-14px;box-sizing:border-box;width:48px;height:48px;border-radius:50%;cursor:pointer;overflow:hidden;-webkit-mask-image:-webkit-radial-gradient(circle,#fff,#000);-webkit-transition-duration:.4s;transition-duration:.4s;-webkit-transition-timing-function:step-end;transition-timing-function:step-end;-webkit-transition-property:left;transition-property:left}.mdl-switch__ripple-container .mdl-ripple{background:#3f51b5}.mdl-switch.is-disabled .mdl-switch__ripple-container{cursor:auto}.mdl-switch.is-disabled .mdl-switch__ripple-container .mdl-ripple{background:0 0}.mdl-switch.is-checked .mdl-switch__ripple-container{cursor:auto;left:2px}.mdl-tabs{display:block;width:100%}.mdl-tabs__tab-bar{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;-webkit-align-content:space-between;-ms-flex-line-pack:justify;align-content:space-between;-webkit-box-align:start;-webkit-align-items:flex-start;-ms-flex-align:start;align-items:flex-start;height:48px;padding:0;margin:0;border-bottom:1px solid #e0e0e0}.mdl-tabs__tab{margin:0;border:none;padding:0 24px;float:left;position:relative;display:block;color:red;text-decoration:none;height:48px;line-height:48px;text-align:center;font-weight:500;font-size:14px;text-transform:uppercase;color:rgba(0,0,0,.54);overflow:hidden}.mdl-tabs.is-upgraded .mdl-tabs__tab.is-active{color:rgba(0,0,0,.87)}.mdl-tabs.is-upgraded .mdl-tabs__tab.is-active:after{height:2px;width:100%;display:block;content:" ";bottom:0;left:0;position:absolute;background:#3f51b5;-webkit-animation:border-expand .2s cubic-bezier(.4,0,.4,1).01s alternate forwards;animation:border-expand .2s cubic-bezier(.4,0,.4,1).01s alternate forwards;-webkit-transition:all 1s cubic-bezier(.4,0,1,1);transition:all 1s cubic-bezier(.4,0,1,1)}.mdl-tabs__tab .mdl-tabs__ripple-container{display:block;position:absolute;height:100%;width:100%;left:0;top:0;z-index:1;overflow:hidden}.mdl-tabs__tab .mdl-tabs__ripple-container .mdl-ripple{background:#3f51b5}.mdl-tabs__panel{display:block}.mdl-tabs.is-upgraded .mdl-tabs__panel{display:none}.mdl-tabs.is-upgraded .mdl-tabs__panel.is-active{display:block}@-webkit-keyframes border-expand{0%{opacity:0;width:0}100%{opacity:1;width:100%}}@keyframes border-expand{0%{opacity:0;width:0}100%{opacity:1;width:100%}}.mdl-textfield{position:relative;font-size:16px;display:inline-block;box-sizing:border-box;width:300px;max-width:100%;margin:0;padding:20px 0}.mdl-textfield .mdl-button{position:absolute;bottom:20px}.mdl-textfield--align-right{text-align:right}.mdl-textfield--full-width{width:100%}.mdl-textfield--expandable{min-width:32px;width:auto;min-height:32px}.mdl-textfield__input{border:none;border-bottom:1px solid rgba(0,0,0,.12);display:block;font-size:16px;margin:0;padding:4px 0;width:100%;background:0 0;text-align:left;color:inherit}.mdl-textfield.is-focused .mdl-textfield__input{outline:none}.mdl-textfield.is-invalid .mdl-textfield__input{border-color:#de3226;box-shadow:none}.mdl-textfield.is-disabled .mdl-textfield__input{background-color:transparent;border-bottom:1px dotted rgba(0,0,0,.12);color:rgba(0,0,0,.26)}.mdl-textfield textarea.mdl-textfield__input{display:block}.mdl-textfield__label{bottom:0;color:rgba(0,0,0,.26);font-size:16px;left:0;right:0;pointer-events:none;position:absolute;display:block;top:24px;width:100%;overflow:hidden;white-space:nowrap;text-align:left}.mdl-textfield.is-dirty .mdl-textfield__label{visibility:hidden}.mdl-textfield--floating-label .mdl-textfield__label{-webkit-transition-duration:.2s;transition-duration:.2s;-webkit-transition-timing-function:cubic-bezier(.4,0,.2,1);transition-timing-function:cubic-bezier(.4,0,.2,1)}.mdl-textfield.is-disabled.is-disabled .mdl-textfield__label{color:rgba(0,0,0,.26)}.mdl-textfield--floating-label.is-focused .mdl-textfield__label,.mdl-textfield--floating-label.is-dirty .mdl-textfield__label{color:#3f51b5;font-size:12px;top:4px;visibility:visible}.mdl-textfield--floating-label.is-focused .mdl-textfield__expandable-holder .mdl-textfield__label,.mdl-textfield--floating-label.is-dirty .mdl-textfield__expandable-holder .mdl-textfield__label{top:-16px}.mdl-textfield--floating-label.is-invalid .mdl-textfield__label{color:#de3226;font-size:12px}.mdl-textfield__label:after{background-color:#3f51b5;bottom:20px;content:'';height:2px;left:45%;position:absolute;-webkit-transition-duration:.2s;transition-duration:.2s;-webkit-transition-timing-function:cubic-bezier(.4,0,.2,1);transition-timing-function:cubic-bezier(.4,0,.2,1);visibility:hidden;width:10px}.mdl-textfield.is-focused .mdl-textfield__label:after{left:0;visibility:visible;width:100%}.mdl-textfield.is-invalid .mdl-textfield__label:after{background-color:#de3226}.mdl-textfield__error{color:#de3226;position:absolute;font-size:12px;margin-top:3px;visibility:hidden;display:block}.mdl-textfield.is-invalid .mdl-textfield__error{visibility:visible}.mdl-textfield__expandable-holder{position:relative;margin-left:32px;-webkit-transition-duration:.2s;transition-duration:.2s;-webkit-transition-timing-function:cubic-bezier(.4,0,.2,1);transition-timing-function:cubic-bezier(.4,0,.2,1);display:inline-block;max-width:.1px}.mdl-textfield.is-focused .mdl-textfield__expandable-holder,.mdl-textfield.is-dirty .mdl-textfield__expandable-holder{max-width:600px}.mdl-textfield__expandable-holder .mdl-textfield__label:after{bottom:0}.mdl-tooltip{-webkit-transform:scale(0);-ms-transform:scale(0);transform:scale(0);-webkit-transform-origin:top center;-ms-transform-origin:top center;transform-origin:top center;will-change:transform;z-index:999;background:rgba(97,97,97,.9);border-radius:2px;color:#fff;display:inline-block;font-size:10px;font-weight:500;line-height:14px;max-width:170px;position:fixed;top:-500px;left:-500px;padding:8px;text-align:center}.mdl-tooltip.is-active{-webkit-animation:pulse 200ms cubic-bezier(0,0,.2,1)forwards;animation:pulse 200ms cubic-bezier(0,0,.2,1)forwards}.mdl-tooltip--large{line-height:14px;font-size:14px;padding:16px}@-webkit-keyframes pulse{0%{-webkit-transform:scale(0);transform:scale(0);opacity:0}50%{-webkit-transform:scale(.99);transform:scale(.99)}100%{-webkit-transform:scale(1);transform:scale(1);opacity:1;visibility:visible}}@keyframes pulse{0%{-webkit-transform:scale(0);transform:scale(0);opacity:0}50%{-webkit-transform:scale(.99);transform:scale(.99)}100%{-webkit-transform:scale(1);transform:scale(1);opacity:1;visibility:visible}}.mdl-shadow--2dp{box-shadow:0 2px 2px 0 rgba(0,0,0,.14),0 3px 1px -2px rgba(0,0,0,.2),0 1px 5px 0 rgba(0,0,0,.12)}.mdl-shadow--3dp{box-shadow:0 3px 4px 0 rgba(0,0,0,.14),0 3px 3px -2px rgba(0,0,0,.2),0 1px 8px 0 rgba(0,0,0,.12)}.mdl-shadow--4dp{box-shadow:0 4px 5px 0 rgba(0,0,0,.14),0 1px 10px 0 rgba(0,0,0,.12),0 2px 4px -1px rgba(0,0,0,.2)}.mdl-shadow--6dp{box-shadow:0 6px 10px 0 rgba(0,0,0,.14),0 1px 18px 0 rgba(0,0,0,.12),0 3px 5px -1px rgba(0,0,0,.2)}.mdl-shadow--8dp{box-shadow:0 8px 10px 1px rgba(0,0,0,.14),0 3px 14px 2px rgba(0,0,0,.12),0 5px 5px -3px rgba(0,0,0,.2)}.mdl-shadow--16dp{box-shadow:0 16px 24px 2px rgba(0,0,0,.14),0 6px 30px 5px rgba(0,0,0,.12),0 8px 10px -5px rgba(0,0,0,.2)}.mdl-grid{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-flow:row wrap;-ms-flex-flow:row wrap;flex-flow:row wrap;margin:0 auto;-webkit-box-align:stretch;-webkit-align-items:stretch;-ms-flex-align:stretch;align-items:stretch}.mdl-grid.mdl-grid--no-spacing{padding:0}.mdl-cell{box-sizing:border-box}.mdl-cell--top{-webkit-align-self:flex-start;-ms-flex-item-align:start;align-self:flex-start}.mdl-cell--middle{-webkit-align-self:center;-ms-flex-item-align:center;align-self:center}.mdl-cell--bottom{-webkit-align-self:flex-end;-ms-flex-item-align:end;align-self:flex-end}.mdl-cell--stretch{-webkit-align-self:stretch;-ms-flex-item-align:stretch;align-self:stretch}.mdl-grid.mdl-grid--no-spacing>.mdl-cell{margin:0}@media (max-width:479px){.mdl-grid{padding:8px}.mdl-cell{margin:8px;width:calc(100% - 16px)}.mdl-grid--no-spacing>.mdl-cell{width:100%}.mdl-cell--hide-phone{display:none!important}.mdl-cell--1-col,.mdl-cell--1-col-phone.mdl-cell--1-col-phone{width:calc(25% - 16px)}.mdl-grid--no-spacing>.mdl-cell--1-col,.mdl-grid--no-spacing>.mdl-cell--1-col-phone.mdl-cell--1-col-phone{width:25%}.mdl-cell--2-col,.mdl-cell--2-col-phone.mdl-cell--2-col-phone{width:calc(50% - 16px)}.mdl-grid--no-spacing>.mdl-cell--2-col,.mdl-grid--no-spacing>.mdl-cell--2-col-phone.mdl-cell--2-col-phone{width:50%}.mdl-cell--3-col,.mdl-cell--3-col-phone.mdl-cell--3-col-phone{width:calc(75% - 16px)}.mdl-grid--no-spacing>.mdl-cell--3-col,.mdl-grid--no-spacing>.mdl-cell--3-col-phone.mdl-cell--3-col-phone{width:75%}.mdl-cell--4-col,.mdl-cell--4-col-phone.mdl-cell--4-col-phone{width:calc(100% - 16px)}.mdl-grid--no-spacing>.mdl-cell--4-col,.mdl-grid--no-spacing>.mdl-cell--4-col-phone.mdl-cell--4-col-phone{width:100%}.mdl-cell--5-col,.mdl-cell--5-col-phone.mdl-cell--5-col-phone{width:calc(100% - 16px)}.mdl-grid--no-spacing>.mdl-cell--5-col,.mdl-grid--no-spacing>.mdl-cell--5-col-phone.mdl-cell--5-col-phone{width:100%}.mdl-cell--6-col,.mdl-cell--6-col-phone.mdl-cell--6-col-phone{width:calc(100% - 16px)}.mdl-grid--no-spacing>.mdl-cell--6-col,.mdl-grid--no-spacing>.mdl-cell--6-col-phone.mdl-cell--6-col-phone{width:100%}.mdl-cell--7-col,.mdl-cell--7-col-phone.mdl-cell--7-col-phone{width:calc(100% - 16px)}.mdl-grid--no-spacing>.mdl-cell--7-col,.mdl-grid--no-spacing>.mdl-cell--7-col-phone.mdl-cell--7-col-phone{width:100%}.mdl-cell--8-col,.mdl-cell--8-col-phone.mdl-cell--8-col-phone{width:calc(100% - 16px)}.mdl-grid--no-spacing>.mdl-cell--8-col,.mdl-grid--no-spacing>.mdl-cell--8-col-phone.mdl-cell--8-col-phone{width:100%}.mdl-cell--9-col,.mdl-cell--9-col-phone.mdl-cell--9-col-phone{width:calc(100% - 16px)}.mdl-grid--no-spacing>.mdl-cell--9-col,.mdl-grid--no-spacing>.mdl-cell--9-col-phone.mdl-cell--9-col-phone{width:100%}.mdl-cell--10-col,.mdl-cell--10-col-phone.mdl-cell--10-col-phone{width:calc(100% - 16px)}.mdl-grid--no-spacing>.mdl-cell--10-col,.mdl-grid--no-spacing>.mdl-cell--10-col-phone.mdl-cell--10-col-phone{width:100%}.mdl-cell--11-col,.mdl-cell--11-col-phone.mdl-cell--11-col-phone{width:calc(100% - 16px)}.mdl-grid--no-spacing>.mdl-cell--11-col,.mdl-grid--no-spacing>.mdl-cell--11-col-phone.mdl-cell--11-col-phone{width:100%}.mdl-cell--12-col,.mdl-cell--12-col-phone.mdl-cell--12-col-phone{width:calc(100% - 16px)}.mdl-grid--no-spacing>.mdl-cell--12-col,.mdl-grid--no-spacing>.mdl-cell--12-col-phone.mdl-cell--12-col-phone{width:100%}}@media (min-width:480px) and (max-width:839px){.mdl-grid{padding:8px}.mdl-cell{margin:8px;width:calc(50% - 16px)}.mdl-grid--no-spacing>.mdl-cell{width:50%}.mdl-cell--hide-tablet{display:none!important}.mdl-cell--1-col,.mdl-cell--1-col-tablet.mdl-cell--1-col-tablet{width:calc(12.5% - 16px)}.mdl-grid--no-spacing>.mdl-cell--1-col,.mdl-grid--no-spacing>.mdl-cell--1-col-tablet.mdl-cell--1-col-tablet{width:12.5%}.mdl-cell--2-col,.mdl-cell--2-col-tablet.mdl-cell--2-col-tablet{width:calc(25% - 16px)}.mdl-grid--no-spacing>.mdl-cell--2-col,.mdl-grid--no-spacing>.mdl-cell--2-col-tablet.mdl-cell--2-col-tablet{width:25%}.mdl-cell--3-col,.mdl-cell--3-col-tablet.mdl-cell--3-col-tablet{width:calc(37.5% - 16px)}.mdl-grid--no-spacing>.mdl-cell--3-col,.mdl-grid--no-spacing>.mdl-cell--3-col-tablet.mdl-cell--3-col-tablet{width:37.5%}.mdl-cell--4-col,.mdl-cell--4-col-tablet.mdl-cell--4-col-tablet{width:calc(50% - 16px)}.mdl-grid--no-spacing>.mdl-cell--4-col,.mdl-grid--no-spacing>.mdl-cell--4-col-tablet.mdl-cell--4-col-tablet{width:50%}.mdl-cell--5-col,.mdl-cell--5-col-tablet.mdl-cell--5-col-tablet{width:calc(62.5% - 16px)}.mdl-grid--no-spacing>.mdl-cell--5-col,.mdl-grid--no-spacing>.mdl-cell--5-col-tablet.mdl-cell--5-col-tablet{width:62.5%}.mdl-cell--6-col,.mdl-cell--6-col-tablet.mdl-cell--6-col-tablet{width:calc(75% - 16px)}.mdl-grid--no-spacing>.mdl-cell--6-col,.mdl-grid--no-spacing>.mdl-cell--6-col-tablet.mdl-cell--6-col-tablet{width:75%}.mdl-cell--7-col,.mdl-cell--7-col-tablet.mdl-cell--7-col-tablet{width:calc(87.5% - 16px)}.mdl-grid--no-spacing>.mdl-cell--7-col,.mdl-grid--no-spacing>.mdl-cell--7-col-tablet.mdl-cell--7-col-tablet{width:87.5%}.mdl-cell--8-col,.mdl-cell--8-col-tablet.mdl-cell--8-col-tablet{width:calc(100% - 16px)}.mdl-grid--no-spacing>.mdl-cell--8-col,.mdl-grid--no-spacing>.mdl-cell--8-col-tablet.mdl-cell--8-col-tablet{width:100%}.mdl-cell--9-col,.mdl-cell--9-col-tablet.mdl-cell--9-col-tablet{width:calc(100% - 16px)}.mdl-grid--no-spacing>.mdl-cell--9-col,.mdl-grid--no-spacing>.mdl-cell--9-col-tablet.mdl-cell--9-col-tablet{width:100%}.mdl-cell--10-col,.mdl-cell--10-col-tablet.mdl-cell--10-col-tablet{width:calc(100% - 16px)}.mdl-grid--no-spacing>.mdl-cell--10-col,.mdl-grid--no-spacing>.mdl-cell--10-col-tablet.mdl-cell--10-col-tablet{width:100%}.mdl-cell--11-col,.mdl-cell--11-col-tablet.mdl-cell--11-col-tablet{width:calc(100% - 16px)}.mdl-grid--no-spacing>.mdl-cell--11-col,.mdl-grid--no-spacing>.mdl-cell--11-col-tablet.mdl-cell--11-col-tablet{width:100%}.mdl-cell--12-col,.mdl-cell--12-col-tablet.mdl-cell--12-col-tablet{width:calc(100% - 16px)}.mdl-grid--no-spacing>.mdl-cell--12-col,.mdl-grid--no-spacing>.mdl-cell--12-col-tablet.mdl-cell--12-col-tablet{width:100%}}@media (min-width:840px){.mdl-grid{padding:8px}.mdl-cell{margin:8px;width:calc(33.3333333333% - 16px)}.mdl-grid--no-spacing>.mdl-cell{width:33.3333333333%}.mdl-cell--hide-desktop{display:none!important}.mdl-cell--1-col,.mdl-cell--1-col-desktop.mdl-cell--1-col-desktop{width:calc(8.3333333333% - 16px)}.mdl-grid--no-spacing>.mdl-cell--1-col,.mdl-grid--no-spacing>.mdl-cell--1-col-desktop.mdl-cell--1-col-desktop{width:8.3333333333%}.mdl-cell--2-col,.mdl-cell--2-col-desktop.mdl-cell--2-col-desktop{width:calc(16.6666666667% - 16px)}.mdl-grid--no-spacing>.mdl-cell--2-col,.mdl-grid--no-spacing>.mdl-cell--2-col-desktop.mdl-cell--2-col-desktop{width:16.6666666667%}.mdl-cell--3-col,.mdl-cell--3-col-desktop.mdl-cell--3-col-desktop{width:calc(25% - 16px)}.mdl-grid--no-spacing>.mdl-cell--3-col,.mdl-grid--no-spacing>.mdl-cell--3-col-desktop.mdl-cell--3-col-desktop{width:25%}.mdl-cell--4-col,.mdl-cell--4-col-desktop.mdl-cell--4-col-desktop{width:calc(33.3333333333% - 16px)}.mdl-grid--no-spacing>.mdl-cell--4-col,.mdl-grid--no-spacing>.mdl-cell--4-col-desktop.mdl-cell--4-col-desktop{width:33.3333333333%}.mdl-cell--5-col,.mdl-cell--5-col-desktop.mdl-cell--5-col-desktop{width:calc(41.6666666667% - 16px)}.mdl-grid--no-spacing>.mdl-cell--5-col,.mdl-grid--no-spacing>.mdl-cell--5-col-desktop.mdl-cell--5-col-desktop{width:41.6666666667%}.mdl-cell--6-col,.mdl-cell--6-col-desktop.mdl-cell--6-col-desktop{width:calc(50% - 16px)}.mdl-grid--no-spacing>.mdl-cell--6-col,.mdl-grid--no-spacing>.mdl-cell--6-col-desktop.mdl-cell--6-col-desktop{width:50%}.mdl-cell--7-col,.mdl-cell--7-col-desktop.mdl-cell--7-col-desktop{width:calc(58.3333333333% - 16px)}.mdl-grid--no-spacing>.mdl-cell--7-col,.mdl-grid--no-spacing>.mdl-cell--7-col-desktop.mdl-cell--7-col-desktop{width:58.3333333333%}.mdl-cell--8-col,.mdl-cell--8-col-desktop.mdl-cell--8-col-desktop{width:calc(66.6666666667% - 16px)}.mdl-grid--no-spacing>.mdl-cell--8-col,.mdl-grid--no-spacing>.mdl-cell--8-col-desktop.mdl-cell--8-col-desktop{width:66.6666666667%}.mdl-cell--9-col,.mdl-cell--9-col-desktop.mdl-cell--9-col-desktop{width:calc(75% - 16px)}.mdl-grid--no-spacing>.mdl-cell--9-col,.mdl-grid--no-spacing>.mdl-cell--9-col-desktop.mdl-cell--9-col-desktop{width:75%}.mdl-cell--10-col,.mdl-cell--10-col-desktop.mdl-cell--10-col-desktop{width:calc(83.3333333333% - 16px)}.mdl-grid--no-spacing>.mdl-cell--10-col,.mdl-grid--no-spacing>.mdl-cell--10-col-desktop.mdl-cell--10-col-desktop{width:83.3333333333%}.mdl-cell--11-col,.mdl-cell--11-col-desktop.mdl-cell--11-col-desktop{width:calc(91.6666666667% - 16px)}.mdl-grid--no-spacing>.mdl-cell--11-col,.mdl-grid--no-spacing>.mdl-cell--11-col-desktop.mdl-cell--11-col-desktop{width:91.6666666667%}.mdl-cell--12-col,.mdl-cell--12-col-desktop.mdl-cell--12-col-desktop{width:calc(100% - 16px)}.mdl-grid--no-spacing>.mdl-cell--12-col,.mdl-grid--no-spacing>.mdl-cell--12-col-desktop.mdl-cell--12-col-desktop{width:100%}} +/*# sourceMappingURL=material.min.css.map */ diff --git a/examples/scalajs-play-core-react/server/src/main/assets/material/material.min.css.map b/examples/scalajs-play-core-react/server/src/main/assets/material/material.min.css.map new file mode 100644 index 0000000..0e20fbc --- /dev/null +++ b/examples/scalajs-play-core-react/server/src/main/assets/material/material.min.css.map @@ -0,0 +1 @@ +{"version":3,"sources":["material.min.css","material.css","material-design-lite.css","material-design-lite.scss","_variables.scss","_color-definitions.scss","_functions.scss","_mixins.scss","resets/_resets.scss","resets/_h5bp.scss","resets/_mobile.scss","typography/_typography.scss","palette/_palette.scss","ripple/_ripple.scss","animation/_animation.scss","badge/_badge.scss","button/_button.scss","card/_card.scss","checkbox/_checkbox.scss","data-table/_data-table.scss","footer/_mega_footer.scss","footer/_mini_footer.scss","icon-toggle/_icon-toggle.scss","menu/_menu.scss","progress/_progress.scss","layout/_layout.scss","radio/_radio.scss","slider/_slider.scss","spinner/_spinner.scss","switch/_switch.scss","tabs/_tabs.scss","textfield/_textfield.scss","tooltip/_tooltip.scss","shadow/_shadow.scss","grid/_grid.scss"],"names":[],"mappings":"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;AAED;EACE,4CAAiE,EAClE;;AAED;EACE,+BAAsD,EACvD;;AAED;EACE,0CAAiE,EAClE;;AAID;EACE,iCAAsD,EACvD;;AAED;EACE,4CAAiE,EAClE;;AAED;EACE,mCAAqD,EACtD;;AAED;EACE,8CAAgE,EACjE;;AAED;EACE,mCAAsD,EACvD;;AAED;EACE,8CAAiE,EAClE;;AAED;EACE,mCAAsD,EACvD;;AAED;EACE,8CAAiE,EAClE;;AAED;EACE,kCAAsD,EACvD;;AAED;EACE,6CAAiE,EAClE;;AAED;EACE,kCAAsD,EACvD;;AAED;EACE,6CAAiE,EAClE;;AAED;EACE,iCAAsD,EACvD;;AAED;EACE,4CAAiE,EAClE;;AAED;EACE,iCAAsD,EACvD;;AAED;EACE,4CAAiE,EAClE;;AAED;EACE,iCAAsD,EACvD;;AAED;EACE,4CAAiE,EAClE;;AAED;EACE,iCAAsD,EACvD;;AAED;EACE,4CAAiE,EAClE;;AAED;EACE,iCAAsD,EACvD;;AAED;EACE,4CAAiE,EAClE;;AAED;EACE,mCAAuD,EACxD;;AAED;EACE,8CAAkE,EACnE;;AAED;EACE,kCAAuD,EACxD;;AAED;EACE,6CAAkE,EACnE;;AAED;EACE,gCAAuD,EACxD;;AAED;EACE,2CAAkE,EACnE;;AAED;EACE,iCAAuD,EACxD;;AAED;EACE,4CAAkE,EACnE;;AAID;EACE,kCAAwD,EACzD;;AAED;EACE,6CAAmE,EACpE;;AAED;EACE,mCAAuD,EACxD;;AAED;EACE,8CAAkE,EACnE;;AAED;EACE,mCAAwD,EACzD;;AAED;EACE,8CAAmE,EACpE;;AAED;EACE,mCAAwD,EACzD;;AAED;EACE,8CAAmE,EACpE;;AAED;EACE,mCAAwD,EACzD;;AAED;EACE,8CAAmE,EACpE;;AAED;EACE,kCAAwD,EACzD;;AAED;EACE,6CAAmE,EACpE;;AAED;EACE,kCAAwD,EACzD;;AAED;EACE,6CAAmE,EACpE;;AAED;EACE,kCAAwD,EACzD;;AAED;EACE,6CAAmE,EACpE;;AAED;EACE,kCAAwD,EACzD;;AAED;EACE,6CAAmE,EACpE;;AAED;EACE,kCAAwD,EACzD;;AAED;EACE,6CAAmE,EACpE;;AAED;EACE,iCAAwD,EACzD;;AAED;EACE,4CAAmE,EACpE;;AAED;EACE,mCAAyD,EAC1D;;AAED;EACE,8CAAoE,EACrE;;AAED;EACE,kCAAyD,EAC1D;;AAED;EACE,6CAAoE,EACrE;;AAED;EACE,iCAAyD,EAC1D;;AAED;EACE,4CAAoE,EACrE;;AAED;EACE,iCAAyD,EAC1D;;AAED;EACE,4CAAoE,EACrE;;AAID;EACE,kCAA6D,EAC9D;;AAED;EACE,6CAAwE,EACzE;;AAED;EACE,mCAA4D,EAC7D;;AAED;EACE,8CAAuE,EACxE;;AAED;EACE,mCAA6D,EAC9D;;AAED;EACE,8CAAwE,EACzE;;AAED;EACE,mCAA6D,EAC9D;;AAED;EACE,8CAAwE,EACzE;;AAED;EACE,mCAA6D,EAC9D;;AAED;EACE,8CAAwE,EACzE;;AAED;EACE,kCAA6D,EAC9D;;AAED;EACE,6CAAwE,EACzE;;AAED;EACE,kCAA6D,EAC9D;;AAED;EACE,6CAAwE,EACzE;;AAED;EACE,iCAA6D,EAC9D;;AAED;EACE,4CAAwE,EACzE;;AAED;EACE,iCAA6D,EAC9D;;AAED;EACE,4CAAwE,EACzE;;AAED;EACE,iCAA6D,EAC9D;;AAED;EACE,4CAAwE,EACzE;;AAED;EACE,iCAA6D,EAC9D;;AAED;EACE,4CAAwE,EACzE;;AAED;EACE,mCAA8D,EAC/D;;AAED;EACE,8CAAyE,EAC1E;;AAED;EACE,kCAA8D,EAC/D;;AAED;EACE,6CAAyE,EAC1E;;AAED;EACE,kCAA8D,EAC/D;;AAED;EACE,6CAAyE,EAC1E;;AAED;EACE,gCAA8D,EAC/D;;AAED;EACE,2CAAyE,EAC1E;;AAID;EACE,iCAAwD,EACzD;;AAED;EACE,4CAAmE,EACpE;;AAED;EACE,mCAAuD,EACxD;;AAED;EACE,8CAAkE,EACnE;;AAED;EACE,mCAAwD,EACzD;;AAED;EACE,8CAAmE,EACpE;;AAED;EACE,mCAAwD,EACzD;;AAED;EACE,8CAAmE,EACpE;;AAED;EACE,mCAAwD,EACzD;;AAED;EACE,8CAAmE,EACpE;;AAED;EACE,kCAAwD,EACzD;;AAED;EACE,6CAAmE,EACpE;;AAED;EACE,iCAAwD,EACzD;;AAED;EACE,4CAAmE,EACpE;;AAED;EACE,iCAAwD,EACzD;;AAED;EACE,4CAAmE,EACpE;;AAED;EACE,iCAAwD,EACzD;;AAED;EACE,4CAAmE,EACpE;;AAED;EACE,iCAAwD,EACzD;;AAED;EACE,4CAAmE,EACpE;;AAED;EACE,iCAAwD,EACzD;;AAED;EACE,4CAAmE,EACpE;;AAED;EACE,mCAAyD,EAC1D;;AAED;EACE,8CAAoE,EACrE;;AAED;EACE,kCAAyD,EAC1D;;AAED;EACE,6CAAoE,EACrE;;AAED;EACE,iCAAyD,EAC1D;;AAED;EACE,4CAAoE,EACrE;;AAED;EACE,iCAAyD,EAC1D;;AAED;EACE,4CAAoE,EACrE;;AAID;EACE,kCAAsD,EACvD;;AAED;EACE,6CAAiE,EAClE;;AAED;EACE,mCAAqD,EACtD;;AAED;EACE,8CAAgE,EACjE;;AAED;EACE,mCAAsD,EACvD;;AAED;EACE,8CAAiE,EAClE;;AAED;EACE,mCAAsD,EACvD;;AAED;EACE,8CAAiE,EAClE;;AAED;EACE,mCAAsD,EACvD;;AAED;EACE,8CAAiE,EAClE;;AAED;EACE,kCAAsD,EACvD;;AAED;EACE,6CAAiE,EAClE;;AAED;EACE,kCAAsD,EACvD;;AAED;EACE,6CAAiE,EAClE;;AAED;EACE,kCAAsD,EACvD;;AAED;EACE,6CAAiE,EAClE;;AAED;EACE,kCAAsD,EACvD;;AAED;EACE,6CAAiE,EAClE;;AAED;EACE,kCAAsD,EACvD;;AAED;EACE,6CAAiE,EAClE;;AAED;EACE,iCAAsD,EACvD;;AAED;EACE,4CAAiE,EAClE;;AAED;EACE,mCAAuD,EACxD;;AAED;EACE,8CAAkE,EACnE;;AAED;EACE,kCAAuD,EACxD;;AAED;EACE,6CAAkE,EACnE;;AAED;EACE,kCAAuD,EACxD;;AAED;EACE,6CAAkE,EACnE;;AAED;EACE,iCAAuD,EACxD;;AAED;EACE,4CAAkE,EACnE;;AAID;EACE,iCAA4D,EAC7D;;AAED;EACE,4CAAuE,EACxE;;AAED;EACE,mCAA2D,EAC5D;;AAED;EACE,8CAAsE,EACvE;;AAED;EACE,mCAA4D,EAC7D;;AAED;EACE,8CAAuE,EACxE;;AAED;EACE,mCAA4D,EAC7D;;AAED;EACE,8CAAuE,EACxE;;AAED;EACE,kCAA4D,EAC7D;;AAED;EACE,6CAAuE,EACxE;;AAED;EACE,kCAA4D,EAC7D;;AAED;EACE,6CAAuE,EACxE;;AAED;EACE,iCAA4D,EAC7D;;AAED;EACE,4CAAuE,EACxE;;AAED;EACE,iCAA4D,EAC7D;;AAED;EACE,4CAAuE,EACxE;;AAED;EACE,iCAA4D,EAC7D;;AAED;EACE,4CAAuE,EACxE;;AAED;EACE,iCAA4D,EAC7D;;AAED;EACE,4CAAuE,EACxE;;AAED;EACE,gCAA4D,EAC7D;;AAED;EACE,2CAAuE,EACxE;;AAED;EACE,mCAA6D,EAC9D;;AAED;EACE,8CAAwE,EACzE;;AAED;EACE,kCAA6D,EAC9D;;AAED;EACE,6CAAwE,EACzE;;AAED;EACE,iCAA6D,EAC9D;;AAED;EACE,4CAAwE,EACzE;;AAED;EACE,iCAA6D,EAC9D;;AAED;EACE,4CAAwE,EACzE;;AAID;EACE,iCAAsD,EACvD;;AAED;EACE,4CAAiE,EAClE;;AAED;EACE,mCAAqD,EACtD;;AAED;EACE,8CAAgE,EACjE;;AAED;EACE,mCAAsD,EACvD;;AAED;EACE,8CAAiE,EAClE;;AAED;EACE,mCAAsD,EACvD;;AAED;EACE,8CAAiE,EAClE;;AAED;EACE,kCAAsD,EACvD;;AAED;EACE,6CAAiE,EAClE;;AAED;EACE,kCAAsD,EACvD;;AAED;EACE,6CAAiE,EAClE;;AAED;EACE,iCAAsD,EACvD;;AAED;EACE,4CAAiE,EAClE;;AAED;EACE,iCAAsD,EACvD;;AAED;EACE,4CAAiE,EAClE;;AAED;EACE,iCAAsD,EACvD;;AAED;EACE,4CAAiE,EAClE;;AAED;EACE,iCAAsD,EACvD;;AAED;EACE,4CAAiE,EAClE;;AAED;EACE,gCAAsD,EACvD;;AAED;EACE,2CAAiE,EAClE;;AAED;EACE,mCAAuD,EACxD;;AAED;EACE,8CAAkE,EACnE;;AAED;EACE,kCAAuD,EACxD;;AAED;EACE,6CAAkE,EACnE;;AAED;EACE,iCAAuD,EACxD;;AAED;EACE,4CAAkE,EACnE;;AAED;EACE,iCAAuD,EACxD;;AAED;EACE,4CAAkE,EACnE;;AAID;EACE,iCAAsD,EACvD;;AAED;EACE,4CAAiE,EAClE;;AAED;EACE,mCAAqD,EACtD;;AAED;EACE,8CAAgE,EACjE;;AAED;EACE,mCAAsD,EACvD;;AAED;EACE,8CAAiE,EAClE;;AAED;EACE,mCAAsD,EACvD;;AAED;EACE,8CAAiE,EAClE;;AAED;EACE,kCAAsD,EACvD;;AAED;EACE,6CAAiE,EAClE;;AAED;EACE,kCAAsD,EACvD;;AAED;EACE,6CAAiE,EAClE;;AAED;EACE,iCAAsD,EACvD;;AAED;EACE,4CAAiE,EAClE;;AAED;EACE,iCAAsD,EACvD;;AAED;EACE,4CAAiE,EAClE;;AAED;EACE,iCAAsD,EACvD;;AAED;EACE,4CAAiE,EAClE;;AAED;EACE,gCAAsD,EACvD;;AAED;EACE,2CAAiE,EAClE;;AAED;EACE,+BAAsD,EACvD;;AAED;EACE,0CAAiE,EAClE;;AAED;EACE,mCAAuD,EACxD;;AAED;EACE,8CAAkE,EACnE;;AAED;EACE,mCAAuD,EACxD;;AAED;EACE,8CAAkE,EACnE;;AAED;EACE,kCAAuD,EACxD;;AAED;EACE,6CAAkE,EACnE;;AAED;EACE,iCAAuD,EACxD;;AAED;EACE,4CAAkE,EACnE;;AAID;EACE,iCAAuD,EACxD;;AAED;EACE,4CAAkE,EACnE;;AAED;EACE,mCAAsD,EACvD;;AAED;EACE,8CAAiE,EAClE;;AAED;EACE,mCAAuD,EACxD;;AAED;EACE,8CAAkE,EACnE;;AAED;EACE,mCAAuD,EACxD;;AAED;EACE,8CAAkE,EACnE;;AAED;EACE,mCAAuD,EACxD;;AAED;EACE,8CAAkE,EACnE;;AAED;EACE,mCAAuD,EACxD;;AAED;EACE,8CAAkE,EACnE;;AAED;EACE,iCAAuD,EACxD;;AAED;EACE,4CAAkE,EACnE;;AAED;EACE,iCAAuD,EACxD;;AAED;EACE,4CAAkE,EACnE;;AAED;EACE,iCAAuD,EACxD;;AAED;EACE,4CAAkE,EACnE;;AAED;EACE,iCAAuD,EACxD;;AAED;EACE,4CAAkE,EACnE;;AAED;EACE,gCAAuD,EACxD;;AAED;EACE,2CAAkE,EACnE;;AAED;EACE,mCAAwD,EACzD;;AAED;EACE,8CAAmE,EACpE;;AAED;EACE,mCAAwD,EACzD;;AAED;EACE,8CAAmE,EACpE;;AAED;EACE,iCAAwD,EACzD;;AAED;EACE,4CAAmE,EACpE;;AAED;EACE,gCAAwD,EACzD;;AAED;EACE,2CAAmE,EACpE;;AAID;EACE,kCAA6D,EAC9D;;AAED;EACE,6CAAwE,EACzE;;AAED;EACE,mCAA4D,EAC7D;;AAED;EACE,8CAAuE,EACxE;;AAED;EACE,mCAA6D,EAC9D;;AAED;EACE,8CAAwE,EACzE;;AAED;EACE,mCAA6D,EAC9D;;AAED;EACE,8CAAwE,EACzE;;AAED;EACE,mCAA6D,EAC9D;;AAED;EACE,8CAAwE,EACzE;;AAED;EACE,mCAA6D,EAC9D;;AAED;EACE,8CAAwE,EACzE;;AAED;EACE,kCAA6D,EAC9D;;AAED;EACE,6CAAwE,EACzE;;AAED;EACE,kCAA6D,EAC9D;;AAED;EACE,6CAAwE,EACzE;;AAED;EACE,kCAA6D,EAC9D;;AAED;EACE,6CAAwE,EACzE;;AAED;EACE,iCAA6D,EAC9D;;AAED;EACE,4CAAwE,EACzE;;AAED;EACE,iCAA6D,EAC9D;;AAED;EACE,4CAAwE,EACzE;;AAED;EACE,mCAA8D,EAC/D;;AAED;EACE,8CAAyE,EAC1E;;AAED;EACE,kCAA8D,EAC/D;;AAED;EACE,6CAAyE,EAC1E;;AAED;EACE,iCAA8D,EAC/D;;AAED;EACE,4CAAyE,EAC1E;;AAED;EACE,kCAA8D,EAC/D;;AAED;EACE,6CAAyE,EAC1E;;AAID;EACE,kCAAsD,EACvD;;AAED;EACE,6CAAiE,EAClE;;AAED;EACE,mCAAqD,EACtD;;AAED;EACE,8CAAgE,EACjE;;AAED;EACE,mCAAsD,EACvD;;AAED;EACE,8CAAiE,EAClE;;AAED;EACE,mCAAsD,EACvD;;AAED;EACE,8CAAiE,EAClE;;AAED;EACE,mCAAsD,EACvD;;AAED;EACE,8CAAiE,EAClE;;AAED;EACE,kCAAsD,EACvD;;AAED;EACE,6CAAiE,EAClE;;AAED;EACE,kCAAsD,EACvD;;AAED;EACE,6CAAiE,EAClE;;AAED;EACE,kCAAsD,EACvD;;AAED;EACE,6CAAiE,EAClE;;AAED;EACE,kCAAsD,EACvD;;AAED;EACE,6CAAiE,EAClE;;AAED;EACE,kCAAsD,EACvD;;AAED;EACE,6CAAiE,EAClE;;AAED;EACE,kCAAsD,EACvD;;AAED;EACE,6CAAiE,EAClE;;AAED;EACE,mCAAuD,EACxD;;AAED;EACE,8CAAkE,EACnE;;AAED;EACE,kCAAuD,EACxD;;AAED;EACE,6CAAkE,EACnE;;AAED;EACE,iCAAuD,EACxD;;AAED;EACE,4CAAkE,EACnE;;AAED;EACE,iCAAuD,EACxD;;AAED;EACE,4CAAkE,EACnE;;AAID;EACE,kCAAwD,EACzD;;AAED;EACE,6CAAmE,EACpE;;AAED;EACE,mCAAuD,EACxD;;AAED;EACE,8CAAkE,EACnE;;AAED;EACE,mCAAwD,EACzD;;AAED;EACE,8CAAmE,EACpE;;AAED;EACE,mCAAwD,EACzD;;AAED;EACE,8CAAmE,EACpE;;AAED;EACE,mCAAwD,EACzD;;AAED;EACE,8CAAmE,EACpE;;AAED;EACE,kCAAwD,EACzD;;AAED;EACE,6CAAmE,EACpE;;AAED;EACE,kCAAwD,EACzD;;AAED;EACE,6CAAmE,EACpE;;AAED;EACE,kCAAwD,EACzD;;AAED;EACE,6CAAmE,EACpE;;AAED;EACE,kCAAwD,EACzD;;AAED;EACE,6CAAmE,EACpE;;AAED;EACE,kCAAwD,EACzD;;AAED;EACE,6CAAmE,EACpE;;AAED;EACE,kCAAwD,EACzD;;AAED;EACE,6CAAmE,EACpE;;AAED;EACE,mCAAyD,EAC1D;;AAED;EACE,8CAAoE,EACrE;;AAED;EACE,iCAAyD,EAC1D;;AAED;EACE,4CAAoE,EACrE;;AAED;EACE,iCAAyD,EAC1D;;AAED;EACE,4CAAoE,EACrE;;AAED;EACE,iCAAyD,EAC1D;;AAED;EACE,4CAAoE,EACrE;;AAID;EACE,iCAAuD,EACxD;;AAED;EACE,4CAAkE,EACnE;;AAED;EACE,mCAAsD,EACvD;;AAED;EACE,8CAAiE,EAClE;;AAED;EACE,mCAAuD,EACxD;;AAED;EACE,8CAAkE,EACnE;;AAED;EACE,mCAAuD,EACxD;;AAED;EACE,8CAAkE,EACnE;;AAED;EACE,kCAAuD,EACxD;;AAED;EACE,6CAAkE,EACnE;;AAED;EACE,kCAAuD,EACxD;;AAED;EACE,6CAAkE,EACnE;;AAED;EACE,iCAAuD,EACxD;;AAED;EACE,4CAAkE,EACnE;;AAED;EACE,iCAAuD,EACxD;;AAED;EACE,4CAAkE,EACnE;;AAED;EACE,iCAAuD,EACxD;;AAED;EACE,4CAAkE,EACnE;;AAED;EACE,iCAAuD,EACxD;;AAED;EACE,4CAAkE,EACnE;;AAED;EACE,iCAAuD,EACxD;;AAED;EACE,4CAAkE,EACnE;;AAED;EACE,mCAAwD,EACzD;;AAED;EACE,8CAAmE,EACpE;;AAED;EACE,kCAAwD,EACzD;;AAED;EACE,6CAAmE,EACpE;;AAED;EACE,iCAAwD,EACzD;;AAED;EACE,4CAAmE,EACpE;;AAED;EACE,iCAAwD,EACzD;;AAED;EACE,4CAAmE,EACpE;;AAID;EACE,iCAAwD,EACzD;;AAED;EACE,4CAAmE,EACpE;;AAED;EACE,mCAAuD,EACxD;;AAED;EACE,8CAAkE,EACnE;;AAED;EACE,mCAAwD,EACzD;;AAED;EACE,8CAAmE,EACpE;;AAED;EACE,mCAAwD,EACzD;;AAED;EACE,8CAAmE,EACpE;;AAED;EACE,kCAAwD,EACzD;;AAED;EACE,6CAAmE,EACpE;;AAED;EACE,kCAAwD,EACzD;;AAED;EACE,6CAAmE,EACpE;;AAED;EACE,iCAAwD,EACzD;;AAED;EACE,4CAAmE,EACpE;;AAED;EACE,iCAAwD,EACzD;;AAED;EACE,4CAAmE,EACpE;;AAED;EACE,iCAAwD,EACzD;;AAED;EACE,4CAAmE,EACpE;;AAED;EACE,iCAAwD,EACzD;;AAED;EACE,4CAAmE,EACpE;;AAED;EACE,gCAAwD,EACzD;;AAED;EACE,2CAAmE,EACpE;;AAED;EACE,mCAAyD,EAC1D;;AAED;EACE,8CAAoE,EACrE;;AAED;EACE,kCAAyD,EAC1D;;AAED;EACE,6CAAoE,EACrE;;AAED;EACE,iCAAyD,EAC1D;;AAED;EACE,4CAAoE,EACrE;;AAED;EACE,iCAAyD,EAC1D;;AAED;EACE,4CAAoE,EACrE;;AAID;EACE,iCAA6D,EAC9D;;AAED;EACE,4CAAwE,EACzE;;AAED;EACE,mCAA4D,EAC7D;;AAED;EACE,8CAAuE,EACxE;;AAED;EACE,mCAA6D,EAC9D;;AAED;EACE,8CAAwE,EACzE;;AAED;EACE,mCAA6D,EAC9D;;AAED;EACE,8CAAwE,EACzE;;AAED;EACE,mCAA6D,EAC9D;;AAED;EACE,8CAAwE,EACzE;;AAED;EACE,kCAA6D,EAC9D;;AAED;EACE,6CAAwE,EACzE;;AAED;EACE,iCAA6D,EAC9D;;AAED;EACE,4CAAwE,EACzE;;AAED;EACE,iCAA6D,EAC9D;;AAED;EACE,4CAAwE,EACzE;;AAED;EACE,iCAA6D,EAC9D;;AAED;EACE,4CAAwE,EACzE;;AAED;EACE,iCAA6D,EAC9D;;AAED;EACE,4CAAwE,EACzE;;AAED;EACE,iCAA6D,EAC9D;;AAED;EACE,4CAAwE,EACzE;;AAED;EACE,mCAA8D,EAC/D;;AAED;EACE,8CAAyE,EAC1E;;AAED;EACE,kCAA8D,EAC/D;;AAED;EACE,6CAAyE,EAC1E;;AAED;EACE,gCAA8D,EAC/D;;AAED;EACE,2CAAyE,EAC1E;;AAED;EACE,gCAA8D,EAC/D;;AAED;EACE,2CAAyE,EAC1E;;AAID;EACE,iCAAuD,EACxD;;AAED;EACE,4CAAkE,EACnE;;AAED;EACE,mCAAsD,EACvD;;AAED;EACE,8CAAiE,EAClE;;AAED;EACE,mCAAuD,EACxD;;AAED;EACE,8CAAkE,EACnE;;AAED;EACE,mCAAuD,EACxD;;AAED;EACE,8CAAkE,EACnE;;AAED;EACE,mCAAuD,EACxD;;AAED;EACE,8CAAkE,EACnE;;AAED;EACE,kCAAuD,EACxD;;AAED;EACE,6CAAkE,EACnE;;AAED;EACE,iCAAuD,EACxD;;AAED;EACE,4CAAkE,EACnE;;AAED;EACE,iCAAuD,EACxD;;AAED;EACE,4CAAkE,EACnE;;AAED;EACE,gCAAuD,EACxD;;AAED;EACE,2CAAkE,EACnE;;AAED;EACE,gCAAuD,EACxD;;AAED;EACE,2CAAkE,EACnE;;AAED;EACE,gCAAuD,EACxD;;AAED;EACE,2CAAkE,EACnE;;AAID;EACE,mCAAsD,EACvD;;AAED;EACE,8CAAiE,EAClE;;AAED;EACE,mCAAqD,EACtD;;AAED;EACE,8CAAgE,EACjE;;AAED;EACE,mCAAsD,EACvD;;AAED;EACE,8CAAiE,EAClE;;AAED;EACE,mCAAsD,EACvD;;AAED;EACE,8CAAiE,EAClE;;AAED;EACE,mCAAsD,EACvD;;AAED;EACE,8CAAiE,EAClE;;AAED;EACE,mCAAsD,EACvD;;AAED;EACE,8CAAiE,EAClE;;AAED;EACE,mCAAsD,EACvD;;AAED;EACE,8CAAiE,EAClE;;AAED;EACE,mCAAsD,EACvD;;AAED;EACE,8CAAiE,EAClE;;AAED;EACE,gCAAsD,EACvD;;AAED;EACE,2CAAiE,EAClE;;AAED;EACE,gCAAsD,EACvD;;AAED;EACE,2CAAiE,EAClE;;AAED;EACE,gCAAsD,EACvD;;AAED;EACE,2CAAiE,EAClE;;AAID;EACE,kCAA2D,EAC5D;;AAED;EACE,6CAAsE,EACvE;;AAED;EACE,mCAA0D,EAC3D;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","file":"material.min.css","sourcesContent":["/**\n * material-design-lite - Material Design Components in CSS, JS and HTML\n * @version v1.0.6\n * @license Apache-2.0\n * @copyright 2015 Google, Inc.\n * @link https://github.com/google/material-design-lite\n */\n@charset \"UTF-8\";html{color:rgba(0,0,0,.87)}::-moz-selection{background:#b3d4fc;text-shadow:none}::selection{background:#b3d4fc;text-shadow:none}hr{display:block;height:1px;border:0;border-top:1px solid #ccc;margin:1em 0;padding:0}audio,canvas,iframe,img,svg,video{vertical-align:middle}fieldset{border:0;margin:0;padding:0}textarea{resize:vertical}.browserupgrade{margin:.2em 0;background:#ccc;color:#000;padding:.2em 0}.hidden{display:none!important}.visuallyhidden{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.visuallyhidden.focusable:active,.visuallyhidden.focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}.invisible{visibility:hidden}.clearfix:before,.clearfix:after{content:\" \";display:table}.clearfix:after{clear:both}@media print{*,*:before,*:after,*:first-letter,*:first-line{background:0 0!important;color:#000!important;box-shadow:none!important;text-shadow:none!important}a,a:visited{text-decoration:underline}a[href]:after{content:\" (\" attr(href)\")\"}abbr[title]:after{content:\" (\" attr(title)\")\"}a[href^=\"#\"]:after,a[href^=\"javascript:\"]:after{content:\"\"}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100%!important}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}}a,.mdl-accordion,.mdl-button,.mdl-card,.mdl-checkbox,.mdl-dropdown-menu,.mdl-icon-toggle,.mdl-item,.mdl-radio,.mdl-slider,.mdl-switch,.mdl-tabs__tab{-webkit-tap-highlight-color:transparent;-webkit-tap-highlight-color:rgba(255,255,255,0)}html{width:100%;height:100%;-ms-touch-action:manipulation;touch-action:manipulation}body{width:100%;min-height:100%;margin:0}main{display:block}*[hidden]{display:none!important}html,body{font-family:\"Helvetica\",\"Arial\",sans-serif;font-size:14px;font-weight:400;line-height:20px}h1,h2,h3,h4,h5,h6,p{padding:0}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small{font-family:\"Roboto\",\"Helvetica\",\"Arial\",sans-serif;font-weight:400;line-height:1.35;letter-spacing:-.02em;opacity:.54;font-size:.6em}h1{font-size:56px;line-height:1.35;letter-spacing:-.02em;margin:24px 0}h1,h2{font-family:\"Roboto\",\"Helvetica\",\"Arial\",sans-serif;font-weight:400}h2{font-size:45px;line-height:48px}h2,h3{margin:24px 0}h3{font-size:34px;line-height:40px}h3,h4{font-family:\"Roboto\",\"Helvetica\",\"Arial\",sans-serif;font-weight:400}h4{font-size:24px;line-height:32px;-moz-osx-font-smoothing:grayscale;margin:24px 0 16px}h5{font-size:20px;font-weight:500;line-height:1;letter-spacing:.02em}h5,h6{font-family:\"Roboto\",\"Helvetica\",\"Arial\",sans-serif;margin:24px 0 16px}h6{font-size:16px;letter-spacing:.04em}h6,p{font-weight:400;line-height:24px}p{font-size:14px;letter-spacing:0;margin:0 0 16px}a{color:#ff4081;font-weight:500}blockquote{font-family:\"Roboto\",\"Helvetica\",\"Arial\",sans-serif;position:relative;font-size:24px;font-weight:300;font-style:italic;line-height:1.35;letter-spacing:.08em}blockquote:before{position:absolute;left:-.5em;content:'“'}blockquote:after{content:'”';margin-left:-.05em}mark{background-color:#f4ff81}dt{font-weight:700}address{font-size:12px;line-height:1;font-style:normal}address,ul,ol{font-weight:400;letter-spacing:0}ul,ol{font-size:14px;line-height:24px}.mdl-typography--display-4,.mdl-typography--display-4-color-contrast{font-family:\"Roboto\",\"Helvetica\",\"Arial\",sans-serif;font-size:112px;font-weight:300;line-height:1;letter-spacing:-.04em}.mdl-typography--display-4-color-contrast{opacity:.54}.mdl-typography--display-3,.mdl-typography--display-3-color-contrast{font-family:\"Roboto\",\"Helvetica\",\"Arial\",sans-serif;font-size:56px;font-weight:400;line-height:1.35;letter-spacing:-.02em}.mdl-typography--display-3-color-contrast{opacity:.54}.mdl-typography--display-2,.mdl-typography--display-2-color-contrast{font-family:\"Roboto\",\"Helvetica\",\"Arial\",sans-serif;font-size:45px;font-weight:400;line-height:48px}.mdl-typography--display-2-color-contrast{opacity:.54}.mdl-typography--display-1,.mdl-typography--display-1-color-contrast{font-family:\"Roboto\",\"Helvetica\",\"Arial\",sans-serif;font-size:34px;font-weight:400;line-height:40px}.mdl-typography--display-1-color-contrast{opacity:.54}.mdl-typography--headline,.mdl-typography--headline-color-contrast{font-family:\"Roboto\",\"Helvetica\",\"Arial\",sans-serif;font-size:24px;font-weight:400;line-height:32px;-moz-osx-font-smoothing:grayscale}.mdl-typography--headline-color-contrast{opacity:.87}.mdl-typography--title,.mdl-typography--title-color-contrast{font-family:\"Roboto\",\"Helvetica\",\"Arial\",sans-serif;font-size:20px;font-weight:500;line-height:1;letter-spacing:.02em}.mdl-typography--title-color-contrast{opacity:.87}.mdl-typography--subhead,.mdl-typography--subhead-color-contrast{font-family:\"Roboto\",\"Helvetica\",\"Arial\",sans-serif;font-size:16px;font-weight:400;line-height:24px;letter-spacing:.04em}.mdl-typography--subhead-color-contrast{opacity:.87}.mdl-typography--body-2,.mdl-typography--body-2-color-contrast{font-size:14px;font-weight:700;line-height:24px;letter-spacing:0}.mdl-typography--body-2-color-contrast{opacity:.87}.mdl-typography--body-1,.mdl-typography--body-1-color-contrast{font-size:14px;font-weight:400;line-height:24px;letter-spacing:0}.mdl-typography--body-1-color-contrast{opacity:.87}.mdl-typography--body-2-force-preferred-font,.mdl-typography--body-2-force-preferred-font-color-contrast{font-family:\"Roboto\",\"Helvetica\",\"Arial\",sans-serif;font-size:14px;font-weight:500;line-height:24px;letter-spacing:0}.mdl-typography--body-2-force-preferred-font-color-contrast{opacity:.87}.mdl-typography--body-1-force-preferred-font,.mdl-typography--body-1-force-preferred-font-color-contrast{font-family:\"Roboto\",\"Helvetica\",\"Arial\",sans-serif;font-size:14px;font-weight:400;line-height:24px;letter-spacing:0}.mdl-typography--body-1-force-preferred-font-color-contrast{opacity:.87}.mdl-typography--caption,.mdl-typography--caption-force-preferred-font{font-size:12px;font-weight:400;line-height:1;letter-spacing:0}.mdl-typography--caption-force-preferred-font{font-family:\"Roboto\",\"Helvetica\",\"Arial\",sans-serif}.mdl-typography--caption-color-contrast,.mdl-typography--caption-force-preferred-font-color-contrast{font-size:12px;font-weight:400;line-height:1;letter-spacing:0;opacity:.54}.mdl-typography--caption-force-preferred-font-color-contrast,.mdl-typography--menu{font-family:\"Roboto\",\"Helvetica\",\"Arial\",sans-serif}.mdl-typography--menu{font-size:14px;font-weight:500;line-height:1;letter-spacing:0}.mdl-typography--menu-color-contrast{opacity:.87}.mdl-typography--menu-color-contrast,.mdl-typography--button,.mdl-typography--button-color-contrast{font-family:\"Roboto\",\"Helvetica\",\"Arial\",sans-serif;font-size:14px;font-weight:500;line-height:1;letter-spacing:0}.mdl-typography--button,.mdl-typography--button-color-contrast{text-transform:uppercase}.mdl-typography--button-color-contrast{opacity:.87}.mdl-typography--text-left{text-align:left}.mdl-typography--text-right{text-align:right}.mdl-typography--text-center{text-align:center}.mdl-typography--text-justify{text-align:justify}.mdl-typography--text-nowrap{white-space:nowrap}.mdl-typography--text-lowercase{text-transform:lowercase}.mdl-typography--text-uppercase{text-transform:uppercase}.mdl-typography--text-capitalize{text-transform:capitalize}.mdl-typography--font-thin{font-weight:200!important}.mdl-typography--font-light{font-weight:300!important}.mdl-typography--font-regular{font-weight:400!important}.mdl-typography--font-medium{font-weight:500!important}.mdl-typography--font-bold{font-weight:700!important}.mdl-typography--font-black{font-weight:900!important}.mdl-color-text--red{color:#f44336 !important}.mdl-color--red{background-color:#f44336 !important}.mdl-color-text--red-50{color:#ffebee !important}.mdl-color--red-50{background-color:#ffebee !important}.mdl-color-text--red-100{color:#ffcdd2 !important}.mdl-color--red-100{background-color:#ffcdd2 !important}.mdl-color-text--red-200{color:#ef9a9a !important}.mdl-color--red-200{background-color:#ef9a9a !important}.mdl-color-text--red-300{color:#e57373 !important}.mdl-color--red-300{background-color:#e57373 !important}.mdl-color-text--red-400{color:#ef5350 !important}.mdl-color--red-400{background-color:#ef5350 !important}.mdl-color-text--red-500{color:#f44336 !important}.mdl-color--red-500{background-color:#f44336 !important}.mdl-color-text--red-600{color:#e53935 !important}.mdl-color--red-600{background-color:#e53935 !important}.mdl-color-text--red-700{color:#d32f2f !important}.mdl-color--red-700{background-color:#d32f2f !important}.mdl-color-text--red-800{color:#c62828 !important}.mdl-color--red-800{background-color:#c62828 !important}.mdl-color-text--red-900{color:#b71c1c !important}.mdl-color--red-900{background-color:#b71c1c !important}.mdl-color-text--red-A100{color:#ff8a80 !important}.mdl-color--red-A100{background-color:#ff8a80 !important}.mdl-color-text--red-A200{color:#ff5252 !important}.mdl-color--red-A200{background-color:#ff5252 !important}.mdl-color-text--red-A400{color:#ff1744 !important}.mdl-color--red-A400{background-color:#ff1744 !important}.mdl-color-text--red-A700{color:#d50000 !important}.mdl-color--red-A700{background-color:#d50000 !important}.mdl-color-text--pink{color:#e91e63 !important}.mdl-color--pink{background-color:#e91e63 !important}.mdl-color-text--pink-50{color:#fce4ec !important}.mdl-color--pink-50{background-color:#fce4ec !important}.mdl-color-text--pink-100{color:#f8bbd0 !important}.mdl-color--pink-100{background-color:#f8bbd0 !important}.mdl-color-text--pink-200{color:#f48fb1 !important}.mdl-color--pink-200{background-color:#f48fb1 !important}.mdl-color-text--pink-300{color:#f06292 !important}.mdl-color--pink-300{background-color:#f06292 !important}.mdl-color-text--pink-400{color:#ec407a !important}.mdl-color--pink-400{background-color:#ec407a !important}.mdl-color-text--pink-500{color:#e91e63 !important}.mdl-color--pink-500{background-color:#e91e63 !important}.mdl-color-text--pink-600{color:#d81b60 !important}.mdl-color--pink-600{background-color:#d81b60 !important}.mdl-color-text--pink-700{color:#c2185b !important}.mdl-color--pink-700{background-color:#c2185b !important}.mdl-color-text--pink-800{color:#ad1457 !important}.mdl-color--pink-800{background-color:#ad1457 !important}.mdl-color-text--pink-900{color:#880e4f !important}.mdl-color--pink-900{background-color:#880e4f !important}.mdl-color-text--pink-A100{color:#ff80ab !important}.mdl-color--pink-A100{background-color:#ff80ab !important}.mdl-color-text--pink-A200{color:#ff4081 !important}.mdl-color--pink-A200{background-color:#ff4081 !important}.mdl-color-text--pink-A400{color:#f50057 !important}.mdl-color--pink-A400{background-color:#f50057 !important}.mdl-color-text--pink-A700{color:#c51162 !important}.mdl-color--pink-A700{background-color:#c51162 !important}.mdl-color-text--purple{color:#9c27b0 !important}.mdl-color--purple{background-color:#9c27b0 !important}.mdl-color-text--purple-50{color:#f3e5f5 !important}.mdl-color--purple-50{background-color:#f3e5f5 !important}.mdl-color-text--purple-100{color:#e1bee7 !important}.mdl-color--purple-100{background-color:#e1bee7 !important}.mdl-color-text--purple-200{color:#ce93d8 !important}.mdl-color--purple-200{background-color:#ce93d8 !important}.mdl-color-text--purple-300{color:#ba68c8 !important}.mdl-color--purple-300{background-color:#ba68c8 !important}.mdl-color-text--purple-400{color:#ab47bc !important}.mdl-color--purple-400{background-color:#ab47bc !important}.mdl-color-text--purple-500{color:#9c27b0 !important}.mdl-color--purple-500{background-color:#9c27b0 !important}.mdl-color-text--purple-600{color:#8e24aa !important}.mdl-color--purple-600{background-color:#8e24aa !important}.mdl-color-text--purple-700{color:#7b1fa2 !important}.mdl-color--purple-700{background-color:#7b1fa2 !important}.mdl-color-text--purple-800{color:#6a1b9a !important}.mdl-color--purple-800{background-color:#6a1b9a !important}.mdl-color-text--purple-900{color:#4a148c !important}.mdl-color--purple-900{background-color:#4a148c !important}.mdl-color-text--purple-A100{color:#ea80fc !important}.mdl-color--purple-A100{background-color:#ea80fc !important}.mdl-color-text--purple-A200{color:#e040fb !important}.mdl-color--purple-A200{background-color:#e040fb !important}.mdl-color-text--purple-A400{color:#d500f9 !important}.mdl-color--purple-A400{background-color:#d500f9 !important}.mdl-color-text--purple-A700{color:#a0f !important}.mdl-color--purple-A700{background-color:#a0f !important}.mdl-color-text--deep-purple{color:#673ab7 !important}.mdl-color--deep-purple{background-color:#673ab7 !important}.mdl-color-text--deep-purple-50{color:#ede7f6 !important}.mdl-color--deep-purple-50{background-color:#ede7f6 !important}.mdl-color-text--deep-purple-100{color:#d1c4e9 !important}.mdl-color--deep-purple-100{background-color:#d1c4e9 !important}.mdl-color-text--deep-purple-200{color:#b39ddb !important}.mdl-color--deep-purple-200{background-color:#b39ddb !important}.mdl-color-text--deep-purple-300{color:#9575cd !important}.mdl-color--deep-purple-300{background-color:#9575cd !important}.mdl-color-text--deep-purple-400{color:#7e57c2 !important}.mdl-color--deep-purple-400{background-color:#7e57c2 !important}.mdl-color-text--deep-purple-500{color:#673ab7 !important}.mdl-color--deep-purple-500{background-color:#673ab7 !important}.mdl-color-text--deep-purple-600{color:#5e35b1 !important}.mdl-color--deep-purple-600{background-color:#5e35b1 !important}.mdl-color-text--deep-purple-700{color:#512da8 !important}.mdl-color--deep-purple-700{background-color:#512da8 !important}.mdl-color-text--deep-purple-800{color:#4527a0 !important}.mdl-color--deep-purple-800{background-color:#4527a0 !important}.mdl-color-text--deep-purple-900{color:#311b92 !important}.mdl-color--deep-purple-900{background-color:#311b92 !important}.mdl-color-text--deep-purple-A100{color:#b388ff !important}.mdl-color--deep-purple-A100{background-color:#b388ff !important}.mdl-color-text--deep-purple-A200{color:#7c4dff !important}.mdl-color--deep-purple-A200{background-color:#7c4dff !important}.mdl-color-text--deep-purple-A400{color:#651fff !important}.mdl-color--deep-purple-A400{background-color:#651fff !important}.mdl-color-text--deep-purple-A700{color:#6200ea !important}.mdl-color--deep-purple-A700{background-color:#6200ea !important}.mdl-color-text--indigo{color:#3f51b5 !important}.mdl-color--indigo{background-color:#3f51b5 !important}.mdl-color-text--indigo-50{color:#e8eaf6 !important}.mdl-color--indigo-50{background-color:#e8eaf6 !important}.mdl-color-text--indigo-100{color:#c5cae9 !important}.mdl-color--indigo-100{background-color:#c5cae9 !important}.mdl-color-text--indigo-200{color:#9fa8da !important}.mdl-color--indigo-200{background-color:#9fa8da !important}.mdl-color-text--indigo-300{color:#7986cb !important}.mdl-color--indigo-300{background-color:#7986cb !important}.mdl-color-text--indigo-400{color:#5c6bc0 !important}.mdl-color--indigo-400{background-color:#5c6bc0 !important}.mdl-color-text--indigo-500{color:#3f51b5 !important}.mdl-color--indigo-500{background-color:#3f51b5 !important}.mdl-color-text--indigo-600{color:#3949ab !important}.mdl-color--indigo-600{background-color:#3949ab !important}.mdl-color-text--indigo-700{color:#303f9f !important}.mdl-color--indigo-700{background-color:#303f9f !important}.mdl-color-text--indigo-800{color:#283593 !important}.mdl-color--indigo-800{background-color:#283593 !important}.mdl-color-text--indigo-900{color:#1a237e !important}.mdl-color--indigo-900{background-color:#1a237e !important}.mdl-color-text--indigo-A100{color:#8c9eff !important}.mdl-color--indigo-A100{background-color:#8c9eff !important}.mdl-color-text--indigo-A200{color:#536dfe !important}.mdl-color--indigo-A200{background-color:#536dfe !important}.mdl-color-text--indigo-A400{color:#3d5afe !important}.mdl-color--indigo-A400{background-color:#3d5afe !important}.mdl-color-text--indigo-A700{color:#304ffe !important}.mdl-color--indigo-A700{background-color:#304ffe !important}.mdl-color-text--blue{color:#2196f3 !important}.mdl-color--blue{background-color:#2196f3 !important}.mdl-color-text--blue-50{color:#e3f2fd !important}.mdl-color--blue-50{background-color:#e3f2fd !important}.mdl-color-text--blue-100{color:#bbdefb !important}.mdl-color--blue-100{background-color:#bbdefb !important}.mdl-color-text--blue-200{color:#90caf9 !important}.mdl-color--blue-200{background-color:#90caf9 !important}.mdl-color-text--blue-300{color:#64b5f6 !important}.mdl-color--blue-300{background-color:#64b5f6 !important}.mdl-color-text--blue-400{color:#42a5f5 !important}.mdl-color--blue-400{background-color:#42a5f5 !important}.mdl-color-text--blue-500{color:#2196f3 !important}.mdl-color--blue-500{background-color:#2196f3 !important}.mdl-color-text--blue-600{color:#1e88e5 !important}.mdl-color--blue-600{background-color:#1e88e5 !important}.mdl-color-text--blue-700{color:#1976d2 !important}.mdl-color--blue-700{background-color:#1976d2 !important}.mdl-color-text--blue-800{color:#1565c0 !important}.mdl-color--blue-800{background-color:#1565c0 !important}.mdl-color-text--blue-900{color:#0d47a1 !important}.mdl-color--blue-900{background-color:#0d47a1 !important}.mdl-color-text--blue-A100{color:#82b1ff !important}.mdl-color--blue-A100{background-color:#82b1ff !important}.mdl-color-text--blue-A200{color:#448aff !important}.mdl-color--blue-A200{background-color:#448aff !important}.mdl-color-text--blue-A400{color:#2979ff !important}.mdl-color--blue-A400{background-color:#2979ff !important}.mdl-color-text--blue-A700{color:#2962ff !important}.mdl-color--blue-A700{background-color:#2962ff !important}.mdl-color-text--light-blue{color:#03a9f4 !important}.mdl-color--light-blue{background-color:#03a9f4 !important}.mdl-color-text--light-blue-50{color:#e1f5fe !important}.mdl-color--light-blue-50{background-color:#e1f5fe !important}.mdl-color-text--light-blue-100{color:#b3e5fc !important}.mdl-color--light-blue-100{background-color:#b3e5fc !important}.mdl-color-text--light-blue-200{color:#81d4fa !important}.mdl-color--light-blue-200{background-color:#81d4fa !important}.mdl-color-text--light-blue-300{color:#4fc3f7 !important}.mdl-color--light-blue-300{background-color:#4fc3f7 !important}.mdl-color-text--light-blue-400{color:#29b6f6 !important}.mdl-color--light-blue-400{background-color:#29b6f6 !important}.mdl-color-text--light-blue-500{color:#03a9f4 !important}.mdl-color--light-blue-500{background-color:#03a9f4 !important}.mdl-color-text--light-blue-600{color:#039be5 !important}.mdl-color--light-blue-600{background-color:#039be5 !important}.mdl-color-text--light-blue-700{color:#0288d1 !important}.mdl-color--light-blue-700{background-color:#0288d1 !important}.mdl-color-text--light-blue-800{color:#0277bd !important}.mdl-color--light-blue-800{background-color:#0277bd !important}.mdl-color-text--light-blue-900{color:#01579b !important}.mdl-color--light-blue-900{background-color:#01579b !important}.mdl-color-text--light-blue-A100{color:#80d8ff !important}.mdl-color--light-blue-A100{background-color:#80d8ff !important}.mdl-color-text--light-blue-A200{color:#40c4ff !important}.mdl-color--light-blue-A200{background-color:#40c4ff !important}.mdl-color-text--light-blue-A400{color:#00b0ff !important}.mdl-color--light-blue-A400{background-color:#00b0ff !important}.mdl-color-text--light-blue-A700{color:#0091ea !important}.mdl-color--light-blue-A700{background-color:#0091ea !important}.mdl-color-text--cyan{color:#00bcd4 !important}.mdl-color--cyan{background-color:#00bcd4 !important}.mdl-color-text--cyan-50{color:#e0f7fa !important}.mdl-color--cyan-50{background-color:#e0f7fa !important}.mdl-color-text--cyan-100{color:#b2ebf2 !important}.mdl-color--cyan-100{background-color:#b2ebf2 !important}.mdl-color-text--cyan-200{color:#80deea !important}.mdl-color--cyan-200{background-color:#80deea !important}.mdl-color-text--cyan-300{color:#4dd0e1 !important}.mdl-color--cyan-300{background-color:#4dd0e1 !important}.mdl-color-text--cyan-400{color:#26c6da !important}.mdl-color--cyan-400{background-color:#26c6da !important}.mdl-color-text--cyan-500{color:#00bcd4 !important}.mdl-color--cyan-500{background-color:#00bcd4 !important}.mdl-color-text--cyan-600{color:#00acc1 !important}.mdl-color--cyan-600{background-color:#00acc1 !important}.mdl-color-text--cyan-700{color:#0097a7 !important}.mdl-color--cyan-700{background-color:#0097a7 !important}.mdl-color-text--cyan-800{color:#00838f !important}.mdl-color--cyan-800{background-color:#00838f !important}.mdl-color-text--cyan-900{color:#006064 !important}.mdl-color--cyan-900{background-color:#006064 !important}.mdl-color-text--cyan-A100{color:#84ffff !important}.mdl-color--cyan-A100{background-color:#84ffff !important}.mdl-color-text--cyan-A200{color:#18ffff !important}.mdl-color--cyan-A200{background-color:#18ffff !important}.mdl-color-text--cyan-A400{color:#00e5ff !important}.mdl-color--cyan-A400{background-color:#00e5ff !important}.mdl-color-text--cyan-A700{color:#00b8d4 !important}.mdl-color--cyan-A700{background-color:#00b8d4 !important}.mdl-color-text--teal{color:#009688 !important}.mdl-color--teal{background-color:#009688 !important}.mdl-color-text--teal-50{color:#e0f2f1 !important}.mdl-color--teal-50{background-color:#e0f2f1 !important}.mdl-color-text--teal-100{color:#b2dfdb !important}.mdl-color--teal-100{background-color:#b2dfdb !important}.mdl-color-text--teal-200{color:#80cbc4 !important}.mdl-color--teal-200{background-color:#80cbc4 !important}.mdl-color-text--teal-300{color:#4db6ac !important}.mdl-color--teal-300{background-color:#4db6ac !important}.mdl-color-text--teal-400{color:#26a69a !important}.mdl-color--teal-400{background-color:#26a69a !important}.mdl-color-text--teal-500{color:#009688 !important}.mdl-color--teal-500{background-color:#009688 !important}.mdl-color-text--teal-600{color:#00897b !important}.mdl-color--teal-600{background-color:#00897b !important}.mdl-color-text--teal-700{color:#00796b !important}.mdl-color--teal-700{background-color:#00796b !important}.mdl-color-text--teal-800{color:#00695c !important}.mdl-color--teal-800{background-color:#00695c !important}.mdl-color-text--teal-900{color:#004d40 !important}.mdl-color--teal-900{background-color:#004d40 !important}.mdl-color-text--teal-A100{color:#a7ffeb !important}.mdl-color--teal-A100{background-color:#a7ffeb !important}.mdl-color-text--teal-A200{color:#64ffda !important}.mdl-color--teal-A200{background-color:#64ffda !important}.mdl-color-text--teal-A400{color:#1de9b6 !important}.mdl-color--teal-A400{background-color:#1de9b6 !important}.mdl-color-text--teal-A700{color:#00bfa5 !important}.mdl-color--teal-A700{background-color:#00bfa5 !important}.mdl-color-text--green{color:#4caf50 !important}.mdl-color--green{background-color:#4caf50 !important}.mdl-color-text--green-50{color:#e8f5e9 !important}.mdl-color--green-50{background-color:#e8f5e9 !important}.mdl-color-text--green-100{color:#c8e6c9 !important}.mdl-color--green-100{background-color:#c8e6c9 !important}.mdl-color-text--green-200{color:#a5d6a7 !important}.mdl-color--green-200{background-color:#a5d6a7 !important}.mdl-color-text--green-300{color:#81c784 !important}.mdl-color--green-300{background-color:#81c784 !important}.mdl-color-text--green-400{color:#66bb6a !important}.mdl-color--green-400{background-color:#66bb6a !important}.mdl-color-text--green-500{color:#4caf50 !important}.mdl-color--green-500{background-color:#4caf50 !important}.mdl-color-text--green-600{color:#43a047 !important}.mdl-color--green-600{background-color:#43a047 !important}.mdl-color-text--green-700{color:#388e3c !important}.mdl-color--green-700{background-color:#388e3c !important}.mdl-color-text--green-800{color:#2e7d32 !important}.mdl-color--green-800{background-color:#2e7d32 !important}.mdl-color-text--green-900{color:#1b5e20 !important}.mdl-color--green-900{background-color:#1b5e20 !important}.mdl-color-text--green-A100{color:#b9f6ca !important}.mdl-color--green-A100{background-color:#b9f6ca !important}.mdl-color-text--green-A200{color:#69f0ae !important}.mdl-color--green-A200{background-color:#69f0ae !important}.mdl-color-text--green-A400{color:#00e676 !important}.mdl-color--green-A400{background-color:#00e676 !important}.mdl-color-text--green-A700{color:#00c853 !important}.mdl-color--green-A700{background-color:#00c853 !important}.mdl-color-text--light-green{color:#8bc34a !important}.mdl-color--light-green{background-color:#8bc34a !important}.mdl-color-text--light-green-50{color:#f1f8e9 !important}.mdl-color--light-green-50{background-color:#f1f8e9 !important}.mdl-color-text--light-green-100{color:#dcedc8 !important}.mdl-color--light-green-100{background-color:#dcedc8 !important}.mdl-color-text--light-green-200{color:#c5e1a5 !important}.mdl-color--light-green-200{background-color:#c5e1a5 !important}.mdl-color-text--light-green-300{color:#aed581 !important}.mdl-color--light-green-300{background-color:#aed581 !important}.mdl-color-text--light-green-400{color:#9ccc65 !important}.mdl-color--light-green-400{background-color:#9ccc65 !important}.mdl-color-text--light-green-500{color:#8bc34a !important}.mdl-color--light-green-500{background-color:#8bc34a !important}.mdl-color-text--light-green-600{color:#7cb342 !important}.mdl-color--light-green-600{background-color:#7cb342 !important}.mdl-color-text--light-green-700{color:#689f38 !important}.mdl-color--light-green-700{background-color:#689f38 !important}.mdl-color-text--light-green-800{color:#558b2f !important}.mdl-color--light-green-800{background-color:#558b2f !important}.mdl-color-text--light-green-900{color:#33691e !important}.mdl-color--light-green-900{background-color:#33691e !important}.mdl-color-text--light-green-A100{color:#ccff90 !important}.mdl-color--light-green-A100{background-color:#ccff90 !important}.mdl-color-text--light-green-A200{color:#b2ff59 !important}.mdl-color--light-green-A200{background-color:#b2ff59 !important}.mdl-color-text--light-green-A400{color:#76ff03 !important}.mdl-color--light-green-A400{background-color:#76ff03 !important}.mdl-color-text--light-green-A700{color:#64dd17 !important}.mdl-color--light-green-A700{background-color:#64dd17 !important}.mdl-color-text--lime{color:#cddc39 !important}.mdl-color--lime{background-color:#cddc39 !important}.mdl-color-text--lime-50{color:#f9fbe7 !important}.mdl-color--lime-50{background-color:#f9fbe7 !important}.mdl-color-text--lime-100{color:#f0f4c3 !important}.mdl-color--lime-100{background-color:#f0f4c3 !important}.mdl-color-text--lime-200{color:#e6ee9c !important}.mdl-color--lime-200{background-color:#e6ee9c !important}.mdl-color-text--lime-300{color:#dce775 !important}.mdl-color--lime-300{background-color:#dce775 !important}.mdl-color-text--lime-400{color:#d4e157 !important}.mdl-color--lime-400{background-color:#d4e157 !important}.mdl-color-text--lime-500{color:#cddc39 !important}.mdl-color--lime-500{background-color:#cddc39 !important}.mdl-color-text--lime-600{color:#c0ca33 !important}.mdl-color--lime-600{background-color:#c0ca33 !important}.mdl-color-text--lime-700{color:#afb42b !important}.mdl-color--lime-700{background-color:#afb42b !important}.mdl-color-text--lime-800{color:#9e9d24 !important}.mdl-color--lime-800{background-color:#9e9d24 !important}.mdl-color-text--lime-900{color:#827717 !important}.mdl-color--lime-900{background-color:#827717 !important}.mdl-color-text--lime-A100{color:#f4ff81 !important}.mdl-color--lime-A100{background-color:#f4ff81 !important}.mdl-color-text--lime-A200{color:#eeff41 !important}.mdl-color--lime-A200{background-color:#eeff41 !important}.mdl-color-text--lime-A400{color:#c6ff00 !important}.mdl-color--lime-A400{background-color:#c6ff00 !important}.mdl-color-text--lime-A700{color:#aeea00 !important}.mdl-color--lime-A700{background-color:#aeea00 !important}.mdl-color-text--yellow{color:#ffeb3b !important}.mdl-color--yellow{background-color:#ffeb3b !important}.mdl-color-text--yellow-50{color:#fffde7 !important}.mdl-color--yellow-50{background-color:#fffde7 !important}.mdl-color-text--yellow-100{color:#fff9c4 !important}.mdl-color--yellow-100{background-color:#fff9c4 !important}.mdl-color-text--yellow-200{color:#fff59d !important}.mdl-color--yellow-200{background-color:#fff59d !important}.mdl-color-text--yellow-300{color:#fff176 !important}.mdl-color--yellow-300{background-color:#fff176 !important}.mdl-color-text--yellow-400{color:#ffee58 !important}.mdl-color--yellow-400{background-color:#ffee58 !important}.mdl-color-text--yellow-500{color:#ffeb3b !important}.mdl-color--yellow-500{background-color:#ffeb3b !important}.mdl-color-text--yellow-600{color:#fdd835 !important}.mdl-color--yellow-600{background-color:#fdd835 !important}.mdl-color-text--yellow-700{color:#fbc02d !important}.mdl-color--yellow-700{background-color:#fbc02d !important}.mdl-color-text--yellow-800{color:#f9a825 !important}.mdl-color--yellow-800{background-color:#f9a825 !important}.mdl-color-text--yellow-900{color:#f57f17 !important}.mdl-color--yellow-900{background-color:#f57f17 !important}.mdl-color-text--yellow-A100{color:#ffff8d !important}.mdl-color--yellow-A100{background-color:#ffff8d !important}.mdl-color-text--yellow-A200{color:#ff0 !important}.mdl-color--yellow-A200{background-color:#ff0 !important}.mdl-color-text--yellow-A400{color:#ffea00 !important}.mdl-color--yellow-A400{background-color:#ffea00 !important}.mdl-color-text--yellow-A700{color:#ffd600 !important}.mdl-color--yellow-A700{background-color:#ffd600 !important}.mdl-color-text--amber{color:#ffc107 !important}.mdl-color--amber{background-color:#ffc107 !important}.mdl-color-text--amber-50{color:#fff8e1 !important}.mdl-color--amber-50{background-color:#fff8e1 !important}.mdl-color-text--amber-100{color:#ffecb3 !important}.mdl-color--amber-100{background-color:#ffecb3 !important}.mdl-color-text--amber-200{color:#ffe082 !important}.mdl-color--amber-200{background-color:#ffe082 !important}.mdl-color-text--amber-300{color:#ffd54f !important}.mdl-color--amber-300{background-color:#ffd54f !important}.mdl-color-text--amber-400{color:#ffca28 !important}.mdl-color--amber-400{background-color:#ffca28 !important}.mdl-color-text--amber-500{color:#ffc107 !important}.mdl-color--amber-500{background-color:#ffc107 !important}.mdl-color-text--amber-600{color:#ffb300 !important}.mdl-color--amber-600{background-color:#ffb300 !important}.mdl-color-text--amber-700{color:#ffa000 !important}.mdl-color--amber-700{background-color:#ffa000 !important}.mdl-color-text--amber-800{color:#ff8f00 !important}.mdl-color--amber-800{background-color:#ff8f00 !important}.mdl-color-text--amber-900{color:#ff6f00 !important}.mdl-color--amber-900{background-color:#ff6f00 !important}.mdl-color-text--amber-A100{color:#ffe57f !important}.mdl-color--amber-A100{background-color:#ffe57f !important}.mdl-color-text--amber-A200{color:#ffd740 !important}.mdl-color--amber-A200{background-color:#ffd740 !important}.mdl-color-text--amber-A400{color:#ffc400 !important}.mdl-color--amber-A400{background-color:#ffc400 !important}.mdl-color-text--amber-A700{color:#ffab00 !important}.mdl-color--amber-A700{background-color:#ffab00 !important}.mdl-color-text--orange{color:#ff9800 !important}.mdl-color--orange{background-color:#ff9800 !important}.mdl-color-text--orange-50{color:#fff3e0 !important}.mdl-color--orange-50{background-color:#fff3e0 !important}.mdl-color-text--orange-100{color:#ffe0b2 !important}.mdl-color--orange-100{background-color:#ffe0b2 !important}.mdl-color-text--orange-200{color:#ffcc80 !important}.mdl-color--orange-200{background-color:#ffcc80 !important}.mdl-color-text--orange-300{color:#ffb74d !important}.mdl-color--orange-300{background-color:#ffb74d !important}.mdl-color-text--orange-400{color:#ffa726 !important}.mdl-color--orange-400{background-color:#ffa726 !important}.mdl-color-text--orange-500{color:#ff9800 !important}.mdl-color--orange-500{background-color:#ff9800 !important}.mdl-color-text--orange-600{color:#fb8c00 !important}.mdl-color--orange-600{background-color:#fb8c00 !important}.mdl-color-text--orange-700{color:#f57c00 !important}.mdl-color--orange-700{background-color:#f57c00 !important}.mdl-color-text--orange-800{color:#ef6c00 !important}.mdl-color--orange-800{background-color:#ef6c00 !important}.mdl-color-text--orange-900{color:#e65100 !important}.mdl-color--orange-900{background-color:#e65100 !important}.mdl-color-text--orange-A100{color:#ffd180 !important}.mdl-color--orange-A100{background-color:#ffd180 !important}.mdl-color-text--orange-A200{color:#ffab40 !important}.mdl-color--orange-A200{background-color:#ffab40 !important}.mdl-color-text--orange-A400{color:#ff9100 !important}.mdl-color--orange-A400{background-color:#ff9100 !important}.mdl-color-text--orange-A700{color:#ff6d00 !important}.mdl-color--orange-A700{background-color:#ff6d00 !important}.mdl-color-text--deep-orange{color:#ff5722 !important}.mdl-color--deep-orange{background-color:#ff5722 !important}.mdl-color-text--deep-orange-50{color:#fbe9e7 !important}.mdl-color--deep-orange-50{background-color:#fbe9e7 !important}.mdl-color-text--deep-orange-100{color:#ffccbc !important}.mdl-color--deep-orange-100{background-color:#ffccbc !important}.mdl-color-text--deep-orange-200{color:#ffab91 !important}.mdl-color--deep-orange-200{background-color:#ffab91 !important}.mdl-color-text--deep-orange-300{color:#ff8a65 !important}.mdl-color--deep-orange-300{background-color:#ff8a65 !important}.mdl-color-text--deep-orange-400{color:#ff7043 !important}.mdl-color--deep-orange-400{background-color:#ff7043 !important}.mdl-color-text--deep-orange-500{color:#ff5722 !important}.mdl-color--deep-orange-500{background-color:#ff5722 !important}.mdl-color-text--deep-orange-600{color:#f4511e !important}.mdl-color--deep-orange-600{background-color:#f4511e !important}.mdl-color-text--deep-orange-700{color:#e64a19 !important}.mdl-color--deep-orange-700{background-color:#e64a19 !important}.mdl-color-text--deep-orange-800{color:#d84315 !important}.mdl-color--deep-orange-800{background-color:#d84315 !important}.mdl-color-text--deep-orange-900{color:#bf360c !important}.mdl-color--deep-orange-900{background-color:#bf360c !important}.mdl-color-text--deep-orange-A100{color:#ff9e80 !important}.mdl-color--deep-orange-A100{background-color:#ff9e80 !important}.mdl-color-text--deep-orange-A200{color:#ff6e40 !important}.mdl-color--deep-orange-A200{background-color:#ff6e40 !important}.mdl-color-text--deep-orange-A400{color:#ff3d00 !important}.mdl-color--deep-orange-A400{background-color:#ff3d00 !important}.mdl-color-text--deep-orange-A700{color:#dd2c00 !important}.mdl-color--deep-orange-A700{background-color:#dd2c00 !important}.mdl-color-text--brown{color:#795548 !important}.mdl-color--brown{background-color:#795548 !important}.mdl-color-text--brown-50{color:#efebe9 !important}.mdl-color--brown-50{background-color:#efebe9 !important}.mdl-color-text--brown-100{color:#d7ccc8 !important}.mdl-color--brown-100{background-color:#d7ccc8 !important}.mdl-color-text--brown-200{color:#bcaaa4 !important}.mdl-color--brown-200{background-color:#bcaaa4 !important}.mdl-color-text--brown-300{color:#a1887f !important}.mdl-color--brown-300{background-color:#a1887f !important}.mdl-color-text--brown-400{color:#8d6e63 !important}.mdl-color--brown-400{background-color:#8d6e63 !important}.mdl-color-text--brown-500{color:#795548 !important}.mdl-color--brown-500{background-color:#795548 !important}.mdl-color-text--brown-600{color:#6d4c41 !important}.mdl-color--brown-600{background-color:#6d4c41 !important}.mdl-color-text--brown-700{color:#5d4037 !important}.mdl-color--brown-700{background-color:#5d4037 !important}.mdl-color-text--brown-800{color:#4e342e !important}.mdl-color--brown-800{background-color:#4e342e !important}.mdl-color-text--brown-900{color:#3e2723 !important}.mdl-color--brown-900{background-color:#3e2723 !important}.mdl-color-text--grey{color:#9e9e9e !important}.mdl-color--grey{background-color:#9e9e9e !important}.mdl-color-text--grey-50{color:#fafafa !important}.mdl-color--grey-50{background-color:#fafafa !important}.mdl-color-text--grey-100{color:#f5f5f5 !important}.mdl-color--grey-100{background-color:#f5f5f5 !important}.mdl-color-text--grey-200{color:#eee !important}.mdl-color--grey-200{background-color:#eee !important}.mdl-color-text--grey-300{color:#e0e0e0 !important}.mdl-color--grey-300{background-color:#e0e0e0 !important}.mdl-color-text--grey-400{color:#bdbdbd !important}.mdl-color--grey-400{background-color:#bdbdbd !important}.mdl-color-text--grey-500{color:#9e9e9e !important}.mdl-color--grey-500{background-color:#9e9e9e !important}.mdl-color-text--grey-600{color:#757575 !important}.mdl-color--grey-600{background-color:#757575 !important}.mdl-color-text--grey-700{color:#616161 !important}.mdl-color--grey-700{background-color:#616161 !important}.mdl-color-text--grey-800{color:#424242 !important}.mdl-color--grey-800{background-color:#424242 !important}.mdl-color-text--grey-900{color:#212121 !important}.mdl-color--grey-900{background-color:#212121 !important}.mdl-color-text--blue-grey{color:#607d8b !important}.mdl-color--blue-grey{background-color:#607d8b !important}.mdl-color-text--blue-grey-50{color:#eceff1 !important}.mdl-color--blue-grey-50{background-color:#eceff1 !important}.mdl-color-text--blue-grey-100{color:#cfd8dc !important}.mdl-color--blue-grey-100{background-color:#cfd8dc !important}.mdl-color-text--blue-grey-200{color:#b0bec5 !important}.mdl-color--blue-grey-200{background-color:#b0bec5 !important}.mdl-color-text--blue-grey-300{color:#90a4ae !important}.mdl-color--blue-grey-300{background-color:#90a4ae !important}.mdl-color-text--blue-grey-400{color:#78909c !important}.mdl-color--blue-grey-400{background-color:#78909c !important}.mdl-color-text--blue-grey-500{color:#607d8b !important}.mdl-color--blue-grey-500{background-color:#607d8b !important}.mdl-color-text--blue-grey-600{color:#546e7a !important}.mdl-color--blue-grey-600{background-color:#546e7a !important}.mdl-color-text--blue-grey-700{color:#455a64 !important}.mdl-color--blue-grey-700{background-color:#455a64 !important}.mdl-color-text--blue-grey-800{color:#37474f !important}.mdl-color--blue-grey-800{background-color:#37474f !important}.mdl-color-text--blue-grey-900{color:#263238 !important}.mdl-color--blue-grey-900{background-color:#263238 !important}.mdl-color--black{background-color:#000 !important}.mdl-color-text--black{color:#000 !important}.mdl-color--white{background-color:#fff !important}.mdl-color-text--white{color:#fff !important}.mdl-color--primary{background-color:#3f51b5 !important}.mdl-color--primary-contrast{background-color:#fff !important}.mdl-color--primary-dark{background-color:#303f9f !important}.mdl-color--accent{background-color:#ff4081 !important}.mdl-color--accent-contrast{background-color:#fff !important}.mdl-color-text--primary{color:#3f51b5 !important}.mdl-color-text--primary-contrast{color:#fff !important}.mdl-color-text--primary-dark{color:#303f9f !important}.mdl-color-text--accent{color:#ff4081 !important}.mdl-color-text--accent-contrast{color:#fff !important}.mdl-ripple{background:#000;border-radius:50%;height:50px;left:0;opacity:0;pointer-events:none;position:absolute;top:0;-webkit-transform:translate(-50%,-50%);-ms-transform:translate(-50%,-50%);transform:translate(-50%,-50%);width:50px;overflow:hidden}.mdl-ripple.is-animating{-webkit-transition:-webkit-transform .3s cubic-bezier(0,0,.2,1),width .3s cubic-bezier(0,0,.2,1),height .3s cubic-bezier(0,0,.2,1),opacity .6s cubic-bezier(0,0,.2,1);transition:transform .3s cubic-bezier(0,0,.2,1),width .3s cubic-bezier(0,0,.2,1),height .3s cubic-bezier(0,0,.2,1),opacity .6s cubic-bezier(0,0,.2,1)}.mdl-ripple.is-visible{opacity:.3}.mdl-animation--default,.mdl-animation--fast-out-slow-in{-webkit-transition-timing-function:cubic-bezier(.4,0,.2,1);transition-timing-function:cubic-bezier(.4,0,.2,1)}.mdl-animation--linear-out-slow-in{-webkit-transition-timing-function:cubic-bezier(0,0,.2,1);transition-timing-function:cubic-bezier(0,0,.2,1)}.mdl-animation--fast-out-linear-in{-webkit-transition-timing-function:cubic-bezier(.4,0,1,1);transition-timing-function:cubic-bezier(.4,0,1,1)}.mdl-badge{position:relative;white-space:nowrap;margin-right:24px}.mdl-badge:not([data-badge]){margin-right:auto}.mdl-badge[data-badge]:after{content:attr(data-badge);display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row;-webkit-flex-wrap:wrap;-ms-flex-wrap:wrap;flex-wrap:wrap;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;-webkit-align-content:center;-ms-flex-line-pack:center;align-content:center;-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;position:absolute;top:-11px;right:-24px;font-family:\"Roboto\",\"Helvetica\",\"Arial\",sans-serif;font-weight:600;font-size:12px;width:22px;height:22px;border-radius:50%;background:#ff4081;color:#fff}.mdl-button .mdl-badge[data-badge]:after{top:-10px;right:-5px}.mdl-badge.mdl-badge--no-background[data-badge]:after{color:#ff4081;background:#fff;box-shadow:0 0 1px gray}.mdl-button{background:0 0;border:none;border-radius:2px;color:#000;position:relative;height:36px;min-width:64px;padding:0 16px;display:inline-block;font-family:\"Roboto\",\"Helvetica\",\"Arial\",sans-serif;font-size:14px;font-weight:500;text-transform:uppercase;letter-spacing:0;overflow:hidden;will-change:box-shadow,transform;-webkit-transition:box-shadow .2s cubic-bezier(.4,0,1,1),background-color .2s cubic-bezier(.4,0,.2,1),color .2s cubic-bezier(.4,0,.2,1);transition:box-shadow .2s cubic-bezier(.4,0,1,1),background-color .2s cubic-bezier(.4,0,.2,1),color .2s cubic-bezier(.4,0,.2,1);outline:none;cursor:pointer;text-decoration:none;text-align:center;line-height:36px;vertical-align:middle}.mdl-button::-moz-focus-inner{border:0}.mdl-button:hover{background-color:rgba(158,158,158,.2)}.mdl-button:focus:not(:active){background-color:rgba(0,0,0,.12)}.mdl-button:active{background-color:rgba(158,158,158,.4)}.mdl-button.mdl-button--colored{color:#3f51b5}.mdl-button.mdl-button--colored:focus:not(:active){background-color:rgba(0,0,0,.12)}input.mdl-button[type=\"submit\"]{-webkit-appearance:none}.mdl-button--raised{background:rgba(158,158,158,.2);box-shadow:0 2px 2px 0 rgba(0,0,0,.14),0 3px 1px -2px rgba(0,0,0,.2),0 1px 5px 0 rgba(0,0,0,.12)}.mdl-button--raised:active{box-shadow:0 4px 5px 0 rgba(0,0,0,.14),0 1px 10px 0 rgba(0,0,0,.12),0 2px 4px -1px rgba(0,0,0,.2);background-color:rgba(158,158,158,.4)}.mdl-button--raised:focus:not(:active){box-shadow:0 0 8px rgba(0,0,0,.18),0 8px 16px rgba(0,0,0,.36);background-color:rgba(158,158,158,.4)}.mdl-button--raised.mdl-button--colored{background:#3f51b5;color:#fff}.mdl-button--raised.mdl-button--colored:hover{background-color:#3f51b5}.mdl-button--raised.mdl-button--colored:active{background-color:#3f51b5}.mdl-button--raised.mdl-button--colored:focus:not(:active){background-color:#3f51b5}.mdl-button--raised.mdl-button--colored .mdl-ripple{background:#fff}.mdl-button--fab{border-radius:50%;font-size:24px;height:56px;margin:auto;min-width:56px;width:56px;padding:0;overflow:hidden;background:rgba(158,158,158,.2);box-shadow:0 1px 1.5px 0 rgba(0,0,0,.12),0 1px 1px 0 rgba(0,0,0,.24);position:relative;line-height:normal}.mdl-button--fab .material-icons{position:absolute;top:50%;left:50%;-webkit-transform:translate(-12px,-12px);-ms-transform:translate(-12px,-12px);transform:translate(-12px,-12px);line-height:24px;width:24px}.mdl-button--fab.mdl-button--mini-fab{height:40px;min-width:40px;width:40px}.mdl-button--fab .mdl-button__ripple-container{border-radius:50%;-webkit-mask-image:-webkit-radial-gradient(circle,#fff,#000)}.mdl-button--fab:active{box-shadow:0 4px 5px 0 rgba(0,0,0,.14),0 1px 10px 0 rgba(0,0,0,.12),0 2px 4px -1px rgba(0,0,0,.2);background-color:rgba(158,158,158,.4)}.mdl-button--fab:focus:not(:active){box-shadow:0 0 8px rgba(0,0,0,.18),0 8px 16px rgba(0,0,0,.36);background-color:rgba(158,158,158,.4)}.mdl-button--fab.mdl-button--colored{background:#ff4081;color:#fff}.mdl-button--fab.mdl-button--colored:hover{background-color:#ff4081}.mdl-button--fab.mdl-button--colored:focus:not(:active){background-color:#ff4081}.mdl-button--fab.mdl-button--colored:active{background-color:#ff4081}.mdl-button--fab.mdl-button--colored .mdl-ripple{background:#fff}.mdl-button--icon{border-radius:50%;font-size:24px;height:32px;margin-left:0;margin-right:0;min-width:32px;width:32px;padding:0;overflow:hidden;color:inherit;line-height:normal}.mdl-button--icon .material-icons{position:absolute;top:50%;left:50%;-webkit-transform:translate(-12px,-12px);-ms-transform:translate(-12px,-12px);transform:translate(-12px,-12px);line-height:24px;width:24px}.mdl-button--icon.mdl-button--mini-icon{height:24px;min-width:24px;width:24px}.mdl-button--icon.mdl-button--mini-icon .material-icons{top:0;left:0}.mdl-button--icon .mdl-button__ripple-container{border-radius:50%;-webkit-mask-image:-webkit-radial-gradient(circle,#fff,#000)}.mdl-button__ripple-container{display:block;height:100%;left:0;position:absolute;top:0;width:100%;z-index:0;overflow:hidden}.mdl-button[disabled] .mdl-button__ripple-container .mdl-ripple,.mdl-button.mdl-button--disabled .mdl-button__ripple-container .mdl-ripple{background-color:transparent}.mdl-button--primary.mdl-button--primary{color:#3f51b5}.mdl-button--primary.mdl-button--primary .mdl-ripple{background:#fff}.mdl-button--primary.mdl-button--primary.mdl-button--raised,.mdl-button--primary.mdl-button--primary.mdl-button--fab{color:#fff;background-color:#3f51b5}.mdl-button--accent.mdl-button--accent{color:#ff4081}.mdl-button--accent.mdl-button--accent .mdl-ripple{background:#fff}.mdl-button--accent.mdl-button--accent.mdl-button--raised,.mdl-button--accent.mdl-button--accent.mdl-button--fab{color:#fff;background-color:#ff4081}.mdl-button[disabled][disabled],.mdl-button.mdl-button--disabled.mdl-button--disabled{color:rgba(0,0,0,.26);cursor:default;background-color:transparent}.mdl-button--fab[disabled][disabled],.mdl-button--fab.mdl-button--disabled.mdl-button--disabled,.mdl-button--raised[disabled][disabled],.mdl-button--raised.mdl-button--disabled.mdl-button--disabled{background-color:rgba(0,0,0,.12);color:rgba(0,0,0,.26);box-shadow:0 2px 2px 0 rgba(0,0,0,.14),0 3px 1px -2px rgba(0,0,0,.2),0 1px 5px 0 rgba(0,0,0,.12)}.mdl-button--colored[disabled][disabled],.mdl-button--colored.mdl-button--disabled.mdl-button--disabled{color:rgba(0,0,0,.26)}.mdl-button .material-icons{vertical-align:middle}.mdl-card{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;font-size:16px;font-weight:400;min-height:200px;overflow:hidden;width:330px;z-index:1;position:relative;background:#fff;border-radius:2px;box-sizing:border-box}.mdl-card__media{background-color:#ff4081;background-repeat:repeat;background-position:50% 50%;background-size:cover;background-origin:padding-box;background-attachment:scroll;box-sizing:border-box}.mdl-card__title{-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;color:#000;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-pack:stretch;-webkit-justify-content:stretch;-ms-flex-pack:stretch;justify-content:stretch;line-height:normal;padding:16px;-webkit-perspective-origin:165px 56px;perspective-origin:165px 56px;-webkit-transform-origin:165px 56px;-ms-transform-origin:165px 56px;transform-origin:165px 56px;box-sizing:border-box}.mdl-card__title.mdl-card--border{border-bottom:1px solid rgba(0,0,0,.1)}.mdl-card__title-text{-webkit-align-self:flex-end;-ms-flex-item-align:end;align-self:flex-end;color:inherit;display:-webkit-flex;display:-ms-flexbox;display:flex;font-size:24px;font-weight:300;line-height:normal;overflow:hidden;-webkit-transform-origin:149px 48px;-ms-transform-origin:149px 48px;transform-origin:149px 48px;margin:0}.mdl-card__subtitle-text{font-size:14px;color:rgba(0,0,0,.54);margin:0}.mdl-card__supporting-text{color:rgba(0,0,0,.54);font-size:13px;line-height:18px;overflow:hidden;padding:16px;width:90%}.mdl-card__actions{font-size:16px;line-height:normal;width:100%;background-color:transparent;padding:8px;box-sizing:border-box}.mdl-card__actions.mdl-card--border{border-top:1px solid rgba(0,0,0,.1)}.mdl-card--expand{-webkit-box-flex:1;-webkit-flex-grow:1;-ms-flex-positive:1;flex-grow:1}.mdl-card__menu{position:absolute;right:16px;top:16px}.mdl-checkbox{position:relative;z-index:1;vertical-align:middle;display:inline-block;box-sizing:border-box;width:100%;height:24px;margin:0;padding:0}.mdl-checkbox.is-upgraded{padding-left:24px}.mdl-checkbox__input{line-height:24px}.mdl-checkbox.is-upgraded .mdl-checkbox__input{position:absolute;width:0;height:0;margin:0;padding:0;opacity:0;-ms-appearance:none;-moz-appearance:none;-webkit-appearance:none;appearance:none;border:none}.mdl-checkbox__box-outline{position:absolute;top:3px;left:0;display:inline-block;box-sizing:border-box;width:16px;height:16px;margin:0;cursor:pointer;overflow:hidden;border:2px solid rgba(0,0,0,.54);border-radius:2px;z-index:2}.mdl-checkbox.is-checked .mdl-checkbox__box-outline{border:2px solid #3f51b5}.mdl-checkbox.is-disabled .mdl-checkbox__box-outline{border:2px solid rgba(0,0,0,.26);cursor:auto}.mdl-checkbox__focus-helper{position:absolute;top:3px;left:0;display:inline-block;box-sizing:border-box;width:16px;height:16px;border-radius:50%;background-color:transparent}.mdl-checkbox.is-focused .mdl-checkbox__focus-helper{box-shadow:0 0 0 8px rgba(0,0,0,.1);background-color:rgba(0,0,0,.1)}.mdl-checkbox.is-focused.is-checked .mdl-checkbox__focus-helper{box-shadow:0 0 0 8px rgba(63,81,181,.26);background-color:rgba(63,81,181,.26)}.mdl-checkbox__tick-outline{position:absolute;top:0;left:0;height:100%;width:100%;-webkit-mask:url(\"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcKICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICB4bWxuczpjYz0iaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbnMjIgogICB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiCiAgIHhtbG5zOnN2Zz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgdmVyc2lvbj0iMS4xIgogICB2aWV3Qm94PSIwIDAgMSAxIgogICBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWluWU1pbiBtZWV0Ij4KICA8ZGVmcz4KICAgIDxjbGlwUGF0aCBpZD0iY2xpcCI+CiAgICAgIDxwYXRoCiAgICAgICAgIGQ9Ik0gMCwwIDAsMSAxLDEgMSwwIDAsMCB6IE0gMC44NTM0Mzc1LDAuMTY3MTg3NSAwLjk1OTY4NzUsMC4yNzMxMjUgMC40MjkzNzUsMC44MDM0Mzc1IDAuMzIzMTI1LDAuOTA5Njg3NSAwLjIxNzE4NzUsMC44MDM0Mzc1IDAuMDQwMzEyNSwwLjYyNjg3NSAwLjE0NjU2MjUsMC41MjA2MjUgMC4zMjMxMjUsMC42OTc1IDAuODUzNDM3NSwwLjE2NzE4NzUgeiIKICAgICAgICAgc3R5bGU9ImZpbGw6I2ZmZmZmZjtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZSIgLz4KICAgIDwvY2xpcFBhdGg+CiAgICA8bWFzayBpZD0ibWFzayIgbWFza1VuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgbWFza0NvbnRlbnRVbml0cz0ib2JqZWN0Qm91bmRpbmdCb3giPgogICAgICA8cGF0aAogICAgICAgICBkPSJNIDAsMCAwLDEgMSwxIDEsMCAwLDAgeiBNIDAuODUzNDM3NSwwLjE2NzE4NzUgMC45NTk2ODc1LDAuMjczMTI1IDAuNDI5Mzc1LDAuODAzNDM3NSAwLjMyMzEyNSwwLjkwOTY4NzUgMC4yMTcxODc1LDAuODAzNDM3NSAwLjA0MDMxMjUsMC42MjY4NzUgMC4xNDY1NjI1LDAuNTIwNjI1IDAuMzIzMTI1LDAuNjk3NSAwLjg1MzQzNzUsMC4xNjcxODc1IHoiCiAgICAgICAgIHN0eWxlPSJmaWxsOiNmZmZmZmY7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmUiIC8+CiAgICA8L21hc2s+CiAgPC9kZWZzPgogIDxyZWN0CiAgICAgd2lkdGg9IjEiCiAgICAgaGVpZ2h0PSIxIgogICAgIHg9IjAiCiAgICAgeT0iMCIKICAgICBjbGlwLXBhdGg9InVybCgjY2xpcCkiCiAgICAgc3R5bGU9ImZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZSIgLz4KPC9zdmc+Cg==\");mask:url(\"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcKICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICB4bWxuczpjYz0iaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbnMjIgogICB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiCiAgIHhtbG5zOnN2Zz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgdmVyc2lvbj0iMS4xIgogICB2aWV3Qm94PSIwIDAgMSAxIgogICBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWluWU1pbiBtZWV0Ij4KICA8ZGVmcz4KICAgIDxjbGlwUGF0aCBpZD0iY2xpcCI+CiAgICAgIDxwYXRoCiAgICAgICAgIGQ9Ik0gMCwwIDAsMSAxLDEgMSwwIDAsMCB6IE0gMC44NTM0Mzc1LDAuMTY3MTg3NSAwLjk1OTY4NzUsMC4yNzMxMjUgMC40MjkzNzUsMC44MDM0Mzc1IDAuMzIzMTI1LDAuOTA5Njg3NSAwLjIxNzE4NzUsMC44MDM0Mzc1IDAuMDQwMzEyNSwwLjYyNjg3NSAwLjE0NjU2MjUsMC41MjA2MjUgMC4zMjMxMjUsMC42OTc1IDAuODUzNDM3NSwwLjE2NzE4NzUgeiIKICAgICAgICAgc3R5bGU9ImZpbGw6I2ZmZmZmZjtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZSIgLz4KICAgIDwvY2xpcFBhdGg+CiAgICA8bWFzayBpZD0ibWFzayIgbWFza1VuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgbWFza0NvbnRlbnRVbml0cz0ib2JqZWN0Qm91bmRpbmdCb3giPgogICAgICA8cGF0aAogICAgICAgICBkPSJNIDAsMCAwLDEgMSwxIDEsMCAwLDAgeiBNIDAuODUzNDM3NSwwLjE2NzE4NzUgMC45NTk2ODc1LDAuMjczMTI1IDAuNDI5Mzc1LDAuODAzNDM3NSAwLjMyMzEyNSwwLjkwOTY4NzUgMC4yMTcxODc1LDAuODAzNDM3NSAwLjA0MDMxMjUsMC42MjY4NzUgMC4xNDY1NjI1LDAuNTIwNjI1IDAuMzIzMTI1LDAuNjk3NSAwLjg1MzQzNzUsMC4xNjcxODc1IHoiCiAgICAgICAgIHN0eWxlPSJmaWxsOiNmZmZmZmY7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmUiIC8+CiAgICA8L21hc2s+CiAgPC9kZWZzPgogIDxyZWN0CiAgICAgd2lkdGg9IjEiCiAgICAgaGVpZ2h0PSIxIgogICAgIHg9IjAiCiAgICAgeT0iMCIKICAgICBjbGlwLXBhdGg9InVybCgjY2xpcCkiCiAgICAgc3R5bGU9ImZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZSIgLz4KPC9zdmc+Cg==\");background:0 0;-webkit-transition-duration:.28s;transition-duration:.28s;-webkit-transition-timing-function:cubic-bezier(.4,0,.2,1);transition-timing-function:cubic-bezier(.4,0,.2,1);-webkit-transition-property:background;transition-property:background}.mdl-checkbox.is-checked .mdl-checkbox__tick-outline{background:#3f51b5 url(\"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcKICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICB4bWxuczpjYz0iaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbnMjIgogICB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiCiAgIHhtbG5zOnN2Zz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgdmVyc2lvbj0iMS4xIgogICB2aWV3Qm94PSIwIDAgMSAxIgogICBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWluWU1pbiBtZWV0Ij4KICA8cGF0aAogICAgIGQ9Ik0gMC4wNDAzODA1OSwwLjYyNjc3NjcgMC4xNDY0NDY2MSwwLjUyMDcxMDY4IDAuNDI5Mjg5MzIsMC44MDM1NTMzOSAwLjMyMzIyMzMsMC45MDk2MTk0MSB6IE0gMC4yMTcxNTcyOSwwLjgwMzU1MzM5IDAuODUzNTUzMzksMC4xNjcxNTcyOSAwLjk1OTYxOTQxLDAuMjczMjIzMyAwLjMyMzIyMzMsMC45MDk2MTk0MSB6IgogICAgIGlkPSJyZWN0Mzc4MCIKICAgICBzdHlsZT0iZmlsbDojZmZmZmZmO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lIiAvPgo8L3N2Zz4K\")}.mdl-checkbox.is-checked.is-disabled .mdl-checkbox__tick-outline{background:rgba(0,0,0,.26)url(\"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcKICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICB4bWxuczpjYz0iaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbnMjIgogICB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiCiAgIHhtbG5zOnN2Zz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgdmVyc2lvbj0iMS4xIgogICB2aWV3Qm94PSIwIDAgMSAxIgogICBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWluWU1pbiBtZWV0Ij4KICA8cGF0aAogICAgIGQ9Ik0gMC4wNDAzODA1OSwwLjYyNjc3NjcgMC4xNDY0NDY2MSwwLjUyMDcxMDY4IDAuNDI5Mjg5MzIsMC44MDM1NTMzOSAwLjMyMzIyMzMsMC45MDk2MTk0MSB6IE0gMC4yMTcxNTcyOSwwLjgwMzU1MzM5IDAuODUzNTUzMzksMC4xNjcxNTcyOSAwLjk1OTYxOTQxLDAuMjczMjIzMyAwLjMyMzIyMzMsMC45MDk2MTk0MSB6IgogICAgIGlkPSJyZWN0Mzc4MCIKICAgICBzdHlsZT0iZmlsbDojZmZmZmZmO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lIiAvPgo8L3N2Zz4K\")}.mdl-checkbox__label{position:relative;cursor:pointer;font-size:16px;line-height:24px;margin:0}.mdl-checkbox.is-disabled .mdl-checkbox__label{color:rgba(0,0,0,.26);cursor:auto}.mdl-checkbox__ripple-container{position:absolute;z-index:2;top:-6px;left:-10px;box-sizing:border-box;width:36px;height:36px;border-radius:50%;cursor:pointer;overflow:hidden;-webkit-mask-image:-webkit-radial-gradient(circle,#fff,#000)}.mdl-checkbox__ripple-container .mdl-ripple{background:#3f51b5}.mdl-checkbox.is-disabled .mdl-checkbox__ripple-container{cursor:auto}.mdl-checkbox.is-disabled .mdl-checkbox__ripple-container .mdl-ripple{background:0 0}.mdl-data-table{position:relative;border:1px solid rgba(0,0,0,.12);border-collapse:collapse;white-space:nowrap;font-size:13px;background-color:#fff}.mdl-data-table thead{padding-bottom:3px}.mdl-data-table thead .mdl-data-table__select{margin-top:0}.mdl-data-table tbody tr{position:relative;height:48px;-webkit-transition-duration:.28s;transition-duration:.28s;-webkit-transition-timing-function:cubic-bezier(.4,0,.2,1);transition-timing-function:cubic-bezier(.4,0,.2,1);-webkit-transition-property:background-color;transition-property:background-color}.mdl-data-table tbody tr.is-selected{background-color:#e0e0e0}.mdl-data-table tbody tr:hover{background-color:#eee}.mdl-data-table td{text-align:right}.mdl-data-table th{padding:0 18px;text-align:right}.mdl-data-table td:first-of-type,.mdl-data-table th:first-of-type{padding-left:24px}.mdl-data-table td:last-of-type,.mdl-data-table th:last-of-type{padding-right:24px}.mdl-data-table td{position:relative;vertical-align:top;height:48px;border-top:1px solid rgba(0,0,0,.12);border-bottom:1px solid rgba(0,0,0,.12);padding:12px 18px 0;box-sizing:border-box}.mdl-data-table td .mdl-data-table__select{vertical-align:top;position:absolute;left:24px}.mdl-data-table th{position:relative;vertical-align:bottom;text-overflow:ellipsis;font-weight:700;line-height:24px;letter-spacing:0;height:48px;font-size:12px;color:rgba(0,0,0,.54);padding-bottom:8px;box-sizing:border-box}.mdl-data-table th .mdl-data-table__select{position:absolute;bottom:8px;left:24px}.mdl-data-table__select{width:16px}.mdl-data-table__cell--non-numeric.mdl-data-table__cell--non-numeric{text-align:left}.mdl-mega-footer{padding:16px 40px;color:#9e9e9e;background-color:#424242}.mdl-mega-footer--top-section:after,.mdl-mega-footer--middle-section:after,.mdl-mega-footer--bottom-section:after,.mdl-mega-footer__top-section:after,.mdl-mega-footer__middle-section:after,.mdl-mega-footer__bottom-section:after{content:'';display:block;clear:both}.mdl-mega-footer--left-section,.mdl-mega-footer__left-section,.mdl-mega-footer--right-section,.mdl-mega-footer__right-section{margin-bottom:16px}.mdl-mega-footer--right-section a,.mdl-mega-footer__right-section a{display:block;margin-bottom:16px;color:inherit;text-decoration:none}@media screen and (min-width:760px){.mdl-mega-footer--left-section,.mdl-mega-footer__left-section{float:left}.mdl-mega-footer--right-section,.mdl-mega-footer__right-section{float:right}.mdl-mega-footer--right-section a,.mdl-mega-footer__right-section a{display:inline-block;margin-left:16px;line-height:36px;vertical-align:middle}}.mdl-mega-footer--social-btn,.mdl-mega-footer__social-btn{width:36px;height:36px;padding:0;margin:0;background-color:#9e9e9e;border:none}.mdl-mega-footer--drop-down-section,.mdl-mega-footer__drop-down-section{display:block;position:relative}@media screen and (min-width:760px){.mdl-mega-footer--drop-down-section,.mdl-mega-footer__drop-down-section{width:33%}.mdl-mega-footer--drop-down-section:nth-child(1),.mdl-mega-footer--drop-down-section:nth-child(2),.mdl-mega-footer__drop-down-section:nth-child(1),.mdl-mega-footer__drop-down-section:nth-child(2){float:left}.mdl-mega-footer--drop-down-section:nth-child(3),.mdl-mega-footer__drop-down-section:nth-child(3){float:right}.mdl-mega-footer--drop-down-section:nth-child(3):after,.mdl-mega-footer__drop-down-section:nth-child(3):after{clear:right}.mdl-mega-footer--drop-down-section:nth-child(4),.mdl-mega-footer__drop-down-section:nth-child(4){clear:right;float:right}.mdl-mega-footer--middle-section:after,.mdl-mega-footer__middle-section:after{content:'';display:block;clear:both}.mdl-mega-footer--bottom-section,.mdl-mega-footer__bottom-section{padding-top:0}}@media screen and (min-width:1024px){.mdl-mega-footer--drop-down-section,.mdl-mega-footer--drop-down-section:nth-child(3),.mdl-mega-footer--drop-down-section:nth-child(4),.mdl-mega-footer__drop-down-section,.mdl-mega-footer__drop-down-section:nth-child(3),.mdl-mega-footer__drop-down-section:nth-child(4){width:24%;float:left}}.mdl-mega-footer--heading-checkbox,.mdl-mega-footer__heading-checkbox{position:absolute;width:100%;height:55.8px;padding:32px;margin:-16px 0 0;cursor:pointer;z-index:1;opacity:0}.mdl-mega-footer--heading-checkbox+.mdl-mega-footer--heading:after,.mdl-mega-footer--heading-checkbox+.mdl-mega-footer__heading:after,.mdl-mega-footer__heading-checkbox+.mdl-mega-footer--heading:after,.mdl-mega-footer__heading-checkbox+.mdl-mega-footer__heading:after{font-family:'Material Icons';content:'\\E5CE'}.mdl-mega-footer--heading-checkbox:checked~.mdl-mega-footer--link-list,.mdl-mega-footer--heading-checkbox:checked~.mdl-mega-footer__link-list,.mdl-mega-footer--heading-checkbox:checked+.mdl-mega-footer--heading+.mdl-mega-footer--link-list,.mdl-mega-footer--heading-checkbox:checked+.mdl-mega-footer__heading+.mdl-mega-footer__link-list,.mdl-mega-footer__heading-checkbox:checked~.mdl-mega-footer--link-list,.mdl-mega-footer__heading-checkbox:checked~.mdl-mega-footer__link-list,.mdl-mega-footer__heading-checkbox:checked+.mdl-mega-footer--heading+.mdl-mega-footer--link-list,.mdl-mega-footer__heading-checkbox:checked+.mdl-mega-footer__heading+.mdl-mega-footer__link-list{display:none}.mdl-mega-footer--heading-checkbox:checked+.mdl-mega-footer--heading:after,.mdl-mega-footer--heading-checkbox:checked+.mdl-mega-footer__heading:after,.mdl-mega-footer__heading-checkbox:checked+.mdl-mega-footer--heading:after,.mdl-mega-footer__heading-checkbox:checked+.mdl-mega-footer__heading:after{font-family:'Material Icons';content:'\\E5CF'}.mdl-mega-footer--heading,.mdl-mega-footer__heading{position:relative;width:100%;padding-right:39.8px;margin-bottom:16px;box-sizing:border-box;font-size:14px;line-height:23.8px;font-weight:500;white-space:nowrap;text-overflow:ellipsis;overflow:hidden;color:#e0e0e0}.mdl-mega-footer--heading:after,.mdl-mega-footer__heading:after{content:'';position:absolute;top:0;right:0;display:block;width:23.8px;height:23.8px;background-size:cover}.mdl-mega-footer--link-list,.mdl-mega-footer__link-list{list-style:none;padding:0;margin:0 0 32px}.mdl-mega-footer--link-list:after,.mdl-mega-footer__link-list:after{clear:both;display:block;content:''}.mdl-mega-footer--link-list li,.mdl-mega-footer__link-list li{font-size:14px;font-weight:400;letter-spacing:0;line-height:20px}.mdl-mega-footer--link-list a,.mdl-mega-footer__link-list a{color:inherit;text-decoration:none;white-space:nowrap}@media screen and (min-width:760px){.mdl-mega-footer--heading-checkbox,.mdl-mega-footer__heading-checkbox{display:none}.mdl-mega-footer--heading-checkbox+.mdl-mega-footer--heading:after,.mdl-mega-footer--heading-checkbox+.mdl-mega-footer__heading:after,.mdl-mega-footer__heading-checkbox+.mdl-mega-footer--heading:after,.mdl-mega-footer__heading-checkbox+.mdl-mega-footer__heading:after{background-image:none}.mdl-mega-footer--heading-checkbox:checked~.mdl-mega-footer--link-list,.mdl-mega-footer--heading-checkbox:checked~.mdl-mega-footer__link-list,.mdl-mega-footer--heading-checkbox:checked+.mdl-mega-footer__heading+.mdl-mega-footer__link-list,.mdl-mega-footer--heading-checkbox:checked+.mdl-mega-footer--heading+.mdl-mega-footer--link-list,.mdl-mega-footer__heading-checkbox:checked~.mdl-mega-footer--link-list,.mdl-mega-footer__heading-checkbox:checked~.mdl-mega-footer__link-list,.mdl-mega-footer__heading-checkbox:checked+.mdl-mega-footer__heading+.mdl-mega-footer__link-list,.mdl-mega-footer__heading-checkbox:checked+.mdl-mega-footer--heading+.mdl-mega-footer--link-list{display:block}.mdl-mega-footer--heading-checkbox:checked+.mdl-mega-footer--heading:after,.mdl-mega-footer--heading-checkbox:checked+.mdl-mega-footer__heading:after,.mdl-mega-footer__heading-checkbox:checked+.mdl-mega-footer--heading:after,.mdl-mega-footer__heading-checkbox:checked+.mdl-mega-footer__heading:after{content:''}}.mdl-mega-footer--bottom-section,.mdl-mega-footer__bottom-section{padding-top:16px;margin-bottom:16px}.mdl-logo{margin-bottom:16px;color:#fff}.mdl-mega-footer--bottom-section .mdl-mega-footer--link-list li,.mdl-mega-footer__bottom-section .mdl-mega-footer__link-list li{float:left;margin-bottom:0;margin-right:16px}@media screen and (min-width:760px){.mdl-logo{float:left;margin-bottom:0;margin-right:16px}}.mdl-mini-footer{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-flow:row wrap;-ms-flex-flow:row wrap;flex-flow:row wrap;-webkit-box-pack:justify;-webkit-justify-content:space-between;-ms-flex-pack:justify;justify-content:space-between;padding:32px 16px;color:#9e9e9e;background-color:#424242}.mdl-mini-footer:after{content:'';display:block}.mdl-mini-footer .mdl-logo{line-height:36px}.mdl-mini-footer--link-list,.mdl-mini-footer__link-list{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-flow:row nowrap;-ms-flex-flow:row nowrap;flex-flow:row nowrap;list-style:none;margin:0;padding:0}.mdl-mini-footer--link-list li,.mdl-mini-footer__link-list li{margin-bottom:0;margin-right:16px}@media screen and (min-width:760px){.mdl-mini-footer--link-list li,.mdl-mini-footer__link-list li{line-height:36px}}.mdl-mini-footer--link-list a,.mdl-mini-footer__link-list a{color:inherit;text-decoration:none;white-space:nowrap}.mdl-mini-footer--left-section,.mdl-mini-footer__left-section{display:inline-block;-webkit-box-ordinal-group:1;-webkit-order:0;-ms-flex-order:0;order:0}.mdl-mini-footer--right-section,.mdl-mini-footer__right-section{display:inline-block;-webkit-box-ordinal-group:2;-webkit-order:1;-ms-flex-order:1;order:1}.mdl-mini-footer--social-btn,.mdl-mini-footer__social-btn{width:36px;height:36px;padding:0;margin:0;background-color:#9e9e9e;border:none}.mdl-icon-toggle{position:relative;z-index:1;vertical-align:middle;display:inline-block;height:32px;margin:0;padding:0}.mdl-icon-toggle__input{line-height:32px}.mdl-icon-toggle.is-upgraded .mdl-icon-toggle__input{position:absolute;width:0;height:0;margin:0;padding:0;opacity:0;-ms-appearance:none;-moz-appearance:none;-webkit-appearance:none;appearance:none;border:none}.mdl-icon-toggle__label{display:inline-block;position:relative;cursor:pointer;height:32px;width:32px;min-width:32px;color:#616161;border-radius:50%;padding:0;margin-left:0;margin-right:0;text-align:center;background-color:transparent;will-change:background-color;-webkit-transition:background-color .2s cubic-bezier(.4,0,.2,1),color .2s cubic-bezier(.4,0,.2,1);transition:background-color .2s cubic-bezier(.4,0,.2,1),color .2s cubic-bezier(.4,0,.2,1)}.mdl-icon-toggle__label.material-icons{line-height:32px;font-size:24px}.mdl-icon-toggle.is-checked .mdl-icon-toggle__label{color:#3f51b5}.mdl-icon-toggle.is-disabled .mdl-icon-toggle__label{color:rgba(0,0,0,.26);cursor:auto;-webkit-transition:none;transition:none}.mdl-icon-toggle.is-focused .mdl-icon-toggle__label{background-color:rgba(0,0,0,.12)}.mdl-icon-toggle.is-focused.is-checked .mdl-icon-toggle__label{background-color:rgba(63,81,181,.26)}.mdl-icon-toggle__ripple-container{position:absolute;z-index:2;top:-2px;left:-2px;box-sizing:border-box;width:36px;height:36px;border-radius:50%;cursor:pointer;overflow:hidden;-webkit-mask-image:-webkit-radial-gradient(circle,#fff,#000)}.mdl-icon-toggle__ripple-container .mdl-ripple{background:#616161}.mdl-icon-toggle.is-disabled .mdl-icon-toggle__ripple-container{cursor:auto}.mdl-icon-toggle.is-disabled .mdl-icon-toggle__ripple-container .mdl-ripple{background:0 0}.mdl-menu__container{display:block;margin:0;padding:0;border:none;position:absolute;overflow:visible;height:0;width:0;visibility:hidden;z-index:-1}.mdl-menu__container.is-visible,.mdl-menu__container.is-animating{z-index:999;visibility:visible}.mdl-menu__outline{display:block;background:#fff;margin:0;padding:0;border:none;border-radius:2px;position:absolute;top:0;left:0;overflow:hidden;opacity:0;-webkit-transform:scale(0);-ms-transform:scale(0);transform:scale(0);-webkit-transform-origin:0 0;-ms-transform-origin:0 0;transform-origin:0 0;box-shadow:0 2px 2px 0 rgba(0,0,0,.14),0 3px 1px -2px rgba(0,0,0,.2),0 1px 5px 0 rgba(0,0,0,.12);will-change:transform;-webkit-transition:-webkit-transform .3s cubic-bezier(.4,0,.2,1),opacity .2s cubic-bezier(.4,0,.2,1);transition:transform .3s cubic-bezier(.4,0,.2,1),opacity .2s cubic-bezier(.4,0,.2,1);z-index:-1}.mdl-menu__container.is-visible .mdl-menu__outline{opacity:1;-webkit-transform:scale(1);-ms-transform:scale(1);transform:scale(1);z-index:999}.mdl-menu__outline.mdl-menu--bottom-right{-webkit-transform-origin:100% 0;-ms-transform-origin:100% 0;transform-origin:100% 0}.mdl-menu__outline.mdl-menu--top-left{-webkit-transform-origin:0 100%;-ms-transform-origin:0 100%;transform-origin:0 100%}.mdl-menu__outline.mdl-menu--top-right{-webkit-transform-origin:100% 100%;-ms-transform-origin:100% 100%;transform-origin:100% 100%}.mdl-menu{position:absolute;list-style:none;top:0;left:0;height:auto;width:auto;min-width:124px;padding:8px 0;margin:0;opacity:0;clip:rect(0 0 0 0);z-index:-1}.mdl-menu__container.is-visible .mdl-menu{opacity:1;z-index:999}.mdl-menu.is-animating{-webkit-transition:opacity .2s cubic-bezier(.4,0,.2,1),clip .3s cubic-bezier(.4,0,.2,1);transition:opacity .2s cubic-bezier(.4,0,.2,1),clip .3s cubic-bezier(.4,0,.2,1)}.mdl-menu.mdl-menu--bottom-right{left:auto;right:0}.mdl-menu.mdl-menu--top-left{top:auto;bottom:0}.mdl-menu.mdl-menu--top-right{top:auto;left:auto;bottom:0;right:0}.mdl-menu.mdl-menu--unaligned{top:auto;left:auto}.mdl-menu__item{display:block;border:none;color:rgba(0,0,0,.87);background-color:transparent;text-align:left;margin:0;padding:0 16px;outline-color:#bdbdbd;position:relative;overflow:hidden;font-size:14px;font-weight:400;letter-spacing:0;text-decoration:none;cursor:pointer;height:48px;line-height:48px;white-space:nowrap;opacity:0;-webkit-transition:opacity .2s cubic-bezier(.4,0,.2,1);transition:opacity .2s cubic-bezier(.4,0,.2,1);-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.mdl-menu__container.is-visible .mdl-menu__item{opacity:1}.mdl-menu__item::-moz-focus-inner{border:0}.mdl-menu__item[disabled]{color:#bdbdbd;background-color:transparent;cursor:auto}.mdl-menu__item[disabled]:hover{background-color:transparent}.mdl-menu__item[disabled]:focus{background-color:transparent}.mdl-menu__item[disabled] .mdl-ripple{background:0 0}.mdl-menu__item:hover{background-color:#eee}.mdl-menu__item:focus{outline:none;background-color:#eee}.mdl-menu__item:active{background-color:#e0e0e0}.mdl-menu__item--ripple-container{display:block;height:100%;left:0;position:absolute;top:0;width:100%;z-index:0;overflow:hidden}.mdl-progress{display:block;position:relative;height:4px;width:500px}.mdl-progress>.bar{display:block;position:absolute;top:0;bottom:0;width:0%;-webkit-transition:width .2s cubic-bezier(.4,0,.2,1);transition:width .2s cubic-bezier(.4,0,.2,1)}.mdl-progress>.progressbar{background-color:#3f51b5;z-index:1;left:0}.mdl-progress>.bufferbar{background-image:-webkit-linear-gradient(left,rgba(255,255,255,.7),rgba(255,255,255,.7)),-webkit-linear-gradient(left,#3f51b5 ,#3f51b5);background-image:linear-gradient(to right,rgba(255,255,255,.7),rgba(255,255,255,.7)),linear-gradient(to right,#3f51b5 ,#3f51b5);z-index:0;left:0}.mdl-progress>.auxbar{right:0}@supports (-webkit-appearance:none){.mdl-progress:not(.mdl-progress__indeterminate):not(.mdl-progress__indeterminate)>.auxbar{background-image:-webkit-linear-gradient(left,rgba(255,255,255,.7),rgba(255,255,255,.7)),-webkit-linear-gradient(left,#3f51b5 ,#3f51b5);background-image:linear-gradient(to right,rgba(255,255,255,.7),rgba(255,255,255,.7)),linear-gradient(to right,#3f51b5 ,#3f51b5);-webkit-mask:url(\"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIj8+Cjxzdmcgd2lkdGg9IjEyIiBoZWlnaHQ9IjQiIHZpZXdQb3J0PSIwIDAgMTIgNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgogIDxlbGxpcHNlIGN4PSIyIiBjeT0iMiIgcng9IjIiIHJ5PSIyIj4KICAgIDxhbmltYXRlIGF0dHJpYnV0ZU5hbWU9ImN4IiBmcm9tPSIyIiB0bz0iLTEwIiBkdXI9IjAuNnMiIHJlcGVhdENvdW50PSJpbmRlZmluaXRlIiAvPgogIDwvZWxsaXBzZT4KICA8ZWxsaXBzZSBjeD0iMTQiIGN5PSIyIiByeD0iMiIgcnk9IjIiIGNsYXNzPSJsb2FkZXIiPgogICAgPGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iY3giIGZyb209IjE0IiB0bz0iMiIgZHVyPSIwLjZzIiByZXBlYXRDb3VudD0iaW5kZWZpbml0ZSIgLz4KICA8L2VsbGlwc2U+Cjwvc3ZnPgo=\");mask:url(\"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIj8+Cjxzdmcgd2lkdGg9IjEyIiBoZWlnaHQ9IjQiIHZpZXdQb3J0PSIwIDAgMTIgNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgogIDxlbGxpcHNlIGN4PSIyIiBjeT0iMiIgcng9IjIiIHJ5PSIyIj4KICAgIDxhbmltYXRlIGF0dHJpYnV0ZU5hbWU9ImN4IiBmcm9tPSIyIiB0bz0iLTEwIiBkdXI9IjAuNnMiIHJlcGVhdENvdW50PSJpbmRlZmluaXRlIiAvPgogIDwvZWxsaXBzZT4KICA8ZWxsaXBzZSBjeD0iMTQiIGN5PSIyIiByeD0iMiIgcnk9IjIiIGNsYXNzPSJsb2FkZXIiPgogICAgPGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iY3giIGZyb209IjE0IiB0bz0iMiIgZHVyPSIwLjZzIiByZXBlYXRDb3VudD0iaW5kZWZpbml0ZSIgLz4KICA8L2VsbGlwc2U+Cjwvc3ZnPgo=\")}}.mdl-progress:not(.mdl-progress__indeterminate)>.auxbar{background-image:-webkit-linear-gradient(left,rgba(255,255,255,.9),rgba(255,255,255,.9)),-webkit-linear-gradient(left,#3f51b5 ,#3f51b5);background-image:linear-gradient(to right,rgba(255,255,255,.9),rgba(255,255,255,.9)),linear-gradient(to right,#3f51b5 ,#3f51b5)}.mdl-progress.mdl-progress__indeterminate>.bar1{-webkit-animation-name:indeterminate1;animation-name:indeterminate1}.mdl-progress.mdl-progress__indeterminate>.bar1,.mdl-progress.mdl-progress__indeterminate>.bar3{background-color:#3f51b5;-webkit-animation-duration:2s;animation-duration:2s;-webkit-animation-iteration-count:infinite;animation-iteration-count:infinite;-webkit-animation-timing-function:linear;animation-timing-function:linear}.mdl-progress.mdl-progress__indeterminate>.bar3{background-image:none;-webkit-animation-name:indeterminate2;animation-name:indeterminate2}@-webkit-keyframes indeterminate1{0%{left:0%;width:0%}50%{left:25%;width:75%}75%{left:100%;width:0%}}@keyframes indeterminate1{0%{left:0%;width:0%}50%{left:25%;width:75%}75%{left:100%;width:0%}}@-webkit-keyframes indeterminate2{0%,50%{left:0%;width:0%}75%{left:0%;width:25%}100%{left:100%;width:0%}}@keyframes indeterminate2{0%,50%{left:0%;width:0%}75%{left:0%;width:25%}100%{left:100%;width:0%}}.mdl-navigation{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-wrap:nowrap;-ms-flex-wrap:nowrap;flex-wrap:nowrap;box-sizing:border-box}.mdl-navigation__link{color:#424242;text-decoration:none;font-weight:500;font-size:13px;margin:0}.mdl-layout{width:100%;height:100%;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;overflow-y:auto;overflow-x:hidden;position:relative;-webkit-overflow-scrolling:touch}.mdl-layout.is-small-screen .mdl-layout--large-screen-only{display:none}.mdl-layout:not(.is-small-screen) .mdl-layout--small-screen-only{display:none}.mdl-layout__container{position:absolute;width:100%;height:100%}.mdl-layout__title,.mdl-layout-title{display:block;position:relative;font-family:\"Roboto\",\"Helvetica\",\"Arial\",sans-serif;font-size:20px;line-height:1;letter-spacing:.02em;font-weight:400;box-sizing:border-box}.mdl-layout-spacer{-webkit-box-flex:1;-webkit-flex-grow:1;-ms-flex-positive:1;flex-grow:1}.mdl-layout__drawer{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;-webkit-flex-wrap:nowrap;-ms-flex-wrap:nowrap;flex-wrap:nowrap;width:240px;height:100%;max-height:100%;position:absolute;top:0;left:0;box-shadow:0 2px 2px 0 rgba(0,0,0,.14),0 3px 1px -2px rgba(0,0,0,.2),0 1px 5px 0 rgba(0,0,0,.12);box-sizing:border-box;border-right:1px solid #e0e0e0;background:#fafafa;-webkit-transform:translateX(-250px);-ms-transform:translateX(-250px);transform:translateX(-250px);-webkit-transform-style:preserve-3d;transform-style:preserve-3d;will-change:transform;-webkit-transition-duration:.2s;transition-duration:.2s;-webkit-transition-timing-function:cubic-bezier(.4,0,.2,1);transition-timing-function:cubic-bezier(.4,0,.2,1);-webkit-transition-property:-webkit-transform;transition-property:transform;color:#424242;overflow:visible;overflow-y:auto;z-index:5}.mdl-layout__drawer.is-visible{-webkit-transform:translateX(0);-ms-transform:translateX(0);transform:translateX(0)}.mdl-layout__drawer.is-visible~.mdl-layout__content.mdl-layout__content{overflow:hidden}.mdl-layout__drawer>*{-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0}.mdl-layout__drawer>.mdl-layout__title,.mdl-layout__drawer>.mdl-layout-title{line-height:64px;padding-left:40px}@media screen and (max-width:1024px){.mdl-layout__drawer>.mdl-layout__title,.mdl-layout__drawer>.mdl-layout-title{line-height:56px;padding-left:16px}}.mdl-layout__drawer .mdl-navigation{-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;-webkit-box-align:stretch;-webkit-align-items:stretch;-ms-flex-align:stretch;align-items:stretch;padding-top:16px}.mdl-layout__drawer .mdl-navigation .mdl-navigation__link{display:block;-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0;padding:16px 40px;margin:0;color:#757575}@media screen and (max-width:1024px){.mdl-layout__drawer .mdl-navigation .mdl-navigation__link{padding:16px}}.mdl-layout__drawer .mdl-navigation .mdl-navigation__link:hover{background-color:#e0e0e0}.mdl-layout__drawer .mdl-navigation .mdl-navigation__link--current{background-color:#000;color:#e0e0e0}@media screen and (min-width:1025px){.mdl-layout--fixed-drawer>.mdl-layout__drawer{-webkit-transform:translateX(0);-ms-transform:translateX(0);transform:translateX(0)}}.mdl-layout__drawer-button{display:block;position:absolute;height:48px;width:48px;border:0;-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0;overflow:hidden;text-align:center;cursor:pointer;font-size:26px;line-height:50px;font-family:Helvetica,Arial,sans-serif;margin:10px 12px;top:0;left:0;color:#fff;z-index:4}.mdl-layout__header .mdl-layout__drawer-button{position:absolute;color:#fff;background-color:inherit}@media screen and (max-width:1024px){.mdl-layout__header .mdl-layout__drawer-button{margin:4px}}@media screen and (max-width:1024px){.mdl-layout__drawer-button{margin:4px;color:rgba(0,0,0,.5)}}@media screen and (min-width:1025px){.mdl-layout--fixed-drawer>.mdl-layout__drawer-button{display:none}}.mdl-layout__header{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;-webkit-flex-wrap:nowrap;-ms-flex-wrap:nowrap;flex-wrap:nowrap;-webkit-box-pack:start;-webkit-justify-content:flex-start;-ms-flex-pack:start;justify-content:flex-start;box-sizing:border-box;-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0;width:100%;margin:0;padding:0;border:none;min-height:64px;max-height:1000px;z-index:3;background-color:#3f51b5;color:#fff;box-shadow:0 2px 2px 0 rgba(0,0,0,.14),0 3px 1px -2px rgba(0,0,0,.2),0 1px 5px 0 rgba(0,0,0,.12);-webkit-transition-duration:.2s;transition-duration:.2s;-webkit-transition-timing-function:cubic-bezier(.4,0,.2,1);transition-timing-function:cubic-bezier(.4,0,.2,1);-webkit-transition-property:max-height,box-shadow;transition-property:max-height,box-shadow}@media screen and (max-width:1024px){.mdl-layout__header{min-height:56px}}.mdl-layout--fixed-drawer.is-upgraded:not(.is-small-screen)>.mdl-layout__header{margin-left:240px;width:calc(100% - 240px)}@media screen and (min-width:1025px){.mdl-layout--fixed-drawer>.mdl-layout__header .mdl-layout__header-row{padding-left:40px}}.mdl-layout__header>.mdl-layout-icon{position:absolute;left:40px;top:16px;height:32px;width:32px;overflow:hidden;z-index:3;display:block}@media screen and (max-width:1024px){.mdl-layout__header>.mdl-layout-icon{left:16px;top:12px}}.mdl-layout.has-drawer .mdl-layout__header>.mdl-layout-icon{display:none}.mdl-layout__header.is-compact{max-height:64px}@media screen and (max-width:1024px){.mdl-layout__header.is-compact{max-height:56px}}.mdl-layout__header.is-compact.has-tabs{height:112px}@media screen and (max-width:1024px){.mdl-layout__header.is-compact.has-tabs{min-height:104px}}@media screen and (max-width:1024px){.mdl-layout__header{display:none}.mdl-layout--fixed-header>.mdl-layout__header{display:-webkit-flex;display:-ms-flexbox;display:flex}}.mdl-layout__header--transparent.mdl-layout__header--transparent{background-color:transparent;box-shadow:none}.mdl-layout__header--seamed,.mdl-layout__header--scroll{box-shadow:none}.mdl-layout__header--waterfall{box-shadow:none;overflow:hidden}.mdl-layout__header--waterfall.is-casting-shadow{box-shadow:0 2px 2px 0 rgba(0,0,0,.14),0 3px 1px -2px rgba(0,0,0,.2),0 1px 5px 0 rgba(0,0,0,.12)}.mdl-layout__header-row{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row;-webkit-flex-wrap:nowrap;-ms-flex-wrap:nowrap;flex-wrap:nowrap;-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0;box-sizing:border-box;-webkit-align-self:stretch;-ms-flex-item-align:stretch;align-self:stretch;-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;height:64px;margin:0;padding:0 40px 0 80px}@media screen and (max-width:1024px){.mdl-layout__header-row{height:56px;padding:0 16px 0 72px}}.mdl-layout__header-row>*{-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0}.mdl-layout__header--scroll .mdl-layout__header-row{width:100%}.mdl-layout__header-row .mdl-navigation{margin:0;padding:0;height:64px;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row;-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center}@media screen and (max-width:1024px){.mdl-layout__header-row .mdl-navigation{height:56px}}.mdl-layout__header-row .mdl-navigation__link{display:block;color:#fff;line-height:64px;padding:0 24px}@media screen and (max-width:1024px){.mdl-layout__header-row .mdl-navigation__link{line-height:56px;padding:0 16px}}.mdl-layout__obfuscator{background-color:transparent;position:absolute;top:0;left:0;height:100%;width:100%;z-index:4;visibility:hidden;-webkit-transition-property:background-color;transition-property:background-color;-webkit-transition-duration:.2s;transition-duration:.2s;-webkit-transition-timing-function:cubic-bezier(.4,0,.2,1);transition-timing-function:cubic-bezier(.4,0,.2,1)}.mdl-layout__obfuscator.is-visible{background-color:rgba(0,0,0,.5);visibility:visible}.mdl-layout__content{-ms-flex:0 1 auto;display:inline-block;overflow-y:auto;overflow-x:hidden;-webkit-box-flex:1;-webkit-flex-grow:1;-ms-flex-positive:1;flex-grow:1;z-index:1;-webkit-overflow-scrolling:touch}.mdl-layout--fixed-drawer>.mdl-layout__content{margin-left:240px}.mdl-layout__container.has-scrolling-header .mdl-layout__content{overflow:visible}@media screen and (max-width:1024px){.mdl-layout--fixed-drawer>.mdl-layout__content{margin-left:0}.mdl-layout__container.has-scrolling-header .mdl-layout__content{overflow-y:auto;overflow-x:hidden}}.mdl-layout__tab-bar{height:96px;margin:0;width:calc(100% - 112px);padding:0 0 0 56px;display:-webkit-flex;display:-ms-flexbox;display:flex;background-color:#3f51b5;overflow-y:hidden;overflow-x:scroll}.mdl-layout__tab-bar::-webkit-scrollbar{display:none}@media screen and (max-width:1024px){.mdl-layout__tab-bar{width:calc(100% - 60px);padding:0 0 0 60px}}.mdl-layout--fixed-tabs .mdl-layout__tab-bar{padding:0;overflow:hidden;width:100%}.mdl-layout__tab-bar-container{position:relative;height:48px;width:100%;border:none;margin:0;z-index:2;-webkit-box-flex:0;-webkit-flex-grow:0;-ms-flex-positive:0;flex-grow:0;-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0;overflow:hidden}.mdl-layout__container>.mdl-layout__tab-bar-container{position:absolute;top:0;left:0}.mdl-layout__tab-bar-button{display:inline-block;position:absolute;top:0;height:48px;width:56px;z-index:4;text-align:center;background-color:#3f51b5;color:transparent;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}@media screen and (max-width:1024px){.mdl-layout__tab-bar-button{display:none;width:60px}}.mdl-layout--fixed-tabs .mdl-layout__tab-bar-button{display:none}.mdl-layout__tab-bar-button .material-icons{line-height:48px}.mdl-layout__tab-bar-button.is-active{color:#fff}.mdl-layout__tab-bar-left-button{left:0}.mdl-layout__tab-bar-right-button{right:0}.mdl-layout__tab{margin:0;border:none;padding:0 24px;float:left;position:relative;display:block;-webkit-box-flex:0;-webkit-flex-grow:0;-ms-flex-positive:0;flex-grow:0;-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0;text-decoration:none;height:48px;line-height:48px;text-align:center;font-weight:500;font-size:14px;text-transform:uppercase;color:rgba(255,255,255,.6);overflow:hidden}@media screen and (max-width:1024px){.mdl-layout__tab{padding:0 12px}}.mdl-layout--fixed-tabs .mdl-layout__tab{float:none;-webkit-box-flex:1;-webkit-flex-grow:1;-ms-flex-positive:1;flex-grow:1;padding:0}.mdl-layout.is-upgraded .mdl-layout__tab.is-active{color:#fff}.mdl-layout.is-upgraded .mdl-layout__tab.is-active::after{height:2px;width:100%;display:block;content:\" \";bottom:0;left:0;position:absolute;background:#ff4081;-webkit-animation:border-expand .2s cubic-bezier(.4,0,.4,1).01s alternate forwards;animation:border-expand .2s cubic-bezier(.4,0,.4,1).01s alternate forwards;-webkit-transition:all 1s cubic-bezier(.4,0,1,1);transition:all 1s cubic-bezier(.4,0,1,1)}.mdl-layout__tab .mdl-layout__tab-ripple-container{display:block;position:absolute;height:100%;width:100%;left:0;top:0;z-index:1;overflow:hidden}.mdl-layout__tab .mdl-layout__tab-ripple-container .mdl-ripple{background-color:#fff}.mdl-layout__tab-panel{display:block}.mdl-layout.is-upgraded .mdl-layout__tab-panel{display:none}.mdl-layout.is-upgraded .mdl-layout__tab-panel.is-active{display:block}.mdl-radio{position:relative;font-size:16px;line-height:24px;display:inline-block;box-sizing:border-box;margin:0;padding-left:0}.mdl-radio.is-upgraded{padding-left:24px}.mdl-radio__button{line-height:24px}.mdl-radio.is-upgraded .mdl-radio__button{position:absolute;width:0;height:0;margin:0;padding:0;opacity:0;-ms-appearance:none;-moz-appearance:none;-webkit-appearance:none;appearance:none;border:none}.mdl-radio__outer-circle{position:absolute;top:4px;left:0;display:inline-block;box-sizing:border-box;width:16px;height:16px;margin:0;cursor:pointer;border:2px solid rgba(0,0,0,.54);border-radius:50%;z-index:2}.mdl-radio.is-checked .mdl-radio__outer-circle{border:2px solid #3f51b5}.mdl-radio.is-disabled .mdl-radio__outer-circle{border:2px solid rgba(0,0,0,.26);cursor:auto}.mdl-radio__inner-circle{position:absolute;z-index:1;margin:0;top:8px;left:4px;box-sizing:border-box;width:8px;height:8px;cursor:pointer;-webkit-transition-duration:.28s;transition-duration:.28s;-webkit-transition-timing-function:cubic-bezier(.4,0,.2,1);transition-timing-function:cubic-bezier(.4,0,.2,1);-webkit-transition-property:-webkit-transform;transition-property:transform;-webkit-transform:scale3d(0,0,0);transform:scale3d(0,0,0);border-radius:50%;background:#3f51b5}.mdl-radio.is-checked .mdl-radio__inner-circle{-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}.mdl-radio.is-disabled .mdl-radio__inner-circle{background:rgba(0,0,0,.26);cursor:auto}.mdl-radio.is-focused .mdl-radio__inner-circle{box-shadow:0 0 0 10px rgba(0,0,0,.1)}.mdl-radio__label{cursor:pointer}.mdl-radio.is-disabled .mdl-radio__label{color:rgba(0,0,0,.26);cursor:auto}.mdl-radio__ripple-container{position:absolute;z-index:2;top:-9px;left:-13px;box-sizing:border-box;width:42px;height:42px;border-radius:50%;cursor:pointer;overflow:hidden;-webkit-mask-image:-webkit-radial-gradient(circle,#fff,#000)}.mdl-radio__ripple-container .mdl-ripple{background:#3f51b5}.mdl-radio.is-disabled .mdl-radio__ripple-container{cursor:auto}.mdl-radio.is-disabled .mdl-radio__ripple-container .mdl-ripple{background:0 0}_:-ms-input-placeholder,:root .mdl-slider.mdl-slider.is-upgraded{-ms-appearance:none;height:32px;margin:0}.mdl-slider{width:calc(100% - 40px);margin:0 20px}.mdl-slider.is-upgraded{-webkit-appearance:none;-moz-appearance:none;appearance:none;height:2px;background:0 0;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;outline:0;padding:0;color:#3f51b5;-webkit-align-self:center;-ms-flex-item-align:center;align-self:center;z-index:1;cursor:pointer}.mdl-slider.is-upgraded::-moz-focus-outer{border:0}.mdl-slider.is-upgraded::-ms-tooltip{display:none}.mdl-slider.is-upgraded::-webkit-slider-runnable-track{background:0 0}.mdl-slider.is-upgraded::-moz-range-track{background:0 0;border:none}.mdl-slider.is-upgraded::-ms-track{background:0 0;color:transparent;height:2px;width:100%;border:none}.mdl-slider.is-upgraded::-ms-fill-lower{padding:0;background:linear-gradient(to right,transparent,transparent 16px,#3f51b5 16px,#3f51b5 0)}.mdl-slider.is-upgraded::-ms-fill-upper{padding:0;background:linear-gradient(to left,transparent,transparent 16px,rgba(0,0,0,.26)16px,rgba(0,0,0,.26)0)}.mdl-slider.is-upgraded::-webkit-slider-thumb{-webkit-appearance:none;width:12px;height:12px;box-sizing:border-box;border-radius:50%;background:#3f51b5;border:none;-webkit-transition:-webkit-transform .18s cubic-bezier(.4,0,.2,1),border .18s cubic-bezier(.4,0,.2,1),box-shadow .18s cubic-bezier(.4,0,.2,1),background .28s cubic-bezier(.4,0,.2,1);transition:transform .18s cubic-bezier(.4,0,.2,1),border .18s cubic-bezier(.4,0,.2,1),box-shadow .18s cubic-bezier(.4,0,.2,1),background .28s cubic-bezier(.4,0,.2,1)}.mdl-slider.is-upgraded::-moz-range-thumb{-moz-appearance:none;width:12px;height:12px;box-sizing:border-box;border-radius:50%;background-image:none;background:#3f51b5;border:none}.mdl-slider.is-upgraded:focus:not(:active)::-webkit-slider-thumb{box-shadow:0 0 0 10px rgba(63,81,181,.26)}.mdl-slider.is-upgraded:focus:not(:active)::-moz-range-thumb{box-shadow:0 0 0 10px rgba(63,81,181,.26)}.mdl-slider.is-upgraded:active::-webkit-slider-thumb{background-image:none;background:#3f51b5;-webkit-transform:scale(1.5);transform:scale(1.5)}.mdl-slider.is-upgraded:active::-moz-range-thumb{background-image:none;background:#3f51b5;transform:scale(1.5)}.mdl-slider.is-upgraded::-ms-thumb{width:32px;height:32px;border:none;border-radius:50%;background:#3f51b5;-ms-transform:scale(.375);transform:scale(.375);transition:transform .18s cubic-bezier(.4,0,.2,1),background .28s cubic-bezier(.4,0,.2,1)}.mdl-slider.is-upgraded:focus:not(:active)::-ms-thumb{background:radial-gradient(circle closest-side,#3f51b5 0%,#3f51b5 37.5%,rgba(63,81,181,.26)37.5%,rgba(63,81,181,.26)100%);-ms-transform:scale(1);transform:scale(1)}.mdl-slider.is-upgraded:active::-ms-thumb{background:#3f51b5;-ms-transform:scale(.5625);transform:scale(.5625)}.mdl-slider.is-upgraded.is-lowest-value::-webkit-slider-thumb{border:2px solid rgba(0,0,0,.26);background:0 0}.mdl-slider.is-upgraded.is-lowest-value::-moz-range-thumb{border:2px solid rgba(0,0,0,.26);background:0 0}.mdl-slider.is-upgraded.is-lowest-value+.mdl-slider__background-flex>.mdl-slider__background-upper{left:6px}.mdl-slider.is-upgraded.is-lowest-value:focus:not(:active)::-webkit-slider-thumb{box-shadow:0 0 0 10px rgba(0,0,0,.12);background:rgba(0,0,0,.12)}.mdl-slider.is-upgraded.is-lowest-value:focus:not(:active)::-moz-range-thumb{box-shadow:0 0 0 10px rgba(0,0,0,.12);background:rgba(0,0,0,.12)}.mdl-slider.is-upgraded.is-lowest-value:active::-webkit-slider-thumb{border:1.6px solid rgba(0,0,0,.26);-webkit-transform:scale(1.5);transform:scale(1.5)}.mdl-slider.is-upgraded.is-lowest-value:active+.mdl-slider__background-flex>.mdl-slider__background-upper{left:9px}.mdl-slider.is-upgraded.is-lowest-value:active::-moz-range-thumb{border:1.5px solid rgba(0,0,0,.26);transform:scale(1.5)}.mdl-slider.is-upgraded.is-lowest-value::-ms-thumb{background:radial-gradient(circle closest-side,transparent 0%,transparent 66.67%,rgba(0,0,0,.26)66.67%,rgba(0,0,0,.26)100%)}.mdl-slider.is-upgraded.is-lowest-value:focus:not(:active)::-ms-thumb{background:radial-gradient(circle closest-side,rgba(0,0,0,.12)0%,rgba(0,0,0,.12)25%,rgba(0,0,0,.26)25%,rgba(0,0,0,.26)37.5%,rgba(0,0,0,.12)37.5%,rgba(0,0,0,.12)100%);-ms-transform:scale(1);transform:scale(1)}.mdl-slider.is-upgraded.is-lowest-value:active::-ms-thumb{-ms-transform:scale(.5625);transform:scale(.5625);background:radial-gradient(circle closest-side,transparent 0%,transparent 77.78%,rgba(0,0,0,.26)77.78%,rgba(0,0,0,.26)100%)}.mdl-slider.is-upgraded.is-lowest-value::-ms-fill-lower{background:0 0}.mdl-slider.is-upgraded.is-lowest-value::-ms-fill-upper{margin-left:6px}.mdl-slider.is-upgraded.is-lowest-value:active::-ms-fill-upper{margin-left:9px}.mdl-slider.is-upgraded:disabled:focus::-webkit-slider-thumb,.mdl-slider.is-upgraded:disabled:active::-webkit-slider-thumb,.mdl-slider.is-upgraded:disabled::-webkit-slider-thumb{-webkit-transform:scale(.667);transform:scale(.667);background:rgba(0,0,0,.26)}.mdl-slider.is-upgraded:disabled:focus::-moz-range-thumb,.mdl-slider.is-upgraded:disabled:active::-moz-range-thumb,.mdl-slider.is-upgraded:disabled::-moz-range-thumb{transform:scale(.667);background:rgba(0,0,0,.26)}.mdl-slider.is-upgraded:disabled+.mdl-slider__background-flex>.mdl-slider__background-lower{background-color:rgba(0,0,0,.26);left:-6px}.mdl-slider.is-upgraded:disabled+.mdl-slider__background-flex>.mdl-slider__background-upper{left:6px}.mdl-slider.is-upgraded.is-lowest-value:disabled:focus::-webkit-slider-thumb,.mdl-slider.is-upgraded.is-lowest-value:disabled:active::-webkit-slider-thumb,.mdl-slider.is-upgraded.is-lowest-value:disabled::-webkit-slider-thumb{border:3px solid rgba(0,0,0,.26);background:0 0;-webkit-transform:scale(.667);transform:scale(.667)}.mdl-slider.is-upgraded.is-lowest-value:disabled:focus::-moz-range-thumb,.mdl-slider.is-upgraded.is-lowest-value:disabled:active::-moz-range-thumb,.mdl-slider.is-upgraded.is-lowest-value:disabled::-moz-range-thumb{border:3px solid rgba(0,0,0,.26);background:0 0;transform:scale(.667)}.mdl-slider.is-upgraded.is-lowest-value:disabled:active+.mdl-slider__background-flex>.mdl-slider__background-upper{left:6px}.mdl-slider.is-upgraded:disabled:focus::-ms-thumb,.mdl-slider.is-upgraded:disabled:active::-ms-thumb,.mdl-slider.is-upgraded:disabled::-ms-thumb{-ms-transform:scale(.25);transform:scale(.25);background:rgba(0,0,0,.26)}.mdl-slider.is-upgraded.is-lowest-value:disabled:focus::-ms-thumb,.mdl-slider.is-upgraded.is-lowest-value:disabled:active::-ms-thumb,.mdl-slider.is-upgraded.is-lowest-value:disabled::-ms-thumb{-ms-transform:scale(.25);transform:scale(.25);background:radial-gradient(circle closest-side,transparent 0%,transparent 50%,rgba(0,0,0,.26)50%,rgba(0,0,0,.26)100%)}.mdl-slider.is-upgraded:disabled::-ms-fill-lower{margin-right:6px;background:linear-gradient(to right,transparent,transparent 25px,rgba(0,0,0,.26)25px,rgba(0,0,0,.26)0)}.mdl-slider.is-upgraded:disabled::-ms-fill-upper{margin-left:6px}.mdl-slider.is-upgraded.is-lowest-value:disabled:active::-ms-fill-upper{margin-left:6px}.mdl-slider__ie-container{height:18px;overflow:visible;border:none;margin:none;padding:none}.mdl-slider__container{height:18px;position:relative;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row}.mdl-slider__container,.mdl-slider__background-flex{background:0 0;display:-webkit-flex;display:-ms-flexbox;display:flex}.mdl-slider__background-flex{position:absolute;height:2px;width:calc(100% - 52px);top:50%;left:0;margin:0 26px;overflow:hidden;border:0;padding:0;-webkit-transform:translate(0,-1px);-ms-transform:translate(0,-1px);transform:translate(0,-1px)}.mdl-slider__background-lower{background:#3f51b5}.mdl-slider__background-lower,.mdl-slider__background-upper{-webkit-box-flex:0;-webkit-flex:0;-ms-flex:0;flex:0;position:relative;border:0;padding:0}.mdl-slider__background-upper{background:rgba(0,0,0,.26);-webkit-transition:left .18s cubic-bezier(.4,0,.2,1);transition:left .18s cubic-bezier(.4,0,.2,1)}.mdl-spinner{display:inline-block;position:relative;width:28px;height:28px}.mdl-spinner:not(.is-upgraded).is-active:after{content:\"Loading...\"}.mdl-spinner.is-upgraded.is-active{-webkit-animation:mdl-spinner__container-rotate 1568.23529412ms linear infinite;animation:mdl-spinner__container-rotate 1568.23529412ms linear infinite}@-webkit-keyframes mdl-spinner__container-rotate{to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes mdl-spinner__container-rotate{to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}.mdl-spinner__layer{position:absolute;width:100%;height:100%;opacity:0}.mdl-spinner__layer-1{border-color:#42a5f5}.mdl-spinner--single-color .mdl-spinner__layer-1{border-color:#3f51b5}.mdl-spinner.is-active .mdl-spinner__layer-1{-webkit-animation:mdl-spinner__fill-unfill-rotate 5332ms cubic-bezier(.4,0,.2,1)infinite both,mdl-spinner__layer-1-fade-in-out 5332ms cubic-bezier(.4,0,.2,1)infinite both;animation:mdl-spinner__fill-unfill-rotate 5332ms cubic-bezier(.4,0,.2,1)infinite both,mdl-spinner__layer-1-fade-in-out 5332ms cubic-bezier(.4,0,.2,1)infinite both}.mdl-spinner__layer-2{border-color:#f44336}.mdl-spinner--single-color .mdl-spinner__layer-2{border-color:#3f51b5}.mdl-spinner.is-active .mdl-spinner__layer-2{-webkit-animation:mdl-spinner__fill-unfill-rotate 5332ms cubic-bezier(.4,0,.2,1)infinite both,mdl-spinner__layer-2-fade-in-out 5332ms cubic-bezier(.4,0,.2,1)infinite both;animation:mdl-spinner__fill-unfill-rotate 5332ms cubic-bezier(.4,0,.2,1)infinite both,mdl-spinner__layer-2-fade-in-out 5332ms cubic-bezier(.4,0,.2,1)infinite both}.mdl-spinner__layer-3{border-color:#fdd835}.mdl-spinner--single-color .mdl-spinner__layer-3{border-color:#3f51b5}.mdl-spinner.is-active .mdl-spinner__layer-3{-webkit-animation:mdl-spinner__fill-unfill-rotate 5332ms cubic-bezier(.4,0,.2,1)infinite both,mdl-spinner__layer-3-fade-in-out 5332ms cubic-bezier(.4,0,.2,1)infinite both;animation:mdl-spinner__fill-unfill-rotate 5332ms cubic-bezier(.4,0,.2,1)infinite both,mdl-spinner__layer-3-fade-in-out 5332ms cubic-bezier(.4,0,.2,1)infinite both}.mdl-spinner__layer-4{border-color:#4caf50}.mdl-spinner--single-color .mdl-spinner__layer-4{border-color:#3f51b5}.mdl-spinner.is-active .mdl-spinner__layer-4{-webkit-animation:mdl-spinner__fill-unfill-rotate 5332ms cubic-bezier(.4,0,.2,1)infinite both,mdl-spinner__layer-4-fade-in-out 5332ms cubic-bezier(.4,0,.2,1)infinite both;animation:mdl-spinner__fill-unfill-rotate 5332ms cubic-bezier(.4,0,.2,1)infinite both,mdl-spinner__layer-4-fade-in-out 5332ms cubic-bezier(.4,0,.2,1)infinite both}@-webkit-keyframes mdl-spinner__fill-unfill-rotate{12.5%{-webkit-transform:rotate(135deg);transform:rotate(135deg)}25%{-webkit-transform:rotate(270deg);transform:rotate(270deg)}37.5%{-webkit-transform:rotate(405deg);transform:rotate(405deg)}50%{-webkit-transform:rotate(540deg);transform:rotate(540deg)}62.5%{-webkit-transform:rotate(675deg);transform:rotate(675deg)}75%{-webkit-transform:rotate(810deg);transform:rotate(810deg)}87.5%{-webkit-transform:rotate(945deg);transform:rotate(945deg)}to{-webkit-transform:rotate(1080deg);transform:rotate(1080deg)}}@keyframes mdl-spinner__fill-unfill-rotate{12.5%{-webkit-transform:rotate(135deg);transform:rotate(135deg)}25%{-webkit-transform:rotate(270deg);transform:rotate(270deg)}37.5%{-webkit-transform:rotate(405deg);transform:rotate(405deg)}50%{-webkit-transform:rotate(540deg);transform:rotate(540deg)}62.5%{-webkit-transform:rotate(675deg);transform:rotate(675deg)}75%{-webkit-transform:rotate(810deg);transform:rotate(810deg)}87.5%{-webkit-transform:rotate(945deg);transform:rotate(945deg)}to{-webkit-transform:rotate(1080deg);transform:rotate(1080deg)}}@-webkit-keyframes mdl-spinner__layer-1-fade-in-out{from,25%{opacity:.99}26%,89%{opacity:0}90%,100%{opacity:.99}}@keyframes mdl-spinner__layer-1-fade-in-out{from,25%{opacity:.99}26%,89%{opacity:0}90%,100%{opacity:.99}}@-webkit-keyframes mdl-spinner__layer-2-fade-in-out{from,15%{opacity:0}25%,50%{opacity:.99}51%{opacity:0}}@keyframes mdl-spinner__layer-2-fade-in-out{from,15%{opacity:0}25%,50%{opacity:.99}51%{opacity:0}}@-webkit-keyframes mdl-spinner__layer-3-fade-in-out{from,40%{opacity:0}50%,75%{opacity:.99}76%{opacity:0}}@keyframes mdl-spinner__layer-3-fade-in-out{from,40%{opacity:0}50%,75%{opacity:.99}76%{opacity:0}}@-webkit-keyframes mdl-spinner__layer-4-fade-in-out{from,65%{opacity:0}75%,90%{opacity:.99}100%{opacity:0}}@keyframes mdl-spinner__layer-4-fade-in-out{from,65%{opacity:0}75%,90%{opacity:.99}100%{opacity:0}}.mdl-spinner__gap-patch{position:absolute;box-sizing:border-box;top:0;left:45%;width:10%;height:100%;overflow:hidden;border-color:inherit}.mdl-spinner__gap-patch .mdl-spinner__circle{width:1000%;left:-450%}.mdl-spinner__circle-clipper{display:inline-block;position:relative;width:50%;height:100%;overflow:hidden;border-color:inherit}.mdl-spinner__circle-clipper .mdl-spinner__circle{width:200%}.mdl-spinner__circle{box-sizing:border-box;height:100%;border-width:3px;border-style:solid;border-color:inherit;border-bottom-color:transparent!important;border-radius:50%;-webkit-animation:none;animation:none;position:absolute;top:0;right:0;bottom:0;left:0}.mdl-spinner__left .mdl-spinner__circle{border-right-color:transparent!important;-webkit-transform:rotate(129deg);-ms-transform:rotate(129deg);transform:rotate(129deg)}.mdl-spinner.is-active .mdl-spinner__left .mdl-spinner__circle{-webkit-animation:mdl-spinner__left-spin 1333ms cubic-bezier(.4,0,.2,1)infinite both;animation:mdl-spinner__left-spin 1333ms cubic-bezier(.4,0,.2,1)infinite both}.mdl-spinner__right .mdl-spinner__circle{left:-100%;border-left-color:transparent!important;-webkit-transform:rotate(-129deg);-ms-transform:rotate(-129deg);transform:rotate(-129deg)}.mdl-spinner.is-active .mdl-spinner__right .mdl-spinner__circle{-webkit-animation:mdl-spinner__right-spin 1333ms cubic-bezier(.4,0,.2,1)infinite both;animation:mdl-spinner__right-spin 1333ms cubic-bezier(.4,0,.2,1)infinite both}@-webkit-keyframes mdl-spinner__left-spin{from{-webkit-transform:rotate(130deg);transform:rotate(130deg)}50%{-webkit-transform:rotate(-5deg);transform:rotate(-5deg)}to{-webkit-transform:rotate(130deg);transform:rotate(130deg)}}@keyframes mdl-spinner__left-spin{from{-webkit-transform:rotate(130deg);transform:rotate(130deg)}50%{-webkit-transform:rotate(-5deg);transform:rotate(-5deg)}to{-webkit-transform:rotate(130deg);transform:rotate(130deg)}}@-webkit-keyframes mdl-spinner__right-spin{from{-webkit-transform:rotate(-130deg);transform:rotate(-130deg)}50%{-webkit-transform:rotate(5deg);transform:rotate(5deg)}to{-webkit-transform:rotate(-130deg);transform:rotate(-130deg)}}@keyframes mdl-spinner__right-spin{from{-webkit-transform:rotate(-130deg);transform:rotate(-130deg)}50%{-webkit-transform:rotate(5deg);transform:rotate(5deg)}to{-webkit-transform:rotate(-130deg);transform:rotate(-130deg)}}.mdl-switch{position:relative;z-index:1;vertical-align:middle;display:inline-block;box-sizing:border-box;width:100%;height:24px;margin:0;padding:0;overflow:visible;-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.mdl-switch.is-upgraded{padding-left:28px}.mdl-switch__input{line-height:24px}.mdl-switch.is-upgraded .mdl-switch__input{position:absolute;width:0;height:0;margin:0;padding:0;opacity:0;-ms-appearance:none;-moz-appearance:none;-webkit-appearance:none;appearance:none;border:none}.mdl-switch__track{background:rgba(0,0,0,.26);position:absolute;left:0;top:5px;height:14px;width:36px;border-radius:14px;cursor:pointer}.mdl-switch.is-checked .mdl-switch__track{background:rgba(63,81,181,.5)}.mdl-switch.is-disabled .mdl-switch__track{background:rgba(0,0,0,.12);cursor:auto}.mdl-switch__thumb{background:#fafafa;position:absolute;left:0;top:2px;height:20px;width:20px;border-radius:50%;cursor:pointer;box-shadow:0 2px 2px 0 rgba(0,0,0,.14),0 3px 1px -2px rgba(0,0,0,.2),0 1px 5px 0 rgba(0,0,0,.12);-webkit-transition-duration:.28s;transition-duration:.28s;-webkit-transition-timing-function:cubic-bezier(.4,0,.2,1);transition-timing-function:cubic-bezier(.4,0,.2,1);-webkit-transition-property:left;transition-property:left}.mdl-switch.is-checked .mdl-switch__thumb{background:#3f51b5;left:16px;box-shadow:0 3px 4px 0 rgba(0,0,0,.14),0 3px 3px -2px rgba(0,0,0,.2),0 1px 8px 0 rgba(0,0,0,.12)}.mdl-switch.is-disabled .mdl-switch__thumb{background:#bdbdbd;cursor:auto}.mdl-switch__focus-helper{position:absolute;top:50%;left:50%;-webkit-transform:translate(-4px,-4px);-ms-transform:translate(-4px,-4px);transform:translate(-4px,-4px);display:inline-block;box-sizing:border-box;width:8px;height:8px;border-radius:50%;background-color:transparent}.mdl-switch.is-focused .mdl-switch__focus-helper{box-shadow:0 0 0 20px rgba(0,0,0,.1);background-color:rgba(0,0,0,.1)}.mdl-switch.is-focused.is-checked .mdl-switch__focus-helper{box-shadow:0 0 0 20px rgba(63,81,181,.26);background-color:rgba(63,81,181,.26)}.mdl-switch__label{position:relative;cursor:pointer;font-size:16px;line-height:24px;margin:0;left:24px}.mdl-switch.is-disabled .mdl-switch__label{color:#bdbdbd;cursor:auto}.mdl-switch__ripple-container{position:absolute;z-index:2;top:-12px;left:-14px;box-sizing:border-box;width:48px;height:48px;border-radius:50%;cursor:pointer;overflow:hidden;-webkit-mask-image:-webkit-radial-gradient(circle,#fff,#000);-webkit-transition-duration:.4s;transition-duration:.4s;-webkit-transition-timing-function:step-end;transition-timing-function:step-end;-webkit-transition-property:left;transition-property:left}.mdl-switch__ripple-container .mdl-ripple{background:#3f51b5}.mdl-switch.is-disabled .mdl-switch__ripple-container{cursor:auto}.mdl-switch.is-disabled .mdl-switch__ripple-container .mdl-ripple{background:0 0}.mdl-switch.is-checked .mdl-switch__ripple-container{cursor:auto;left:2px}.mdl-tabs{display:block;width:100%}.mdl-tabs__tab-bar{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;-webkit-align-content:space-between;-ms-flex-line-pack:justify;align-content:space-between;-webkit-box-align:start;-webkit-align-items:flex-start;-ms-flex-align:start;align-items:flex-start;height:48px;padding:0;margin:0;border-bottom:1px solid #e0e0e0}.mdl-tabs__tab{margin:0;border:none;padding:0 24px;float:left;position:relative;display:block;color:red;text-decoration:none;height:48px;line-height:48px;text-align:center;font-weight:500;font-size:14px;text-transform:uppercase;color:rgba(0,0,0,.54);overflow:hidden}.mdl-tabs.is-upgraded .mdl-tabs__tab.is-active{color:rgba(0,0,0,.87)}.mdl-tabs.is-upgraded .mdl-tabs__tab.is-active:after{height:2px;width:100%;display:block;content:\" \";bottom:0;left:0;position:absolute;background:#3f51b5;-webkit-animation:border-expand .2s cubic-bezier(.4,0,.4,1).01s alternate forwards;animation:border-expand .2s cubic-bezier(.4,0,.4,1).01s alternate forwards;-webkit-transition:all 1s cubic-bezier(.4,0,1,1);transition:all 1s cubic-bezier(.4,0,1,1)}.mdl-tabs__tab .mdl-tabs__ripple-container{display:block;position:absolute;height:100%;width:100%;left:0;top:0;z-index:1;overflow:hidden}.mdl-tabs__tab .mdl-tabs__ripple-container .mdl-ripple{background:#3f51b5}.mdl-tabs__panel{display:block}.mdl-tabs.is-upgraded .mdl-tabs__panel{display:none}.mdl-tabs.is-upgraded .mdl-tabs__panel.is-active{display:block}@-webkit-keyframes border-expand{0%{opacity:0;width:0}100%{opacity:1;width:100%}}@keyframes border-expand{0%{opacity:0;width:0}100%{opacity:1;width:100%}}.mdl-textfield{position:relative;font-size:16px;display:inline-block;box-sizing:border-box;width:300px;max-width:100%;margin:0;padding:20px 0}.mdl-textfield .mdl-button{position:absolute;bottom:20px}.mdl-textfield--align-right{text-align:right}.mdl-textfield--full-width{width:100%}.mdl-textfield--expandable{min-width:32px;width:auto;min-height:32px}.mdl-textfield__input{border:none;border-bottom:1px solid rgba(0,0,0,.12);display:block;font-size:16px;margin:0;padding:4px 0;width:100%;background:0 0;text-align:left;color:inherit}.mdl-textfield.is-focused .mdl-textfield__input{outline:none}.mdl-textfield.is-invalid .mdl-textfield__input{border-color:#de3226;box-shadow:none}.mdl-textfield.is-disabled .mdl-textfield__input{background-color:transparent;border-bottom:1px dotted rgba(0,0,0,.12);color:rgba(0,0,0,.26)}.mdl-textfield textarea.mdl-textfield__input{display:block}.mdl-textfield__label{bottom:0;color:rgba(0,0,0,.26);font-size:16px;left:0;right:0;pointer-events:none;position:absolute;display:block;top:24px;width:100%;overflow:hidden;white-space:nowrap;text-align:left}.mdl-textfield.is-dirty .mdl-textfield__label{visibility:hidden}.mdl-textfield--floating-label .mdl-textfield__label{-webkit-transition-duration:.2s;transition-duration:.2s;-webkit-transition-timing-function:cubic-bezier(.4,0,.2,1);transition-timing-function:cubic-bezier(.4,0,.2,1)}.mdl-textfield.is-disabled.is-disabled .mdl-textfield__label{color:rgba(0,0,0,.26)}.mdl-textfield--floating-label.is-focused .mdl-textfield__label,.mdl-textfield--floating-label.is-dirty .mdl-textfield__label{color:#3f51b5;font-size:12px;top:4px;visibility:visible}.mdl-textfield--floating-label.is-focused .mdl-textfield__expandable-holder .mdl-textfield__label,.mdl-textfield--floating-label.is-dirty .mdl-textfield__expandable-holder .mdl-textfield__label{top:-16px}.mdl-textfield--floating-label.is-invalid .mdl-textfield__label{color:#de3226;font-size:12px}.mdl-textfield__label:after{background-color:#3f51b5;bottom:20px;content:'';height:2px;left:45%;position:absolute;-webkit-transition-duration:.2s;transition-duration:.2s;-webkit-transition-timing-function:cubic-bezier(.4,0,.2,1);transition-timing-function:cubic-bezier(.4,0,.2,1);visibility:hidden;width:10px}.mdl-textfield.is-focused .mdl-textfield__label:after{left:0;visibility:visible;width:100%}.mdl-textfield.is-invalid .mdl-textfield__label:after{background-color:#de3226}.mdl-textfield__error{color:#de3226;position:absolute;font-size:12px;margin-top:3px;visibility:hidden;display:block}.mdl-textfield.is-invalid .mdl-textfield__error{visibility:visible}.mdl-textfield__expandable-holder{position:relative;margin-left:32px;-webkit-transition-duration:.2s;transition-duration:.2s;-webkit-transition-timing-function:cubic-bezier(.4,0,.2,1);transition-timing-function:cubic-bezier(.4,0,.2,1);display:inline-block;max-width:.1px}.mdl-textfield.is-focused .mdl-textfield__expandable-holder,.mdl-textfield.is-dirty .mdl-textfield__expandable-holder{max-width:600px}.mdl-textfield__expandable-holder .mdl-textfield__label:after{bottom:0}.mdl-tooltip{-webkit-transform:scale(0);-ms-transform:scale(0);transform:scale(0);-webkit-transform-origin:top center;-ms-transform-origin:top center;transform-origin:top center;will-change:transform;z-index:999;background:rgba(97,97,97,.9);border-radius:2px;color:#fff;display:inline-block;font-size:10px;font-weight:500;line-height:14px;max-width:170px;position:fixed;top:-500px;left:-500px;padding:8px;text-align:center}.mdl-tooltip.is-active{-webkit-animation:pulse 200ms cubic-bezier(0,0,.2,1)forwards;animation:pulse 200ms cubic-bezier(0,0,.2,1)forwards}.mdl-tooltip--large{line-height:14px;font-size:14px;padding:16px}@-webkit-keyframes pulse{0%{-webkit-transform:scale(0);transform:scale(0);opacity:0}50%{-webkit-transform:scale(.99);transform:scale(.99)}100%{-webkit-transform:scale(1);transform:scale(1);opacity:1;visibility:visible}}@keyframes pulse{0%{-webkit-transform:scale(0);transform:scale(0);opacity:0}50%{-webkit-transform:scale(.99);transform:scale(.99)}100%{-webkit-transform:scale(1);transform:scale(1);opacity:1;visibility:visible}}.mdl-shadow--2dp{box-shadow:0 2px 2px 0 rgba(0,0,0,.14),0 3px 1px -2px rgba(0,0,0,.2),0 1px 5px 0 rgba(0,0,0,.12)}.mdl-shadow--3dp{box-shadow:0 3px 4px 0 rgba(0,0,0,.14),0 3px 3px -2px rgba(0,0,0,.2),0 1px 8px 0 rgba(0,0,0,.12)}.mdl-shadow--4dp{box-shadow:0 4px 5px 0 rgba(0,0,0,.14),0 1px 10px 0 rgba(0,0,0,.12),0 2px 4px -1px rgba(0,0,0,.2)}.mdl-shadow--6dp{box-shadow:0 6px 10px 0 rgba(0,0,0,.14),0 1px 18px 0 rgba(0,0,0,.12),0 3px 5px -1px rgba(0,0,0,.2)}.mdl-shadow--8dp{box-shadow:0 8px 10px 1px rgba(0,0,0,.14),0 3px 14px 2px rgba(0,0,0,.12),0 5px 5px -3px rgba(0,0,0,.2)}.mdl-shadow--16dp{box-shadow:0 16px 24px 2px rgba(0,0,0,.14),0 6px 30px 5px rgba(0,0,0,.12),0 8px 10px -5px rgba(0,0,0,.2)}.mdl-grid{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-flow:row wrap;-ms-flex-flow:row wrap;flex-flow:row wrap;margin:0 auto;-webkit-box-align:stretch;-webkit-align-items:stretch;-ms-flex-align:stretch;align-items:stretch}.mdl-grid.mdl-grid--no-spacing{padding:0}.mdl-cell{box-sizing:border-box}.mdl-cell--top{-webkit-align-self:flex-start;-ms-flex-item-align:start;align-self:flex-start}.mdl-cell--middle{-webkit-align-self:center;-ms-flex-item-align:center;align-self:center}.mdl-cell--bottom{-webkit-align-self:flex-end;-ms-flex-item-align:end;align-self:flex-end}.mdl-cell--stretch{-webkit-align-self:stretch;-ms-flex-item-align:stretch;align-self:stretch}.mdl-grid.mdl-grid--no-spacing>.mdl-cell{margin:0}@media (max-width:479px){.mdl-grid{padding:8px}.mdl-cell{margin:8px;width:calc(100% - 16px)}.mdl-grid--no-spacing>.mdl-cell{width:100%}.mdl-cell--hide-phone{display:none!important}.mdl-cell--1-col,.mdl-cell--1-col-phone.mdl-cell--1-col-phone{width:calc(25% - 16px)}.mdl-grid--no-spacing>.mdl-cell--1-col,.mdl-grid--no-spacing>.mdl-cell--1-col-phone.mdl-cell--1-col-phone{width:25%}.mdl-cell--2-col,.mdl-cell--2-col-phone.mdl-cell--2-col-phone{width:calc(50% - 16px)}.mdl-grid--no-spacing>.mdl-cell--2-col,.mdl-grid--no-spacing>.mdl-cell--2-col-phone.mdl-cell--2-col-phone{width:50%}.mdl-cell--3-col,.mdl-cell--3-col-phone.mdl-cell--3-col-phone{width:calc(75% - 16px)}.mdl-grid--no-spacing>.mdl-cell--3-col,.mdl-grid--no-spacing>.mdl-cell--3-col-phone.mdl-cell--3-col-phone{width:75%}.mdl-cell--4-col,.mdl-cell--4-col-phone.mdl-cell--4-col-phone{width:calc(100% - 16px)}.mdl-grid--no-spacing>.mdl-cell--4-col,.mdl-grid--no-spacing>.mdl-cell--4-col-phone.mdl-cell--4-col-phone{width:100%}.mdl-cell--5-col,.mdl-cell--5-col-phone.mdl-cell--5-col-phone{width:calc(100% - 16px)}.mdl-grid--no-spacing>.mdl-cell--5-col,.mdl-grid--no-spacing>.mdl-cell--5-col-phone.mdl-cell--5-col-phone{width:100%}.mdl-cell--6-col,.mdl-cell--6-col-phone.mdl-cell--6-col-phone{width:calc(100% - 16px)}.mdl-grid--no-spacing>.mdl-cell--6-col,.mdl-grid--no-spacing>.mdl-cell--6-col-phone.mdl-cell--6-col-phone{width:100%}.mdl-cell--7-col,.mdl-cell--7-col-phone.mdl-cell--7-col-phone{width:calc(100% - 16px)}.mdl-grid--no-spacing>.mdl-cell--7-col,.mdl-grid--no-spacing>.mdl-cell--7-col-phone.mdl-cell--7-col-phone{width:100%}.mdl-cell--8-col,.mdl-cell--8-col-phone.mdl-cell--8-col-phone{width:calc(100% - 16px)}.mdl-grid--no-spacing>.mdl-cell--8-col,.mdl-grid--no-spacing>.mdl-cell--8-col-phone.mdl-cell--8-col-phone{width:100%}.mdl-cell--9-col,.mdl-cell--9-col-phone.mdl-cell--9-col-phone{width:calc(100% - 16px)}.mdl-grid--no-spacing>.mdl-cell--9-col,.mdl-grid--no-spacing>.mdl-cell--9-col-phone.mdl-cell--9-col-phone{width:100%}.mdl-cell--10-col,.mdl-cell--10-col-phone.mdl-cell--10-col-phone{width:calc(100% - 16px)}.mdl-grid--no-spacing>.mdl-cell--10-col,.mdl-grid--no-spacing>.mdl-cell--10-col-phone.mdl-cell--10-col-phone{width:100%}.mdl-cell--11-col,.mdl-cell--11-col-phone.mdl-cell--11-col-phone{width:calc(100% - 16px)}.mdl-grid--no-spacing>.mdl-cell--11-col,.mdl-grid--no-spacing>.mdl-cell--11-col-phone.mdl-cell--11-col-phone{width:100%}.mdl-cell--12-col,.mdl-cell--12-col-phone.mdl-cell--12-col-phone{width:calc(100% - 16px)}.mdl-grid--no-spacing>.mdl-cell--12-col,.mdl-grid--no-spacing>.mdl-cell--12-col-phone.mdl-cell--12-col-phone{width:100%}}@media (min-width:480px) and (max-width:839px){.mdl-grid{padding:8px}.mdl-cell{margin:8px;width:calc(50% - 16px)}.mdl-grid--no-spacing>.mdl-cell{width:50%}.mdl-cell--hide-tablet{display:none!important}.mdl-cell--1-col,.mdl-cell--1-col-tablet.mdl-cell--1-col-tablet{width:calc(12.5% - 16px)}.mdl-grid--no-spacing>.mdl-cell--1-col,.mdl-grid--no-spacing>.mdl-cell--1-col-tablet.mdl-cell--1-col-tablet{width:12.5%}.mdl-cell--2-col,.mdl-cell--2-col-tablet.mdl-cell--2-col-tablet{width:calc(25% - 16px)}.mdl-grid--no-spacing>.mdl-cell--2-col,.mdl-grid--no-spacing>.mdl-cell--2-col-tablet.mdl-cell--2-col-tablet{width:25%}.mdl-cell--3-col,.mdl-cell--3-col-tablet.mdl-cell--3-col-tablet{width:calc(37.5% - 16px)}.mdl-grid--no-spacing>.mdl-cell--3-col,.mdl-grid--no-spacing>.mdl-cell--3-col-tablet.mdl-cell--3-col-tablet{width:37.5%}.mdl-cell--4-col,.mdl-cell--4-col-tablet.mdl-cell--4-col-tablet{width:calc(50% - 16px)}.mdl-grid--no-spacing>.mdl-cell--4-col,.mdl-grid--no-spacing>.mdl-cell--4-col-tablet.mdl-cell--4-col-tablet{width:50%}.mdl-cell--5-col,.mdl-cell--5-col-tablet.mdl-cell--5-col-tablet{width:calc(62.5% - 16px)}.mdl-grid--no-spacing>.mdl-cell--5-col,.mdl-grid--no-spacing>.mdl-cell--5-col-tablet.mdl-cell--5-col-tablet{width:62.5%}.mdl-cell--6-col,.mdl-cell--6-col-tablet.mdl-cell--6-col-tablet{width:calc(75% - 16px)}.mdl-grid--no-spacing>.mdl-cell--6-col,.mdl-grid--no-spacing>.mdl-cell--6-col-tablet.mdl-cell--6-col-tablet{width:75%}.mdl-cell--7-col,.mdl-cell--7-col-tablet.mdl-cell--7-col-tablet{width:calc(87.5% - 16px)}.mdl-grid--no-spacing>.mdl-cell--7-col,.mdl-grid--no-spacing>.mdl-cell--7-col-tablet.mdl-cell--7-col-tablet{width:87.5%}.mdl-cell--8-col,.mdl-cell--8-col-tablet.mdl-cell--8-col-tablet{width:calc(100% - 16px)}.mdl-grid--no-spacing>.mdl-cell--8-col,.mdl-grid--no-spacing>.mdl-cell--8-col-tablet.mdl-cell--8-col-tablet{width:100%}.mdl-cell--9-col,.mdl-cell--9-col-tablet.mdl-cell--9-col-tablet{width:calc(100% - 16px)}.mdl-grid--no-spacing>.mdl-cell--9-col,.mdl-grid--no-spacing>.mdl-cell--9-col-tablet.mdl-cell--9-col-tablet{width:100%}.mdl-cell--10-col,.mdl-cell--10-col-tablet.mdl-cell--10-col-tablet{width:calc(100% - 16px)}.mdl-grid--no-spacing>.mdl-cell--10-col,.mdl-grid--no-spacing>.mdl-cell--10-col-tablet.mdl-cell--10-col-tablet{width:100%}.mdl-cell--11-col,.mdl-cell--11-col-tablet.mdl-cell--11-col-tablet{width:calc(100% - 16px)}.mdl-grid--no-spacing>.mdl-cell--11-col,.mdl-grid--no-spacing>.mdl-cell--11-col-tablet.mdl-cell--11-col-tablet{width:100%}.mdl-cell--12-col,.mdl-cell--12-col-tablet.mdl-cell--12-col-tablet{width:calc(100% - 16px)}.mdl-grid--no-spacing>.mdl-cell--12-col,.mdl-grid--no-spacing>.mdl-cell--12-col-tablet.mdl-cell--12-col-tablet{width:100%}}@media (min-width:840px){.mdl-grid{padding:8px}.mdl-cell{margin:8px;width:calc(33.3333333333% - 16px)}.mdl-grid--no-spacing>.mdl-cell{width:33.3333333333%}.mdl-cell--hide-desktop{display:none!important}.mdl-cell--1-col,.mdl-cell--1-col-desktop.mdl-cell--1-col-desktop{width:calc(8.3333333333% - 16px)}.mdl-grid--no-spacing>.mdl-cell--1-col,.mdl-grid--no-spacing>.mdl-cell--1-col-desktop.mdl-cell--1-col-desktop{width:8.3333333333%}.mdl-cell--2-col,.mdl-cell--2-col-desktop.mdl-cell--2-col-desktop{width:calc(16.6666666667% - 16px)}.mdl-grid--no-spacing>.mdl-cell--2-col,.mdl-grid--no-spacing>.mdl-cell--2-col-desktop.mdl-cell--2-col-desktop{width:16.6666666667%}.mdl-cell--3-col,.mdl-cell--3-col-desktop.mdl-cell--3-col-desktop{width:calc(25% - 16px)}.mdl-grid--no-spacing>.mdl-cell--3-col,.mdl-grid--no-spacing>.mdl-cell--3-col-desktop.mdl-cell--3-col-desktop{width:25%}.mdl-cell--4-col,.mdl-cell--4-col-desktop.mdl-cell--4-col-desktop{width:calc(33.3333333333% - 16px)}.mdl-grid--no-spacing>.mdl-cell--4-col,.mdl-grid--no-spacing>.mdl-cell--4-col-desktop.mdl-cell--4-col-desktop{width:33.3333333333%}.mdl-cell--5-col,.mdl-cell--5-col-desktop.mdl-cell--5-col-desktop{width:calc(41.6666666667% - 16px)}.mdl-grid--no-spacing>.mdl-cell--5-col,.mdl-grid--no-spacing>.mdl-cell--5-col-desktop.mdl-cell--5-col-desktop{width:41.6666666667%}.mdl-cell--6-col,.mdl-cell--6-col-desktop.mdl-cell--6-col-desktop{width:calc(50% - 16px)}.mdl-grid--no-spacing>.mdl-cell--6-col,.mdl-grid--no-spacing>.mdl-cell--6-col-desktop.mdl-cell--6-col-desktop{width:50%}.mdl-cell--7-col,.mdl-cell--7-col-desktop.mdl-cell--7-col-desktop{width:calc(58.3333333333% - 16px)}.mdl-grid--no-spacing>.mdl-cell--7-col,.mdl-grid--no-spacing>.mdl-cell--7-col-desktop.mdl-cell--7-col-desktop{width:58.3333333333%}.mdl-cell--8-col,.mdl-cell--8-col-desktop.mdl-cell--8-col-desktop{width:calc(66.6666666667% - 16px)}.mdl-grid--no-spacing>.mdl-cell--8-col,.mdl-grid--no-spacing>.mdl-cell--8-col-desktop.mdl-cell--8-col-desktop{width:66.6666666667%}.mdl-cell--9-col,.mdl-cell--9-col-desktop.mdl-cell--9-col-desktop{width:calc(75% - 16px)}.mdl-grid--no-spacing>.mdl-cell--9-col,.mdl-grid--no-spacing>.mdl-cell--9-col-desktop.mdl-cell--9-col-desktop{width:75%}.mdl-cell--10-col,.mdl-cell--10-col-desktop.mdl-cell--10-col-desktop{width:calc(83.3333333333% - 16px)}.mdl-grid--no-spacing>.mdl-cell--10-col,.mdl-grid--no-spacing>.mdl-cell--10-col-desktop.mdl-cell--10-col-desktop{width:83.3333333333%}.mdl-cell--11-col,.mdl-cell--11-col-desktop.mdl-cell--11-col-desktop{width:calc(91.6666666667% - 16px)}.mdl-grid--no-spacing>.mdl-cell--11-col,.mdl-grid--no-spacing>.mdl-cell--11-col-desktop.mdl-cell--11-col-desktop{width:91.6666666667%}.mdl-cell--12-col,.mdl-cell--12-col-desktop.mdl-cell--12-col-desktop{width:calc(100% - 16px)}.mdl-grid--no-spacing>.mdl-cell--12-col,.mdl-grid--no-spacing>.mdl-cell--12-col-desktop.mdl-cell--12-col-desktop{width:100%}}\n/*# sourceMappingURL=material.min.css.map */\n","/**\n * material-design-lite - Material Design Components in CSS, JS and HTML\n * @version v1.0.6\n * @license Apache-2.0\n * @copyright 2015 Google, Inc.\n * @link https://github.com/google/material-design-lite\n */\n@charset \"UTF-8\";\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* Material Design Lite */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* Typography */\n/* Shadows */\n/* Animations */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n/*\n * What follows is the result of much research on cross-browser styling.\n * Credit left inline and big thanks to Nicolas Gallagher, Jonathan Neal,\n * Kroc Camen, and the H5BP dev community and team.\n */\n/* ==========================================================================\n Base styles: opinionated defaults\n ========================================================================== */\nhtml {\n color: rgba(0,0,0, 0.87);\n font-size: 1em;\n line-height: 1.4; }\n\n/*\n * Remove text-shadow in selection highlight:\n * https://twitter.com/miketaylr/status/12228805301\n *\n * These selection rule sets have to be separate.\n * Customize the background color to match your design.\n */\n::-moz-selection {\n background: #b3d4fc;\n text-shadow: none; }\n::selection {\n background: #b3d4fc;\n text-shadow: none; }\n\n/*\n * A better looking default horizontal rule\n */\nhr {\n display: block;\n height: 1px;\n border: 0;\n border-top: 1px solid #ccc;\n margin: 1em 0;\n padding: 0; }\n\n/*\n * Remove the gap between audio, canvas, iframes,\n * images, videos and the bottom of their containers:\n * https://github.com/h5bp/html5-boilerplate/issues/440\n */\naudio,\ncanvas,\niframe,\nimg,\nsvg,\nvideo {\n vertical-align: middle; }\n\n/*\n * Remove default fieldset styles.\n */\nfieldset {\n border: 0;\n margin: 0;\n padding: 0; }\n\n/*\n * Allow only vertical resizing of textareas.\n */\ntextarea {\n resize: vertical; }\n\n/* ==========================================================================\n Browser Upgrade Prompt\n ========================================================================== */\n.browserupgrade {\n margin: 0.2em 0;\n background: #ccc;\n color: #000;\n padding: 0.2em 0; }\n\n/* ==========================================================================\n Author's custom styles\n ========================================================================== */\n/* ==========================================================================\n Helper classes\n ========================================================================== */\n/*\n * Hide visually and from screen readers:\n */\n.hidden {\n display: none !important; }\n\n/*\n * Hide only visually, but have it available for screen readers:\n * http://snook.ca/archives/html_and_css/hiding-content-for-accessibility\n */\n.visuallyhidden {\n border: 0;\n clip: rect(0 0 0 0);\n height: 1px;\n margin: -1px;\n overflow: hidden;\n padding: 0;\n position: absolute;\n width: 1px; }\n\n/*\n * Extends the .visuallyhidden class to allow the element\n * to be focusable when navigated to via the keyboard:\n * https://www.drupal.org/node/897638\n */\n.visuallyhidden.focusable:active,\n.visuallyhidden.focusable:focus {\n clip: auto;\n height: auto;\n margin: 0;\n overflow: visible;\n position: static;\n width: auto; }\n\n/*\n * Hide visually and from screen readers, but maintain layout\n */\n.invisible {\n visibility: hidden; }\n\n/*\n * Clearfix: contain floats\n *\n * For modern browsers\n * 1. The space content is one way to avoid an Opera bug when the\n * `contenteditable` attribute is included anywhere else in the document.\n * Otherwise it causes space to appear at the top and bottom of elements\n * that receive the `clearfix` class.\n * 2. The use of `table` rather than `block` is only necessary if using\n * `:before` to contain the top-margins of child elements.\n */\n.clearfix:before,\n.clearfix:after {\n content: \" \";\n /* 1 */\n display: table;\n /* 2 */ }\n\n.clearfix:after {\n clear: both; }\n\n/* ==========================================================================\n EXAMPLE Media Queries for Responsive Design.\n These examples override the primary ('mobile first') styles.\n Modify as content requires.\n ========================================================================== */\n/* ==========================================================================\n Print styles.\n Inlined to avoid the additional HTTP request:\n http://www.phpied.com/delay-loading-your-print-css/\n ========================================================================== */\n@media print {\n *,\n *:before,\n *:after,\n *:first-letter,\n *:first-line {\n background: transparent !important;\n color: #000 !important;\n /* Black prints faster: http://www.sanbeiji.com/archives/953 */\n box-shadow: none !important;\n text-shadow: none !important; }\n a,\n a:visited {\n text-decoration: underline; }\n a[href]:after {\n content: \" (\" attr(href) \")\"; }\n abbr[title]:after {\n content: \" (\" attr(title) \")\"; }\n /*\n * Don't show links that are fragment identifiers,\n * or use the `javascript:` pseudo protocol\n */\n a[href^=\"#\"]:after,\n a[href^=\"javascript:\"]:after {\n content: \"\"; }\n pre,\n blockquote {\n border: 1px solid #999;\n page-break-inside: avoid; }\n /*\n * Printing Tables:\n * http://css-discuss.incutio.com/wiki/Printing_Tables\n */\n thead {\n display: table-header-group; }\n tr,\n img {\n page-break-inside: avoid; }\n img {\n max-width: 100% !important; }\n p,\n h2,\n h3 {\n orphans: 3;\n widows: 3; }\n h2,\n h3 {\n page-break-after: avoid; } }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* Remove the unwanted box around FAB buttons */\n/* More info: http://goo.gl/IPwKi */\na, .mdl-accordion, .mdl-button, .mdl-card, .mdl-checkbox, .mdl-dropdown-menu,\n.mdl-icon-toggle, .mdl-item, .mdl-radio, .mdl-slider, .mdl-switch, .mdl-tabs__tab {\n -webkit-tap-highlight-color: transparent;\n -webkit-tap-highlight-color: rgba(255, 255, 255, 0); }\n\n/*\n * Make html take up the entire screen\n * Then set touch-action to avoid touch delay on mobile IE\n */\nhtml {\n width: 100%;\n height: 100%;\n -ms-touch-action: manipulation;\n touch-action: manipulation; }\n\n/*\n* Make body take up the entire screen\n* Remove body margin so layout containers don't cause extra overflow.\n*/\nbody {\n width: 100%;\n min-height: 100%;\n margin: 0; }\n\n/*\n * Main display reset for IE support.\n * Source: http://weblog.west-wind.com/posts/2015/Jan/12/main-HTML5-Tag-not-working-in-Internet-Explorer-91011\n */\nmain {\n display: block; }\n\n/*\n* Apply no display to elements with the hidden attribute.\n* IE 9 and 10 support.\n*/\n*[hidden] {\n display: none !important; }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* Typography */\n/* Shadows */\n/* Animations */\nhtml, body {\n font-family: \"Helvetica\", \"Arial\", sans-serif;\n font-size: 14px;\n font-weight: 400;\n line-height: 20px; }\n\nh1, h2, h3, h4, h5, h6, p {\n margin: 0;\n padding: 0; }\n\n/**\n * Styles for HTML elements\n */\nh1 small, h2 small, h3 small, h4 small, h5 small, h6 small {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 56px;\n font-weight: 400;\n line-height: 1.35;\n letter-spacing: -0.02em;\n opacity: 0.54;\n font-size: 0.6em; }\n\nh1 {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 56px;\n font-weight: 400;\n line-height: 1.35;\n letter-spacing: -0.02em;\n margin-top: 24px;\n margin-bottom: 24px; }\n\nh2 {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 45px;\n font-weight: 400;\n line-height: 48px;\n margin-top: 24px;\n margin-bottom: 24px; }\n\nh3 {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 34px;\n font-weight: 400;\n line-height: 40px;\n margin-top: 24px;\n margin-bottom: 24px; }\n\nh4 {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 24px;\n font-weight: 400;\n line-height: 32px;\n -moz-osx-font-smoothing: grayscale;\n margin-top: 24px;\n margin-bottom: 16px; }\n\nh5 {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 20px;\n font-weight: 500;\n line-height: 1;\n letter-spacing: 0.02em;\n margin-top: 24px;\n margin-bottom: 16px; }\n\nh6 {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 16px;\n font-weight: 400;\n line-height: 24px;\n letter-spacing: 0.04em;\n margin-top: 24px;\n margin-bottom: 16px; }\n\np {\n font-size: 14px;\n font-weight: 400;\n line-height: 24px;\n letter-spacing: 0;\n margin-bottom: 16px; }\n\na {\n color: rgb(255,64,129);\n font-weight: 500; }\n\nblockquote {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n position: relative;\n font-size: 24px;\n font-weight: 300;\n font-style: italic;\n line-height: 1.35;\n letter-spacing: 0.08em; }\n blockquote:before {\n position: absolute;\n left: -0.5em;\n content: '“'; }\n blockquote:after {\n content: '”';\n margin-left: -0.05em; }\n\nmark {\n background-color: #f4ff81; }\n\ndt {\n font-weight: 700; }\n\naddress {\n font-size: 12px;\n font-weight: 400;\n line-height: 1;\n letter-spacing: 0;\n font-style: normal; }\n\nul, ol {\n font-size: 14px;\n font-weight: 400;\n line-height: 24px;\n letter-spacing: 0; }\n\n/**\n * Class Name Styles\n */\n.mdl-typography--display-4 {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 112px;\n font-weight: 300;\n line-height: 1;\n letter-spacing: -0.04em; }\n\n.mdl-typography--display-4-color-contrast {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 112px;\n font-weight: 300;\n line-height: 1;\n letter-spacing: -0.04em;\n opacity: 0.54; }\n\n.mdl-typography--display-3 {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 56px;\n font-weight: 400;\n line-height: 1.35;\n letter-spacing: -0.02em; }\n\n.mdl-typography--display-3-color-contrast {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 56px;\n font-weight: 400;\n line-height: 1.35;\n letter-spacing: -0.02em;\n opacity: 0.54; }\n\n.mdl-typography--display-2 {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 45px;\n font-weight: 400;\n line-height: 48px; }\n\n.mdl-typography--display-2-color-contrast {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 45px;\n font-weight: 400;\n line-height: 48px;\n opacity: 0.54; }\n\n.mdl-typography--display-1 {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 34px;\n font-weight: 400;\n line-height: 40px; }\n\n.mdl-typography--display-1-color-contrast {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 34px;\n font-weight: 400;\n line-height: 40px;\n opacity: 0.54; }\n\n.mdl-typography--headline {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 24px;\n font-weight: 400;\n line-height: 32px;\n -moz-osx-font-smoothing: grayscale; }\n\n.mdl-typography--headline-color-contrast {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 24px;\n font-weight: 400;\n line-height: 32px;\n -moz-osx-font-smoothing: grayscale;\n opacity: 0.87; }\n\n.mdl-typography--title {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 20px;\n font-weight: 500;\n line-height: 1;\n letter-spacing: 0.02em; }\n\n.mdl-typography--title-color-contrast {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 20px;\n font-weight: 500;\n line-height: 1;\n letter-spacing: 0.02em;\n opacity: 0.87; }\n\n.mdl-typography--subhead {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 16px;\n font-weight: 400;\n line-height: 24px;\n letter-spacing: 0.04em; }\n\n.mdl-typography--subhead-color-contrast {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 16px;\n font-weight: 400;\n line-height: 24px;\n letter-spacing: 0.04em;\n opacity: 0.87; }\n\n.mdl-typography--body-2 {\n font-size: 14px;\n font-weight: bold;\n line-height: 24px;\n letter-spacing: 0; }\n\n.mdl-typography--body-2-color-contrast {\n font-size: 14px;\n font-weight: bold;\n line-height: 24px;\n letter-spacing: 0;\n opacity: 0.87; }\n\n.mdl-typography--body-1 {\n font-size: 14px;\n font-weight: 400;\n line-height: 24px;\n letter-spacing: 0; }\n\n.mdl-typography--body-1-color-contrast {\n font-size: 14px;\n font-weight: 400;\n line-height: 24px;\n letter-spacing: 0;\n opacity: 0.87; }\n\n.mdl-typography--body-2-force-preferred-font {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 14px;\n font-weight: 500;\n line-height: 24px;\n letter-spacing: 0; }\n\n.mdl-typography--body-2-force-preferred-font-color-contrast {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 14px;\n font-weight: 500;\n line-height: 24px;\n letter-spacing: 0;\n opacity: 0.87; }\n\n.mdl-typography--body-1-force-preferred-font {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 14px;\n font-weight: 400;\n line-height: 24px;\n letter-spacing: 0; }\n\n.mdl-typography--body-1-force-preferred-font-color-contrast {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 14px;\n font-weight: 400;\n line-height: 24px;\n letter-spacing: 0;\n opacity: 0.87; }\n\n.mdl-typography--caption {\n font-size: 12px;\n font-weight: 400;\n line-height: 1;\n letter-spacing: 0; }\n\n.mdl-typography--caption-force-preferred-font {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 12px;\n font-weight: 400;\n line-height: 1;\n letter-spacing: 0; }\n\n.mdl-typography--caption-color-contrast {\n font-size: 12px;\n font-weight: 400;\n line-height: 1;\n letter-spacing: 0;\n opacity: 0.54; }\n\n.mdl-typography--caption-force-preferred-font-color-contrast {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 12px;\n font-weight: 400;\n line-height: 1;\n letter-spacing: 0;\n opacity: 0.54; }\n\n.mdl-typography--menu {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 14px;\n font-weight: 500;\n line-height: 1;\n letter-spacing: 0; }\n\n.mdl-typography--menu-color-contrast {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 14px;\n font-weight: 500;\n line-height: 1;\n letter-spacing: 0;\n opacity: 0.87; }\n\n.mdl-typography--button {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 14px;\n font-weight: 500;\n text-transform: uppercase;\n line-height: 1;\n letter-spacing: 0; }\n\n.mdl-typography--button-color-contrast {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 14px;\n font-weight: 500;\n text-transform: uppercase;\n line-height: 1;\n letter-spacing: 0;\n opacity: 0.87; }\n\n.mdl-typography--text-left {\n text-align: left; }\n\n.mdl-typography--text-right {\n text-align: right; }\n\n.mdl-typography--text-center {\n text-align: center; }\n\n.mdl-typography--text-justify {\n text-align: justify; }\n\n.mdl-typography--text-nowrap {\n white-space: nowrap; }\n\n.mdl-typography--text-lowercase {\n text-transform: lowercase; }\n\n.mdl-typography--text-uppercase {\n text-transform: uppercase; }\n\n.mdl-typography--text-capitalize {\n text-transform: capitalize; }\n\n.mdl-typography--font-thin {\n font-weight: 200 !important; }\n\n.mdl-typography--font-light {\n font-weight: 300 !important; }\n\n.mdl-typography--font-regular {\n font-weight: 400 !important; }\n\n.mdl-typography--font-medium {\n font-weight: 500 !important; }\n\n.mdl-typography--font-bold {\n font-weight: 700 !important; }\n\n.mdl-typography--font-black {\n font-weight: 900 !important; }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n.mdl-color-text--red {\n color: rgb(244,67,54) !important; }\n\n.mdl-color--red {\n background-color: rgb(244,67,54) !important; }\n\n.mdl-color-text--red-50 {\n color: rgb(255,235,238) !important; }\n\n.mdl-color--red-50 {\n background-color: rgb(255,235,238) !important; }\n\n.mdl-color-text--red-100 {\n color: rgb(255,205,210) !important; }\n\n.mdl-color--red-100 {\n background-color: rgb(255,205,210) !important; }\n\n.mdl-color-text--red-200 {\n color: rgb(239,154,154) !important; }\n\n.mdl-color--red-200 {\n background-color: rgb(239,154,154) !important; }\n\n.mdl-color-text--red-300 {\n color: rgb(229,115,115) !important; }\n\n.mdl-color--red-300 {\n background-color: rgb(229,115,115) !important; }\n\n.mdl-color-text--red-400 {\n color: rgb(239,83,80) !important; }\n\n.mdl-color--red-400 {\n background-color: rgb(239,83,80) !important; }\n\n.mdl-color-text--red-500 {\n color: rgb(244,67,54) !important; }\n\n.mdl-color--red-500 {\n background-color: rgb(244,67,54) !important; }\n\n.mdl-color-text--red-600 {\n color: rgb(229,57,53) !important; }\n\n.mdl-color--red-600 {\n background-color: rgb(229,57,53) !important; }\n\n.mdl-color-text--red-700 {\n color: rgb(211,47,47) !important; }\n\n.mdl-color--red-700 {\n background-color: rgb(211,47,47) !important; }\n\n.mdl-color-text--red-800 {\n color: rgb(198,40,40) !important; }\n\n.mdl-color--red-800 {\n background-color: rgb(198,40,40) !important; }\n\n.mdl-color-text--red-900 {\n color: rgb(183,28,28) !important; }\n\n.mdl-color--red-900 {\n background-color: rgb(183,28,28) !important; }\n\n.mdl-color-text--red-A100 {\n color: rgb(255,138,128) !important; }\n\n.mdl-color--red-A100 {\n background-color: rgb(255,138,128) !important; }\n\n.mdl-color-text--red-A200 {\n color: rgb(255,82,82) !important; }\n\n.mdl-color--red-A200 {\n background-color: rgb(255,82,82) !important; }\n\n.mdl-color-text--red-A400 {\n color: rgb(255,23,68) !important; }\n\n.mdl-color--red-A400 {\n background-color: rgb(255,23,68) !important; }\n\n.mdl-color-text--red-A700 {\n color: rgb(213,0,0) !important; }\n\n.mdl-color--red-A700 {\n background-color: rgb(213,0,0) !important; }\n\n.mdl-color-text--pink {\n color: rgb(233,30,99) !important; }\n\n.mdl-color--pink {\n background-color: rgb(233,30,99) !important; }\n\n.mdl-color-text--pink-50 {\n color: rgb(252,228,236) !important; }\n\n.mdl-color--pink-50 {\n background-color: rgb(252,228,236) !important; }\n\n.mdl-color-text--pink-100 {\n color: rgb(248,187,208) !important; }\n\n.mdl-color--pink-100 {\n background-color: rgb(248,187,208) !important; }\n\n.mdl-color-text--pink-200 {\n color: rgb(244,143,177) !important; }\n\n.mdl-color--pink-200 {\n background-color: rgb(244,143,177) !important; }\n\n.mdl-color-text--pink-300 {\n color: rgb(240,98,146) !important; }\n\n.mdl-color--pink-300 {\n background-color: rgb(240,98,146) !important; }\n\n.mdl-color-text--pink-400 {\n color: rgb(236,64,122) !important; }\n\n.mdl-color--pink-400 {\n background-color: rgb(236,64,122) !important; }\n\n.mdl-color-text--pink-500 {\n color: rgb(233,30,99) !important; }\n\n.mdl-color--pink-500 {\n background-color: rgb(233,30,99) !important; }\n\n.mdl-color-text--pink-600 {\n color: rgb(216,27,96) !important; }\n\n.mdl-color--pink-600 {\n background-color: rgb(216,27,96) !important; }\n\n.mdl-color-text--pink-700 {\n color: rgb(194,24,91) !important; }\n\n.mdl-color--pink-700 {\n background-color: rgb(194,24,91) !important; }\n\n.mdl-color-text--pink-800 {\n color: rgb(173,20,87) !important; }\n\n.mdl-color--pink-800 {\n background-color: rgb(173,20,87) !important; }\n\n.mdl-color-text--pink-900 {\n color: rgb(136,14,79) !important; }\n\n.mdl-color--pink-900 {\n background-color: rgb(136,14,79) !important; }\n\n.mdl-color-text--pink-A100 {\n color: rgb(255,128,171) !important; }\n\n.mdl-color--pink-A100 {\n background-color: rgb(255,128,171) !important; }\n\n.mdl-color-text--pink-A200 {\n color: rgb(255,64,129) !important; }\n\n.mdl-color--pink-A200 {\n background-color: rgb(255,64,129) !important; }\n\n.mdl-color-text--pink-A400 {\n color: rgb(245,0,87) !important; }\n\n.mdl-color--pink-A400 {\n background-color: rgb(245,0,87) !important; }\n\n.mdl-color-text--pink-A700 {\n color: rgb(197,17,98) !important; }\n\n.mdl-color--pink-A700 {\n background-color: rgb(197,17,98) !important; }\n\n.mdl-color-text--purple {\n color: rgb(156,39,176) !important; }\n\n.mdl-color--purple {\n background-color: rgb(156,39,176) !important; }\n\n.mdl-color-text--purple-50 {\n color: rgb(243,229,245) !important; }\n\n.mdl-color--purple-50 {\n background-color: rgb(243,229,245) !important; }\n\n.mdl-color-text--purple-100 {\n color: rgb(225,190,231) !important; }\n\n.mdl-color--purple-100 {\n background-color: rgb(225,190,231) !important; }\n\n.mdl-color-text--purple-200 {\n color: rgb(206,147,216) !important; }\n\n.mdl-color--purple-200 {\n background-color: rgb(206,147,216) !important; }\n\n.mdl-color-text--purple-300 {\n color: rgb(186,104,200) !important; }\n\n.mdl-color--purple-300 {\n background-color: rgb(186,104,200) !important; }\n\n.mdl-color-text--purple-400 {\n color: rgb(171,71,188) !important; }\n\n.mdl-color--purple-400 {\n background-color: rgb(171,71,188) !important; }\n\n.mdl-color-text--purple-500 {\n color: rgb(156,39,176) !important; }\n\n.mdl-color--purple-500 {\n background-color: rgb(156,39,176) !important; }\n\n.mdl-color-text--purple-600 {\n color: rgb(142,36,170) !important; }\n\n.mdl-color--purple-600 {\n background-color: rgb(142,36,170) !important; }\n\n.mdl-color-text--purple-700 {\n color: rgb(123,31,162) !important; }\n\n.mdl-color--purple-700 {\n background-color: rgb(123,31,162) !important; }\n\n.mdl-color-text--purple-800 {\n color: rgb(106,27,154) !important; }\n\n.mdl-color--purple-800 {\n background-color: rgb(106,27,154) !important; }\n\n.mdl-color-text--purple-900 {\n color: rgb(74,20,140) !important; }\n\n.mdl-color--purple-900 {\n background-color: rgb(74,20,140) !important; }\n\n.mdl-color-text--purple-A100 {\n color: rgb(234,128,252) !important; }\n\n.mdl-color--purple-A100 {\n background-color: rgb(234,128,252) !important; }\n\n.mdl-color-text--purple-A200 {\n color: rgb(224,64,251) !important; }\n\n.mdl-color--purple-A200 {\n background-color: rgb(224,64,251) !important; }\n\n.mdl-color-text--purple-A400 {\n color: rgb(213,0,249) !important; }\n\n.mdl-color--purple-A400 {\n background-color: rgb(213,0,249) !important; }\n\n.mdl-color-text--purple-A700 {\n color: rgb(170,0,255) !important; }\n\n.mdl-color--purple-A700 {\n background-color: rgb(170,0,255) !important; }\n\n.mdl-color-text--deep-purple {\n color: rgb(103,58,183) !important; }\n\n.mdl-color--deep-purple {\n background-color: rgb(103,58,183) !important; }\n\n.mdl-color-text--deep-purple-50 {\n color: rgb(237,231,246) !important; }\n\n.mdl-color--deep-purple-50 {\n background-color: rgb(237,231,246) !important; }\n\n.mdl-color-text--deep-purple-100 {\n color: rgb(209,196,233) !important; }\n\n.mdl-color--deep-purple-100 {\n background-color: rgb(209,196,233) !important; }\n\n.mdl-color-text--deep-purple-200 {\n color: rgb(179,157,219) !important; }\n\n.mdl-color--deep-purple-200 {\n background-color: rgb(179,157,219) !important; }\n\n.mdl-color-text--deep-purple-300 {\n color: rgb(149,117,205) !important; }\n\n.mdl-color--deep-purple-300 {\n background-color: rgb(149,117,205) !important; }\n\n.mdl-color-text--deep-purple-400 {\n color: rgb(126,87,194) !important; }\n\n.mdl-color--deep-purple-400 {\n background-color: rgb(126,87,194) !important; }\n\n.mdl-color-text--deep-purple-500 {\n color: rgb(103,58,183) !important; }\n\n.mdl-color--deep-purple-500 {\n background-color: rgb(103,58,183) !important; }\n\n.mdl-color-text--deep-purple-600 {\n color: rgb(94,53,177) !important; }\n\n.mdl-color--deep-purple-600 {\n background-color: rgb(94,53,177) !important; }\n\n.mdl-color-text--deep-purple-700 {\n color: rgb(81,45,168) !important; }\n\n.mdl-color--deep-purple-700 {\n background-color: rgb(81,45,168) !important; }\n\n.mdl-color-text--deep-purple-800 {\n color: rgb(69,39,160) !important; }\n\n.mdl-color--deep-purple-800 {\n background-color: rgb(69,39,160) !important; }\n\n.mdl-color-text--deep-purple-900 {\n color: rgb(49,27,146) !important; }\n\n.mdl-color--deep-purple-900 {\n background-color: rgb(49,27,146) !important; }\n\n.mdl-color-text--deep-purple-A100 {\n color: rgb(179,136,255) !important; }\n\n.mdl-color--deep-purple-A100 {\n background-color: rgb(179,136,255) !important; }\n\n.mdl-color-text--deep-purple-A200 {\n color: rgb(124,77,255) !important; }\n\n.mdl-color--deep-purple-A200 {\n background-color: rgb(124,77,255) !important; }\n\n.mdl-color-text--deep-purple-A400 {\n color: rgb(101,31,255) !important; }\n\n.mdl-color--deep-purple-A400 {\n background-color: rgb(101,31,255) !important; }\n\n.mdl-color-text--deep-purple-A700 {\n color: rgb(98,0,234) !important; }\n\n.mdl-color--deep-purple-A700 {\n background-color: rgb(98,0,234) !important; }\n\n.mdl-color-text--indigo {\n color: rgb(63,81,181) !important; }\n\n.mdl-color--indigo {\n background-color: rgb(63,81,181) !important; }\n\n.mdl-color-text--indigo-50 {\n color: rgb(232,234,246) !important; }\n\n.mdl-color--indigo-50 {\n background-color: rgb(232,234,246) !important; }\n\n.mdl-color-text--indigo-100 {\n color: rgb(197,202,233) !important; }\n\n.mdl-color--indigo-100 {\n background-color: rgb(197,202,233) !important; }\n\n.mdl-color-text--indigo-200 {\n color: rgb(159,168,218) !important; }\n\n.mdl-color--indigo-200 {\n background-color: rgb(159,168,218) !important; }\n\n.mdl-color-text--indigo-300 {\n color: rgb(121,134,203) !important; }\n\n.mdl-color--indigo-300 {\n background-color: rgb(121,134,203) !important; }\n\n.mdl-color-text--indigo-400 {\n color: rgb(92,107,192) !important; }\n\n.mdl-color--indigo-400 {\n background-color: rgb(92,107,192) !important; }\n\n.mdl-color-text--indigo-500 {\n color: rgb(63,81,181) !important; }\n\n.mdl-color--indigo-500 {\n background-color: rgb(63,81,181) !important; }\n\n.mdl-color-text--indigo-600 {\n color: rgb(57,73,171) !important; }\n\n.mdl-color--indigo-600 {\n background-color: rgb(57,73,171) !important; }\n\n.mdl-color-text--indigo-700 {\n color: rgb(48,63,159) !important; }\n\n.mdl-color--indigo-700 {\n background-color: rgb(48,63,159) !important; }\n\n.mdl-color-text--indigo-800 {\n color: rgb(40,53,147) !important; }\n\n.mdl-color--indigo-800 {\n background-color: rgb(40,53,147) !important; }\n\n.mdl-color-text--indigo-900 {\n color: rgb(26,35,126) !important; }\n\n.mdl-color--indigo-900 {\n background-color: rgb(26,35,126) !important; }\n\n.mdl-color-text--indigo-A100 {\n color: rgb(140,158,255) !important; }\n\n.mdl-color--indigo-A100 {\n background-color: rgb(140,158,255) !important; }\n\n.mdl-color-text--indigo-A200 {\n color: rgb(83,109,254) !important; }\n\n.mdl-color--indigo-A200 {\n background-color: rgb(83,109,254) !important; }\n\n.mdl-color-text--indigo-A400 {\n color: rgb(61,90,254) !important; }\n\n.mdl-color--indigo-A400 {\n background-color: rgb(61,90,254) !important; }\n\n.mdl-color-text--indigo-A700 {\n color: rgb(48,79,254) !important; }\n\n.mdl-color--indigo-A700 {\n background-color: rgb(48,79,254) !important; }\n\n.mdl-color-text--blue {\n color: rgb(33,150,243) !important; }\n\n.mdl-color--blue {\n background-color: rgb(33,150,243) !important; }\n\n.mdl-color-text--blue-50 {\n color: rgb(227,242,253) !important; }\n\n.mdl-color--blue-50 {\n background-color: rgb(227,242,253) !important; }\n\n.mdl-color-text--blue-100 {\n color: rgb(187,222,251) !important; }\n\n.mdl-color--blue-100 {\n background-color: rgb(187,222,251) !important; }\n\n.mdl-color-text--blue-200 {\n color: rgb(144,202,249) !important; }\n\n.mdl-color--blue-200 {\n background-color: rgb(144,202,249) !important; }\n\n.mdl-color-text--blue-300 {\n color: rgb(100,181,246) !important; }\n\n.mdl-color--blue-300 {\n background-color: rgb(100,181,246) !important; }\n\n.mdl-color-text--blue-400 {\n color: rgb(66,165,245) !important; }\n\n.mdl-color--blue-400 {\n background-color: rgb(66,165,245) !important; }\n\n.mdl-color-text--blue-500 {\n color: rgb(33,150,243) !important; }\n\n.mdl-color--blue-500 {\n background-color: rgb(33,150,243) !important; }\n\n.mdl-color-text--blue-600 {\n color: rgb(30,136,229) !important; }\n\n.mdl-color--blue-600 {\n background-color: rgb(30,136,229) !important; }\n\n.mdl-color-text--blue-700 {\n color: rgb(25,118,210) !important; }\n\n.mdl-color--blue-700 {\n background-color: rgb(25,118,210) !important; }\n\n.mdl-color-text--blue-800 {\n color: rgb(21,101,192) !important; }\n\n.mdl-color--blue-800 {\n background-color: rgb(21,101,192) !important; }\n\n.mdl-color-text--blue-900 {\n color: rgb(13,71,161) !important; }\n\n.mdl-color--blue-900 {\n background-color: rgb(13,71,161) !important; }\n\n.mdl-color-text--blue-A100 {\n color: rgb(130,177,255) !important; }\n\n.mdl-color--blue-A100 {\n background-color: rgb(130,177,255) !important; }\n\n.mdl-color-text--blue-A200 {\n color: rgb(68,138,255) !important; }\n\n.mdl-color--blue-A200 {\n background-color: rgb(68,138,255) !important; }\n\n.mdl-color-text--blue-A400 {\n color: rgb(41,121,255) !important; }\n\n.mdl-color--blue-A400 {\n background-color: rgb(41,121,255) !important; }\n\n.mdl-color-text--blue-A700 {\n color: rgb(41,98,255) !important; }\n\n.mdl-color--blue-A700 {\n background-color: rgb(41,98,255) !important; }\n\n.mdl-color-text--light-blue {\n color: rgb(3,169,244) !important; }\n\n.mdl-color--light-blue {\n background-color: rgb(3,169,244) !important; }\n\n.mdl-color-text--light-blue-50 {\n color: rgb(225,245,254) !important; }\n\n.mdl-color--light-blue-50 {\n background-color: rgb(225,245,254) !important; }\n\n.mdl-color-text--light-blue-100 {\n color: rgb(179,229,252) !important; }\n\n.mdl-color--light-blue-100 {\n background-color: rgb(179,229,252) !important; }\n\n.mdl-color-text--light-blue-200 {\n color: rgb(129,212,250) !important; }\n\n.mdl-color--light-blue-200 {\n background-color: rgb(129,212,250) !important; }\n\n.mdl-color-text--light-blue-300 {\n color: rgb(79,195,247) !important; }\n\n.mdl-color--light-blue-300 {\n background-color: rgb(79,195,247) !important; }\n\n.mdl-color-text--light-blue-400 {\n color: rgb(41,182,246) !important; }\n\n.mdl-color--light-blue-400 {\n background-color: rgb(41,182,246) !important; }\n\n.mdl-color-text--light-blue-500 {\n color: rgb(3,169,244) !important; }\n\n.mdl-color--light-blue-500 {\n background-color: rgb(3,169,244) !important; }\n\n.mdl-color-text--light-blue-600 {\n color: rgb(3,155,229) !important; }\n\n.mdl-color--light-blue-600 {\n background-color: rgb(3,155,229) !important; }\n\n.mdl-color-text--light-blue-700 {\n color: rgb(2,136,209) !important; }\n\n.mdl-color--light-blue-700 {\n background-color: rgb(2,136,209) !important; }\n\n.mdl-color-text--light-blue-800 {\n color: rgb(2,119,189) !important; }\n\n.mdl-color--light-blue-800 {\n background-color: rgb(2,119,189) !important; }\n\n.mdl-color-text--light-blue-900 {\n color: rgb(1,87,155) !important; }\n\n.mdl-color--light-blue-900 {\n background-color: rgb(1,87,155) !important; }\n\n.mdl-color-text--light-blue-A100 {\n color: rgb(128,216,255) !important; }\n\n.mdl-color--light-blue-A100 {\n background-color: rgb(128,216,255) !important; }\n\n.mdl-color-text--light-blue-A200 {\n color: rgb(64,196,255) !important; }\n\n.mdl-color--light-blue-A200 {\n background-color: rgb(64,196,255) !important; }\n\n.mdl-color-text--light-blue-A400 {\n color: rgb(0,176,255) !important; }\n\n.mdl-color--light-blue-A400 {\n background-color: rgb(0,176,255) !important; }\n\n.mdl-color-text--light-blue-A700 {\n color: rgb(0,145,234) !important; }\n\n.mdl-color--light-blue-A700 {\n background-color: rgb(0,145,234) !important; }\n\n.mdl-color-text--cyan {\n color: rgb(0,188,212) !important; }\n\n.mdl-color--cyan {\n background-color: rgb(0,188,212) !important; }\n\n.mdl-color-text--cyan-50 {\n color: rgb(224,247,250) !important; }\n\n.mdl-color--cyan-50 {\n background-color: rgb(224,247,250) !important; }\n\n.mdl-color-text--cyan-100 {\n color: rgb(178,235,242) !important; }\n\n.mdl-color--cyan-100 {\n background-color: rgb(178,235,242) !important; }\n\n.mdl-color-text--cyan-200 {\n color: rgb(128,222,234) !important; }\n\n.mdl-color--cyan-200 {\n background-color: rgb(128,222,234) !important; }\n\n.mdl-color-text--cyan-300 {\n color: rgb(77,208,225) !important; }\n\n.mdl-color--cyan-300 {\n background-color: rgb(77,208,225) !important; }\n\n.mdl-color-text--cyan-400 {\n color: rgb(38,198,218) !important; }\n\n.mdl-color--cyan-400 {\n background-color: rgb(38,198,218) !important; }\n\n.mdl-color-text--cyan-500 {\n color: rgb(0,188,212) !important; }\n\n.mdl-color--cyan-500 {\n background-color: rgb(0,188,212) !important; }\n\n.mdl-color-text--cyan-600 {\n color: rgb(0,172,193) !important; }\n\n.mdl-color--cyan-600 {\n background-color: rgb(0,172,193) !important; }\n\n.mdl-color-text--cyan-700 {\n color: rgb(0,151,167) !important; }\n\n.mdl-color--cyan-700 {\n background-color: rgb(0,151,167) !important; }\n\n.mdl-color-text--cyan-800 {\n color: rgb(0,131,143) !important; }\n\n.mdl-color--cyan-800 {\n background-color: rgb(0,131,143) !important; }\n\n.mdl-color-text--cyan-900 {\n color: rgb(0,96,100) !important; }\n\n.mdl-color--cyan-900 {\n background-color: rgb(0,96,100) !important; }\n\n.mdl-color-text--cyan-A100 {\n color: rgb(132,255,255) !important; }\n\n.mdl-color--cyan-A100 {\n background-color: rgb(132,255,255) !important; }\n\n.mdl-color-text--cyan-A200 {\n color: rgb(24,255,255) !important; }\n\n.mdl-color--cyan-A200 {\n background-color: rgb(24,255,255) !important; }\n\n.mdl-color-text--cyan-A400 {\n color: rgb(0,229,255) !important; }\n\n.mdl-color--cyan-A400 {\n background-color: rgb(0,229,255) !important; }\n\n.mdl-color-text--cyan-A700 {\n color: rgb(0,184,212) !important; }\n\n.mdl-color--cyan-A700 {\n background-color: rgb(0,184,212) !important; }\n\n.mdl-color-text--teal {\n color: rgb(0,150,136) !important; }\n\n.mdl-color--teal {\n background-color: rgb(0,150,136) !important; }\n\n.mdl-color-text--teal-50 {\n color: rgb(224,242,241) !important; }\n\n.mdl-color--teal-50 {\n background-color: rgb(224,242,241) !important; }\n\n.mdl-color-text--teal-100 {\n color: rgb(178,223,219) !important; }\n\n.mdl-color--teal-100 {\n background-color: rgb(178,223,219) !important; }\n\n.mdl-color-text--teal-200 {\n color: rgb(128,203,196) !important; }\n\n.mdl-color--teal-200 {\n background-color: rgb(128,203,196) !important; }\n\n.mdl-color-text--teal-300 {\n color: rgb(77,182,172) !important; }\n\n.mdl-color--teal-300 {\n background-color: rgb(77,182,172) !important; }\n\n.mdl-color-text--teal-400 {\n color: rgb(38,166,154) !important; }\n\n.mdl-color--teal-400 {\n background-color: rgb(38,166,154) !important; }\n\n.mdl-color-text--teal-500 {\n color: rgb(0,150,136) !important; }\n\n.mdl-color--teal-500 {\n background-color: rgb(0,150,136) !important; }\n\n.mdl-color-text--teal-600 {\n color: rgb(0,137,123) !important; }\n\n.mdl-color--teal-600 {\n background-color: rgb(0,137,123) !important; }\n\n.mdl-color-text--teal-700 {\n color: rgb(0,121,107) !important; }\n\n.mdl-color--teal-700 {\n background-color: rgb(0,121,107) !important; }\n\n.mdl-color-text--teal-800 {\n color: rgb(0,105,92) !important; }\n\n.mdl-color--teal-800 {\n background-color: rgb(0,105,92) !important; }\n\n.mdl-color-text--teal-900 {\n color: rgb(0,77,64) !important; }\n\n.mdl-color--teal-900 {\n background-color: rgb(0,77,64) !important; }\n\n.mdl-color-text--teal-A100 {\n color: rgb(167,255,235) !important; }\n\n.mdl-color--teal-A100 {\n background-color: rgb(167,255,235) !important; }\n\n.mdl-color-text--teal-A200 {\n color: rgb(100,255,218) !important; }\n\n.mdl-color--teal-A200 {\n background-color: rgb(100,255,218) !important; }\n\n.mdl-color-text--teal-A400 {\n color: rgb(29,233,182) !important; }\n\n.mdl-color--teal-A400 {\n background-color: rgb(29,233,182) !important; }\n\n.mdl-color-text--teal-A700 {\n color: rgb(0,191,165) !important; }\n\n.mdl-color--teal-A700 {\n background-color: rgb(0,191,165) !important; }\n\n.mdl-color-text--green {\n color: rgb(76,175,80) !important; }\n\n.mdl-color--green {\n background-color: rgb(76,175,80) !important; }\n\n.mdl-color-text--green-50 {\n color: rgb(232,245,233) !important; }\n\n.mdl-color--green-50 {\n background-color: rgb(232,245,233) !important; }\n\n.mdl-color-text--green-100 {\n color: rgb(200,230,201) !important; }\n\n.mdl-color--green-100 {\n background-color: rgb(200,230,201) !important; }\n\n.mdl-color-text--green-200 {\n color: rgb(165,214,167) !important; }\n\n.mdl-color--green-200 {\n background-color: rgb(165,214,167) !important; }\n\n.mdl-color-text--green-300 {\n color: rgb(129,199,132) !important; }\n\n.mdl-color--green-300 {\n background-color: rgb(129,199,132) !important; }\n\n.mdl-color-text--green-400 {\n color: rgb(102,187,106) !important; }\n\n.mdl-color--green-400 {\n background-color: rgb(102,187,106) !important; }\n\n.mdl-color-text--green-500 {\n color: rgb(76,175,80) !important; }\n\n.mdl-color--green-500 {\n background-color: rgb(76,175,80) !important; }\n\n.mdl-color-text--green-600 {\n color: rgb(67,160,71) !important; }\n\n.mdl-color--green-600 {\n background-color: rgb(67,160,71) !important; }\n\n.mdl-color-text--green-700 {\n color: rgb(56,142,60) !important; }\n\n.mdl-color--green-700 {\n background-color: rgb(56,142,60) !important; }\n\n.mdl-color-text--green-800 {\n color: rgb(46,125,50) !important; }\n\n.mdl-color--green-800 {\n background-color: rgb(46,125,50) !important; }\n\n.mdl-color-text--green-900 {\n color: rgb(27,94,32) !important; }\n\n.mdl-color--green-900 {\n background-color: rgb(27,94,32) !important; }\n\n.mdl-color-text--green-A100 {\n color: rgb(185,246,202) !important; }\n\n.mdl-color--green-A100 {\n background-color: rgb(185,246,202) !important; }\n\n.mdl-color-text--green-A200 {\n color: rgb(105,240,174) !important; }\n\n.mdl-color--green-A200 {\n background-color: rgb(105,240,174) !important; }\n\n.mdl-color-text--green-A400 {\n color: rgb(0,230,118) !important; }\n\n.mdl-color--green-A400 {\n background-color: rgb(0,230,118) !important; }\n\n.mdl-color-text--green-A700 {\n color: rgb(0,200,83) !important; }\n\n.mdl-color--green-A700 {\n background-color: rgb(0,200,83) !important; }\n\n.mdl-color-text--light-green {\n color: rgb(139,195,74) !important; }\n\n.mdl-color--light-green {\n background-color: rgb(139,195,74) !important; }\n\n.mdl-color-text--light-green-50 {\n color: rgb(241,248,233) !important; }\n\n.mdl-color--light-green-50 {\n background-color: rgb(241,248,233) !important; }\n\n.mdl-color-text--light-green-100 {\n color: rgb(220,237,200) !important; }\n\n.mdl-color--light-green-100 {\n background-color: rgb(220,237,200) !important; }\n\n.mdl-color-text--light-green-200 {\n color: rgb(197,225,165) !important; }\n\n.mdl-color--light-green-200 {\n background-color: rgb(197,225,165) !important; }\n\n.mdl-color-text--light-green-300 {\n color: rgb(174,213,129) !important; }\n\n.mdl-color--light-green-300 {\n background-color: rgb(174,213,129) !important; }\n\n.mdl-color-text--light-green-400 {\n color: rgb(156,204,101) !important; }\n\n.mdl-color--light-green-400 {\n background-color: rgb(156,204,101) !important; }\n\n.mdl-color-text--light-green-500 {\n color: rgb(139,195,74) !important; }\n\n.mdl-color--light-green-500 {\n background-color: rgb(139,195,74) !important; }\n\n.mdl-color-text--light-green-600 {\n color: rgb(124,179,66) !important; }\n\n.mdl-color--light-green-600 {\n background-color: rgb(124,179,66) !important; }\n\n.mdl-color-text--light-green-700 {\n color: rgb(104,159,56) !important; }\n\n.mdl-color--light-green-700 {\n background-color: rgb(104,159,56) !important; }\n\n.mdl-color-text--light-green-800 {\n color: rgb(85,139,47) !important; }\n\n.mdl-color--light-green-800 {\n background-color: rgb(85,139,47) !important; }\n\n.mdl-color-text--light-green-900 {\n color: rgb(51,105,30) !important; }\n\n.mdl-color--light-green-900 {\n background-color: rgb(51,105,30) !important; }\n\n.mdl-color-text--light-green-A100 {\n color: rgb(204,255,144) !important; }\n\n.mdl-color--light-green-A100 {\n background-color: rgb(204,255,144) !important; }\n\n.mdl-color-text--light-green-A200 {\n color: rgb(178,255,89) !important; }\n\n.mdl-color--light-green-A200 {\n background-color: rgb(178,255,89) !important; }\n\n.mdl-color-text--light-green-A400 {\n color: rgb(118,255,3) !important; }\n\n.mdl-color--light-green-A400 {\n background-color: rgb(118,255,3) !important; }\n\n.mdl-color-text--light-green-A700 {\n color: rgb(100,221,23) !important; }\n\n.mdl-color--light-green-A700 {\n background-color: rgb(100,221,23) !important; }\n\n.mdl-color-text--lime {\n color: rgb(205,220,57) !important; }\n\n.mdl-color--lime {\n background-color: rgb(205,220,57) !important; }\n\n.mdl-color-text--lime-50 {\n color: rgb(249,251,231) !important; }\n\n.mdl-color--lime-50 {\n background-color: rgb(249,251,231) !important; }\n\n.mdl-color-text--lime-100 {\n color: rgb(240,244,195) !important; }\n\n.mdl-color--lime-100 {\n background-color: rgb(240,244,195) !important; }\n\n.mdl-color-text--lime-200 {\n color: rgb(230,238,156) !important; }\n\n.mdl-color--lime-200 {\n background-color: rgb(230,238,156) !important; }\n\n.mdl-color-text--lime-300 {\n color: rgb(220,231,117) !important; }\n\n.mdl-color--lime-300 {\n background-color: rgb(220,231,117) !important; }\n\n.mdl-color-text--lime-400 {\n color: rgb(212,225,87) !important; }\n\n.mdl-color--lime-400 {\n background-color: rgb(212,225,87) !important; }\n\n.mdl-color-text--lime-500 {\n color: rgb(205,220,57) !important; }\n\n.mdl-color--lime-500 {\n background-color: rgb(205,220,57) !important; }\n\n.mdl-color-text--lime-600 {\n color: rgb(192,202,51) !important; }\n\n.mdl-color--lime-600 {\n background-color: rgb(192,202,51) !important; }\n\n.mdl-color-text--lime-700 {\n color: rgb(175,180,43) !important; }\n\n.mdl-color--lime-700 {\n background-color: rgb(175,180,43) !important; }\n\n.mdl-color-text--lime-800 {\n color: rgb(158,157,36) !important; }\n\n.mdl-color--lime-800 {\n background-color: rgb(158,157,36) !important; }\n\n.mdl-color-text--lime-900 {\n color: rgb(130,119,23) !important; }\n\n.mdl-color--lime-900 {\n background-color: rgb(130,119,23) !important; }\n\n.mdl-color-text--lime-A100 {\n color: rgb(244,255,129) !important; }\n\n.mdl-color--lime-A100 {\n background-color: rgb(244,255,129) !important; }\n\n.mdl-color-text--lime-A200 {\n color: rgb(238,255,65) !important; }\n\n.mdl-color--lime-A200 {\n background-color: rgb(238,255,65) !important; }\n\n.mdl-color-text--lime-A400 {\n color: rgb(198,255,0) !important; }\n\n.mdl-color--lime-A400 {\n background-color: rgb(198,255,0) !important; }\n\n.mdl-color-text--lime-A700 {\n color: rgb(174,234,0) !important; }\n\n.mdl-color--lime-A700 {\n background-color: rgb(174,234,0) !important; }\n\n.mdl-color-text--yellow {\n color: rgb(255,235,59) !important; }\n\n.mdl-color--yellow {\n background-color: rgb(255,235,59) !important; }\n\n.mdl-color-text--yellow-50 {\n color: rgb(255,253,231) !important; }\n\n.mdl-color--yellow-50 {\n background-color: rgb(255,253,231) !important; }\n\n.mdl-color-text--yellow-100 {\n color: rgb(255,249,196) !important; }\n\n.mdl-color--yellow-100 {\n background-color: rgb(255,249,196) !important; }\n\n.mdl-color-text--yellow-200 {\n color: rgb(255,245,157) !important; }\n\n.mdl-color--yellow-200 {\n background-color: rgb(255,245,157) !important; }\n\n.mdl-color-text--yellow-300 {\n color: rgb(255,241,118) !important; }\n\n.mdl-color--yellow-300 {\n background-color: rgb(255,241,118) !important; }\n\n.mdl-color-text--yellow-400 {\n color: rgb(255,238,88) !important; }\n\n.mdl-color--yellow-400 {\n background-color: rgb(255,238,88) !important; }\n\n.mdl-color-text--yellow-500 {\n color: rgb(255,235,59) !important; }\n\n.mdl-color--yellow-500 {\n background-color: rgb(255,235,59) !important; }\n\n.mdl-color-text--yellow-600 {\n color: rgb(253,216,53) !important; }\n\n.mdl-color--yellow-600 {\n background-color: rgb(253,216,53) !important; }\n\n.mdl-color-text--yellow-700 {\n color: rgb(251,192,45) !important; }\n\n.mdl-color--yellow-700 {\n background-color: rgb(251,192,45) !important; }\n\n.mdl-color-text--yellow-800 {\n color: rgb(249,168,37) !important; }\n\n.mdl-color--yellow-800 {\n background-color: rgb(249,168,37) !important; }\n\n.mdl-color-text--yellow-900 {\n color: rgb(245,127,23) !important; }\n\n.mdl-color--yellow-900 {\n background-color: rgb(245,127,23) !important; }\n\n.mdl-color-text--yellow-A100 {\n color: rgb(255,255,141) !important; }\n\n.mdl-color--yellow-A100 {\n background-color: rgb(255,255,141) !important; }\n\n.mdl-color-text--yellow-A200 {\n color: rgb(255,255,0) !important; }\n\n.mdl-color--yellow-A200 {\n background-color: rgb(255,255,0) !important; }\n\n.mdl-color-text--yellow-A400 {\n color: rgb(255,234,0) !important; }\n\n.mdl-color--yellow-A400 {\n background-color: rgb(255,234,0) !important; }\n\n.mdl-color-text--yellow-A700 {\n color: rgb(255,214,0) !important; }\n\n.mdl-color--yellow-A700 {\n background-color: rgb(255,214,0) !important; }\n\n.mdl-color-text--amber {\n color: rgb(255,193,7) !important; }\n\n.mdl-color--amber {\n background-color: rgb(255,193,7) !important; }\n\n.mdl-color-text--amber-50 {\n color: rgb(255,248,225) !important; }\n\n.mdl-color--amber-50 {\n background-color: rgb(255,248,225) !important; }\n\n.mdl-color-text--amber-100 {\n color: rgb(255,236,179) !important; }\n\n.mdl-color--amber-100 {\n background-color: rgb(255,236,179) !important; }\n\n.mdl-color-text--amber-200 {\n color: rgb(255,224,130) !important; }\n\n.mdl-color--amber-200 {\n background-color: rgb(255,224,130) !important; }\n\n.mdl-color-text--amber-300 {\n color: rgb(255,213,79) !important; }\n\n.mdl-color--amber-300 {\n background-color: rgb(255,213,79) !important; }\n\n.mdl-color-text--amber-400 {\n color: rgb(255,202,40) !important; }\n\n.mdl-color--amber-400 {\n background-color: rgb(255,202,40) !important; }\n\n.mdl-color-text--amber-500 {\n color: rgb(255,193,7) !important; }\n\n.mdl-color--amber-500 {\n background-color: rgb(255,193,7) !important; }\n\n.mdl-color-text--amber-600 {\n color: rgb(255,179,0) !important; }\n\n.mdl-color--amber-600 {\n background-color: rgb(255,179,0) !important; }\n\n.mdl-color-text--amber-700 {\n color: rgb(255,160,0) !important; }\n\n.mdl-color--amber-700 {\n background-color: rgb(255,160,0) !important; }\n\n.mdl-color-text--amber-800 {\n color: rgb(255,143,0) !important; }\n\n.mdl-color--amber-800 {\n background-color: rgb(255,143,0) !important; }\n\n.mdl-color-text--amber-900 {\n color: rgb(255,111,0) !important; }\n\n.mdl-color--amber-900 {\n background-color: rgb(255,111,0) !important; }\n\n.mdl-color-text--amber-A100 {\n color: rgb(255,229,127) !important; }\n\n.mdl-color--amber-A100 {\n background-color: rgb(255,229,127) !important; }\n\n.mdl-color-text--amber-A200 {\n color: rgb(255,215,64) !important; }\n\n.mdl-color--amber-A200 {\n background-color: rgb(255,215,64) !important; }\n\n.mdl-color-text--amber-A400 {\n color: rgb(255,196,0) !important; }\n\n.mdl-color--amber-A400 {\n background-color: rgb(255,196,0) !important; }\n\n.mdl-color-text--amber-A700 {\n color: rgb(255,171,0) !important; }\n\n.mdl-color--amber-A700 {\n background-color: rgb(255,171,0) !important; }\n\n.mdl-color-text--orange {\n color: rgb(255,152,0) !important; }\n\n.mdl-color--orange {\n background-color: rgb(255,152,0) !important; }\n\n.mdl-color-text--orange-50 {\n color: rgb(255,243,224) !important; }\n\n.mdl-color--orange-50 {\n background-color: rgb(255,243,224) !important; }\n\n.mdl-color-text--orange-100 {\n color: rgb(255,224,178) !important; }\n\n.mdl-color--orange-100 {\n background-color: rgb(255,224,178) !important; }\n\n.mdl-color-text--orange-200 {\n color: rgb(255,204,128) !important; }\n\n.mdl-color--orange-200 {\n background-color: rgb(255,204,128) !important; }\n\n.mdl-color-text--orange-300 {\n color: rgb(255,183,77) !important; }\n\n.mdl-color--orange-300 {\n background-color: rgb(255,183,77) !important; }\n\n.mdl-color-text--orange-400 {\n color: rgb(255,167,38) !important; }\n\n.mdl-color--orange-400 {\n background-color: rgb(255,167,38) !important; }\n\n.mdl-color-text--orange-500 {\n color: rgb(255,152,0) !important; }\n\n.mdl-color--orange-500 {\n background-color: rgb(255,152,0) !important; }\n\n.mdl-color-text--orange-600 {\n color: rgb(251,140,0) !important; }\n\n.mdl-color--orange-600 {\n background-color: rgb(251,140,0) !important; }\n\n.mdl-color-text--orange-700 {\n color: rgb(245,124,0) !important; }\n\n.mdl-color--orange-700 {\n background-color: rgb(245,124,0) !important; }\n\n.mdl-color-text--orange-800 {\n color: rgb(239,108,0) !important; }\n\n.mdl-color--orange-800 {\n background-color: rgb(239,108,0) !important; }\n\n.mdl-color-text--orange-900 {\n color: rgb(230,81,0) !important; }\n\n.mdl-color--orange-900 {\n background-color: rgb(230,81,0) !important; }\n\n.mdl-color-text--orange-A100 {\n color: rgb(255,209,128) !important; }\n\n.mdl-color--orange-A100 {\n background-color: rgb(255,209,128) !important; }\n\n.mdl-color-text--orange-A200 {\n color: rgb(255,171,64) !important; }\n\n.mdl-color--orange-A200 {\n background-color: rgb(255,171,64) !important; }\n\n.mdl-color-text--orange-A400 {\n color: rgb(255,145,0) !important; }\n\n.mdl-color--orange-A400 {\n background-color: rgb(255,145,0) !important; }\n\n.mdl-color-text--orange-A700 {\n color: rgb(255,109,0) !important; }\n\n.mdl-color--orange-A700 {\n background-color: rgb(255,109,0) !important; }\n\n.mdl-color-text--deep-orange {\n color: rgb(255,87,34) !important; }\n\n.mdl-color--deep-orange {\n background-color: rgb(255,87,34) !important; }\n\n.mdl-color-text--deep-orange-50 {\n color: rgb(251,233,231) !important; }\n\n.mdl-color--deep-orange-50 {\n background-color: rgb(251,233,231) !important; }\n\n.mdl-color-text--deep-orange-100 {\n color: rgb(255,204,188) !important; }\n\n.mdl-color--deep-orange-100 {\n background-color: rgb(255,204,188) !important; }\n\n.mdl-color-text--deep-orange-200 {\n color: rgb(255,171,145) !important; }\n\n.mdl-color--deep-orange-200 {\n background-color: rgb(255,171,145) !important; }\n\n.mdl-color-text--deep-orange-300 {\n color: rgb(255,138,101) !important; }\n\n.mdl-color--deep-orange-300 {\n background-color: rgb(255,138,101) !important; }\n\n.mdl-color-text--deep-orange-400 {\n color: rgb(255,112,67) !important; }\n\n.mdl-color--deep-orange-400 {\n background-color: rgb(255,112,67) !important; }\n\n.mdl-color-text--deep-orange-500 {\n color: rgb(255,87,34) !important; }\n\n.mdl-color--deep-orange-500 {\n background-color: rgb(255,87,34) !important; }\n\n.mdl-color-text--deep-orange-600 {\n color: rgb(244,81,30) !important; }\n\n.mdl-color--deep-orange-600 {\n background-color: rgb(244,81,30) !important; }\n\n.mdl-color-text--deep-orange-700 {\n color: rgb(230,74,25) !important; }\n\n.mdl-color--deep-orange-700 {\n background-color: rgb(230,74,25) !important; }\n\n.mdl-color-text--deep-orange-800 {\n color: rgb(216,67,21) !important; }\n\n.mdl-color--deep-orange-800 {\n background-color: rgb(216,67,21) !important; }\n\n.mdl-color-text--deep-orange-900 {\n color: rgb(191,54,12) !important; }\n\n.mdl-color--deep-orange-900 {\n background-color: rgb(191,54,12) !important; }\n\n.mdl-color-text--deep-orange-A100 {\n color: rgb(255,158,128) !important; }\n\n.mdl-color--deep-orange-A100 {\n background-color: rgb(255,158,128) !important; }\n\n.mdl-color-text--deep-orange-A200 {\n color: rgb(255,110,64) !important; }\n\n.mdl-color--deep-orange-A200 {\n background-color: rgb(255,110,64) !important; }\n\n.mdl-color-text--deep-orange-A400 {\n color: rgb(255,61,0) !important; }\n\n.mdl-color--deep-orange-A400 {\n background-color: rgb(255,61,0) !important; }\n\n.mdl-color-text--deep-orange-A700 {\n color: rgb(221,44,0) !important; }\n\n.mdl-color--deep-orange-A700 {\n background-color: rgb(221,44,0) !important; }\n\n.mdl-color-text--brown {\n color: rgb(121,85,72) !important; }\n\n.mdl-color--brown {\n background-color: rgb(121,85,72) !important; }\n\n.mdl-color-text--brown-50 {\n color: rgb(239,235,233) !important; }\n\n.mdl-color--brown-50 {\n background-color: rgb(239,235,233) !important; }\n\n.mdl-color-text--brown-100 {\n color: rgb(215,204,200) !important; }\n\n.mdl-color--brown-100 {\n background-color: rgb(215,204,200) !important; }\n\n.mdl-color-text--brown-200 {\n color: rgb(188,170,164) !important; }\n\n.mdl-color--brown-200 {\n background-color: rgb(188,170,164) !important; }\n\n.mdl-color-text--brown-300 {\n color: rgb(161,136,127) !important; }\n\n.mdl-color--brown-300 {\n background-color: rgb(161,136,127) !important; }\n\n.mdl-color-text--brown-400 {\n color: rgb(141,110,99) !important; }\n\n.mdl-color--brown-400 {\n background-color: rgb(141,110,99) !important; }\n\n.mdl-color-text--brown-500 {\n color: rgb(121,85,72) !important; }\n\n.mdl-color--brown-500 {\n background-color: rgb(121,85,72) !important; }\n\n.mdl-color-text--brown-600 {\n color: rgb(109,76,65) !important; }\n\n.mdl-color--brown-600 {\n background-color: rgb(109,76,65) !important; }\n\n.mdl-color-text--brown-700 {\n color: rgb(93,64,55) !important; }\n\n.mdl-color--brown-700 {\n background-color: rgb(93,64,55) !important; }\n\n.mdl-color-text--brown-800 {\n color: rgb(78,52,46) !important; }\n\n.mdl-color--brown-800 {\n background-color: rgb(78,52,46) !important; }\n\n.mdl-color-text--brown-900 {\n color: rgb(62,39,35) !important; }\n\n.mdl-color--brown-900 {\n background-color: rgb(62,39,35) !important; }\n\n.mdl-color-text--grey {\n color: rgb(158,158,158) !important; }\n\n.mdl-color--grey {\n background-color: rgb(158,158,158) !important; }\n\n.mdl-color-text--grey-50 {\n color: rgb(250,250,250) !important; }\n\n.mdl-color--grey-50 {\n background-color: rgb(250,250,250) !important; }\n\n.mdl-color-text--grey-100 {\n color: rgb(245,245,245) !important; }\n\n.mdl-color--grey-100 {\n background-color: rgb(245,245,245) !important; }\n\n.mdl-color-text--grey-200 {\n color: rgb(238,238,238) !important; }\n\n.mdl-color--grey-200 {\n background-color: rgb(238,238,238) !important; }\n\n.mdl-color-text--grey-300 {\n color: rgb(224,224,224) !important; }\n\n.mdl-color--grey-300 {\n background-color: rgb(224,224,224) !important; }\n\n.mdl-color-text--grey-400 {\n color: rgb(189,189,189) !important; }\n\n.mdl-color--grey-400 {\n background-color: rgb(189,189,189) !important; }\n\n.mdl-color-text--grey-500 {\n color: rgb(158,158,158) !important; }\n\n.mdl-color--grey-500 {\n background-color: rgb(158,158,158) !important; }\n\n.mdl-color-text--grey-600 {\n color: rgb(117,117,117) !important; }\n\n.mdl-color--grey-600 {\n background-color: rgb(117,117,117) !important; }\n\n.mdl-color-text--grey-700 {\n color: rgb(97,97,97) !important; }\n\n.mdl-color--grey-700 {\n background-color: rgb(97,97,97) !important; }\n\n.mdl-color-text--grey-800 {\n color: rgb(66,66,66) !important; }\n\n.mdl-color--grey-800 {\n background-color: rgb(66,66,66) !important; }\n\n.mdl-color-text--grey-900 {\n color: rgb(33,33,33) !important; }\n\n.mdl-color--grey-900 {\n background-color: rgb(33,33,33) !important; }\n\n.mdl-color-text--blue-grey {\n color: rgb(96,125,139) !important; }\n\n.mdl-color--blue-grey {\n background-color: rgb(96,125,139) !important; }\n\n.mdl-color-text--blue-grey-50 {\n color: rgb(236,239,241) !important; }\n\n.mdl-color--blue-grey-50 {\n background-color: rgb(236,239,241) !important; }\n\n.mdl-color-text--blue-grey-100 {\n color: rgb(207,216,220) !important; }\n\n.mdl-color--blue-grey-100 {\n background-color: rgb(207,216,220) !important; }\n\n.mdl-color-text--blue-grey-200 {\n color: rgb(176,190,197) !important; }\n\n.mdl-color--blue-grey-200 {\n background-color: rgb(176,190,197) !important; }\n\n.mdl-color-text--blue-grey-300 {\n color: rgb(144,164,174) !important; }\n\n.mdl-color--blue-grey-300 {\n background-color: rgb(144,164,174) !important; }\n\n.mdl-color-text--blue-grey-400 {\n color: rgb(120,144,156) !important; }\n\n.mdl-color--blue-grey-400 {\n background-color: rgb(120,144,156) !important; }\n\n.mdl-color-text--blue-grey-500 {\n color: rgb(96,125,139) !important; }\n\n.mdl-color--blue-grey-500 {\n background-color: rgb(96,125,139) !important; }\n\n.mdl-color-text--blue-grey-600 {\n color: rgb(84,110,122) !important; }\n\n.mdl-color--blue-grey-600 {\n background-color: rgb(84,110,122) !important; }\n\n.mdl-color-text--blue-grey-700 {\n color: rgb(69,90,100) !important; }\n\n.mdl-color--blue-grey-700 {\n background-color: rgb(69,90,100) !important; }\n\n.mdl-color-text--blue-grey-800 {\n color: rgb(55,71,79) !important; }\n\n.mdl-color--blue-grey-800 {\n background-color: rgb(55,71,79) !important; }\n\n.mdl-color-text--blue-grey-900 {\n color: rgb(38,50,56) !important; }\n\n.mdl-color--blue-grey-900 {\n background-color: rgb(38,50,56) !important; }\n\n.mdl-color--black {\n background-color: rgb(0,0,0) !important; }\n\n.mdl-color-text--black {\n color: rgb(0,0,0) !important; }\n\n.mdl-color--white {\n background-color: rgb(255,255,255) !important; }\n\n.mdl-color-text--white {\n color: rgb(255,255,255) !important; }\n\n.mdl-color--primary {\n background-color: rgb(63,81,181) !important; }\n\n.mdl-color--primary-contrast {\n background-color: rgb(255,255,255) !important; }\n\n.mdl-color--primary-dark {\n background-color: rgb(48,63,159) !important; }\n\n.mdl-color--accent {\n background-color: rgb(255,64,129) !important; }\n\n.mdl-color--accent-contrast {\n background-color: rgb(255,255,255) !important; }\n\n.mdl-color-text--primary {\n color: rgb(63,81,181) !important; }\n\n.mdl-color-text--primary-contrast {\n color: rgb(255,255,255) !important; }\n\n.mdl-color-text--primary-dark {\n color: rgb(48,63,159) !important; }\n\n.mdl-color-text--accent {\n color: rgb(255,64,129) !important; }\n\n.mdl-color-text--accent-contrast {\n color: rgb(255,255,255) !important; }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n.mdl-ripple {\n background: rgb(0,0,0);\n border-radius: 50%;\n height: 50px;\n left: 0;\n opacity: 0;\n pointer-events: none;\n position: absolute;\n top: 0;\n -webkit-transform: translate(-50%, -50%);\n -ms-transform: translate(-50%, -50%);\n transform: translate(-50%, -50%);\n width: 50px;\n overflow: hidden; }\n .mdl-ripple.is-animating {\n -webkit-transition: -webkit-transform 0.3s cubic-bezier(0, 0, 0.2, 1), width 0.3s cubic-bezier(0, 0, 0.2, 1), height 0.3s cubic-bezier(0, 0, 0.2, 1), opacity 0.6s cubic-bezier(0, 0, 0.2, 1);\n transition: transform 0.3s cubic-bezier(0, 0, 0.2, 1), width 0.3s cubic-bezier(0, 0, 0.2, 1), height 0.3s cubic-bezier(0, 0, 0.2, 1), opacity 0.6s cubic-bezier(0, 0, 0.2, 1); }\n .mdl-ripple.is-visible {\n opacity: 0.3; }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n.mdl-animation--default {\n -webkit-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); }\n\n.mdl-animation--fast-out-slow-in {\n -webkit-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); }\n\n.mdl-animation--linear-out-slow-in {\n -webkit-transition-timing-function: cubic-bezier(0, 0, 0.2, 1);\n transition-timing-function: cubic-bezier(0, 0, 0.2, 1); }\n\n.mdl-animation--fast-out-linear-in {\n -webkit-transition-timing-function: cubic-bezier(0.4, 0, 1, 1);\n transition-timing-function: cubic-bezier(0.4, 0, 1, 1); }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n.mdl-badge {\n position: relative;\n white-space: nowrap;\n margin-right: 24px; }\n .mdl-badge:not([data-badge]) {\n margin-right: auto; }\n .mdl-badge[data-badge]:after {\n content: attr(data-badge);\n display: -webkit-box;\n display: -webkit-flex;\n display: -ms-flexbox;\n display: flex;\n -webkit-box-orient: horizontal;\n -webkit-box-direction: normal;\n -webkit-flex-direction: row;\n -ms-flex-direction: row;\n flex-direction: row;\n -webkit-flex-wrap: wrap;\n -ms-flex-wrap: wrap;\n flex-wrap: wrap;\n -webkit-box-pack: center;\n -webkit-justify-content: center;\n -ms-flex-pack: center;\n justify-content: center;\n -webkit-align-content: center;\n -ms-flex-line-pack: center;\n align-content: center;\n -webkit-box-align: center;\n -webkit-align-items: center;\n -ms-flex-align: center;\n align-items: center;\n position: absolute;\n top: -11px;\n right: -24px;\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-weight: 600;\n font-size: 12px;\n width: 22px;\n height: 22px;\n border-radius: 50%;\n background: rgb(255,64,129);\n color: rgb(255,255,255); }\n .mdl-button .mdl-badge[data-badge]:after {\n top: -10px;\n right: -5px; }\n .mdl-badge.mdl-badge--no-background[data-badge]:after {\n color: rgb(255,64,129);\n background: rgb(255,255,255);\n box-shadow: 0 0 1px gray; }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* Typography */\n/* Shadows */\n/* Animations */\n.mdl-button {\n background: transparent;\n border: none;\n border-radius: 2px;\n color: rgb(0,0,0);\n position: relative;\n height: 36px;\n min-width: 64px;\n padding: 0 16px;\n display: inline-block;\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 14px;\n font-weight: 500;\n text-transform: uppercase;\n line-height: 1;\n letter-spacing: 0;\n overflow: hidden;\n will-change: box-shadow, transform;\n -webkit-transition: box-shadow 0.2s cubic-bezier(0.4, 0, 1, 1), background-color 0.2s cubic-bezier(0.4, 0, 0.2, 1), color 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n transition: box-shadow 0.2s cubic-bezier(0.4, 0, 1, 1), background-color 0.2s cubic-bezier(0.4, 0, 0.2, 1), color 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n outline: none;\n cursor: pointer;\n text-decoration: none;\n text-align: center;\n line-height: 36px;\n vertical-align: middle; }\n .mdl-button::-moz-focus-inner {\n border: 0; }\n .mdl-button:hover {\n background-color: rgba(158,158,158, 0.20); }\n .mdl-button:focus:not(:active) {\n background-color: rgba(0,0,0, 0.12); }\n .mdl-button:active {\n background-color: rgba(158,158,158, 0.40); }\n .mdl-button.mdl-button--colored {\n color: rgb(63,81,181); }\n .mdl-button.mdl-button--colored:focus:not(:active) {\n background-color: rgba(0,0,0, 0.12); }\n\ninput.mdl-button[type=\"submit\"] {\n -webkit-appearance: none; }\n\n.mdl-button--raised {\n background: rgba(158,158,158, 0.20);\n box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12); }\n .mdl-button--raised:active {\n box-shadow: 0 4px 5px 0 rgba(0, 0, 0, 0.14), 0 1px 10px 0 rgba(0, 0, 0, 0.12), 0 2px 4px -1px rgba(0, 0, 0, 0.2);\n background-color: rgba(158,158,158, 0.40); }\n .mdl-button--raised:focus:not(:active) {\n box-shadow: 0 0 8px rgba(0, 0, 0, 0.18), 0 8px 16px rgba(0, 0, 0, 0.36);\n background-color: rgba(158,158,158, 0.40); }\n .mdl-button--raised.mdl-button--colored {\n background: rgb(63,81,181);\n color: rgb(255,255,255); }\n .mdl-button--raised.mdl-button--colored:hover {\n background-color: rgb(63,81,181); }\n .mdl-button--raised.mdl-button--colored:active {\n background-color: rgb(63,81,181); }\n .mdl-button--raised.mdl-button--colored:focus:not(:active) {\n background-color: rgb(63,81,181); }\n .mdl-button--raised.mdl-button--colored .mdl-ripple {\n background: rgb(255,255,255); }\n\n.mdl-button--fab {\n border-radius: 50%;\n font-size: 24px;\n height: 56px;\n margin: auto;\n min-width: 56px;\n width: 56px;\n padding: 0;\n overflow: hidden;\n background: rgba(158,158,158, 0.20);\n box-shadow: 0 1px 1.5px 0 rgba(0, 0, 0, 0.12), 0 1px 1px 0 rgba(0, 0, 0, 0.24);\n position: relative;\n line-height: normal; }\n .mdl-button--fab .material-icons {\n position: absolute;\n top: 50%;\n left: 50%;\n -webkit-transform: translate(-12px, -12px);\n -ms-transform: translate(-12px, -12px);\n transform: translate(-12px, -12px);\n line-height: 24px;\n width: 24px; }\n .mdl-button--fab.mdl-button--mini-fab {\n height: 40px;\n min-width: 40px;\n width: 40px; }\n .mdl-button--fab .mdl-button__ripple-container {\n border-radius: 50%;\n -webkit-mask-image: -webkit-radial-gradient(circle, white, black); }\n .mdl-button--fab:active {\n box-shadow: 0 4px 5px 0 rgba(0, 0, 0, 0.14), 0 1px 10px 0 rgba(0, 0, 0, 0.12), 0 2px 4px -1px rgba(0, 0, 0, 0.2);\n background-color: rgba(158,158,158, 0.40); }\n .mdl-button--fab:focus:not(:active) {\n box-shadow: 0 0 8px rgba(0, 0, 0, 0.18), 0 8px 16px rgba(0, 0, 0, 0.36);\n background-color: rgba(158,158,158, 0.40); }\n .mdl-button--fab.mdl-button--colored {\n background: rgb(255,64,129);\n color: rgb(255,255,255); }\n .mdl-button--fab.mdl-button--colored:hover {\n background-color: rgb(255,64,129); }\n .mdl-button--fab.mdl-button--colored:focus:not(:active) {\n background-color: rgb(255,64,129); }\n .mdl-button--fab.mdl-button--colored:active {\n background-color: rgb(255,64,129); }\n .mdl-button--fab.mdl-button--colored .mdl-ripple {\n background: rgb(255,255,255); }\n\n.mdl-button--icon {\n border-radius: 50%;\n font-size: 24px;\n height: 32px;\n margin-left: 0;\n margin-right: 0;\n min-width: 32px;\n width: 32px;\n padding: 0;\n overflow: hidden;\n color: inherit;\n line-height: normal; }\n .mdl-button--icon .material-icons {\n position: absolute;\n top: 50%;\n left: 50%;\n -webkit-transform: translate(-12px, -12px);\n -ms-transform: translate(-12px, -12px);\n transform: translate(-12px, -12px);\n line-height: 24px;\n width: 24px; }\n .mdl-button--icon.mdl-button--mini-icon {\n height: 24px;\n min-width: 24px;\n width: 24px; }\n .mdl-button--icon.mdl-button--mini-icon .material-icons {\n top: 0px;\n left: 0px; }\n .mdl-button--icon .mdl-button__ripple-container {\n border-radius: 50%;\n -webkit-mask-image: -webkit-radial-gradient(circle, white, black); }\n\n.mdl-button__ripple-container {\n display: block;\n height: 100%;\n left: 0px;\n position: absolute;\n top: 0px;\n width: 100%;\n z-index: 0;\n overflow: hidden; }\n .mdl-button[disabled] .mdl-button__ripple-container .mdl-ripple,\n .mdl-button.mdl-button--disabled .mdl-button__ripple-container .mdl-ripple {\n background-color: transparent; }\n\n.mdl-button--primary.mdl-button--primary {\n color: rgb(63,81,181); }\n .mdl-button--primary.mdl-button--primary .mdl-ripple {\n background: rgb(255,255,255); }\n .mdl-button--primary.mdl-button--primary.mdl-button--raised, .mdl-button--primary.mdl-button--primary.mdl-button--fab {\n color: rgb(255,255,255);\n background-color: rgb(63,81,181); }\n\n.mdl-button--accent.mdl-button--accent {\n color: rgb(255,64,129); }\n .mdl-button--accent.mdl-button--accent .mdl-ripple {\n background: rgb(255,255,255); }\n .mdl-button--accent.mdl-button--accent.mdl-button--raised, .mdl-button--accent.mdl-button--accent.mdl-button--fab {\n color: rgb(255,255,255);\n background-color: rgb(255,64,129); }\n\n.mdl-button[disabled][disabled], .mdl-button.mdl-button--disabled.mdl-button--disabled {\n color: rgba(0,0,0, 0.26);\n cursor: default;\n background-color: transparent; }\n\n.mdl-button--fab[disabled][disabled], .mdl-button--fab.mdl-button--disabled.mdl-button--disabled {\n background-color: rgba(0,0,0, 0.12);\n color: rgba(0,0,0, 0.26);\n box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12); }\n\n.mdl-button--raised[disabled][disabled], .mdl-button--raised.mdl-button--disabled.mdl-button--disabled {\n background-color: rgba(0,0,0, 0.12);\n color: rgba(0,0,0, 0.26);\n box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12); }\n\n.mdl-button--colored[disabled][disabled], .mdl-button--colored.mdl-button--disabled.mdl-button--disabled {\n color: rgba(0,0,0, 0.26); }\n\n.mdl-button .material-icons {\n vertical-align: middle; }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n.mdl-card {\n display: -webkit-box;\n display: -webkit-flex;\n display: -ms-flexbox;\n display: flex;\n -webkit-box-orient: vertical;\n -webkit-box-direction: normal;\n -webkit-flex-direction: column;\n -ms-flex-direction: column;\n flex-direction: column;\n font-size: 16px;\n font-weight: 400;\n min-height: 200px;\n overflow: hidden;\n width: 330px;\n z-index: 1;\n position: relative;\n background: rgb(255,255,255);\n border-radius: 2px;\n box-sizing: border-box; }\n\n.mdl-card__media {\n background-color: rgb(255,64,129);\n background-repeat: repeat;\n background-position: 50% 50%;\n background-size: cover;\n background-origin: padding-box;\n background-attachment: scroll;\n box-sizing: border-box; }\n\n.mdl-card__title {\n -webkit-box-align: center;\n -webkit-align-items: center;\n -ms-flex-align: center;\n align-items: center;\n color: rgb(0,0,0);\n display: block;\n display: -webkit-box;\n display: -webkit-flex;\n display: -ms-flexbox;\n display: flex;\n -webkit-box-pack: stretch;\n -webkit-justify-content: stretch;\n -ms-flex-pack: stretch;\n justify-content: stretch;\n line-height: normal;\n padding: 16px 16px;\n -webkit-perspective-origin: 165px 56px;\n perspective-origin: 165px 56px;\n -webkit-transform-origin: 165px 56px;\n -ms-transform-origin: 165px 56px;\n transform-origin: 165px 56px;\n box-sizing: border-box; }\n .mdl-card__title.mdl-card--border {\n border-bottom: 1px solid rgba(0, 0, 0, 0.1); }\n\n.mdl-card__title-text {\n -webkit-align-self: flex-end;\n -ms-flex-item-align: end;\n align-self: flex-end;\n color: inherit;\n display: block;\n display: -webkit-box;\n display: -webkit-flex;\n display: -ms-flexbox;\n display: flex;\n font-size: 24px;\n font-weight: 300;\n line-height: normal;\n overflow: hidden;\n -webkit-transform-origin: 149px 48px;\n -ms-transform-origin: 149px 48px;\n transform-origin: 149px 48px;\n margin: 0; }\n\n.mdl-card__subtitle-text {\n font-size: 14px;\n color: rgba(0,0,0, 0.54);\n margin: 0; }\n\n.mdl-card__supporting-text {\n color: rgba(0,0,0, 0.54);\n font-size: 13px;\n line-height: 18px;\n overflow: hidden;\n padding: 16px 16px;\n width: 90%; }\n\n.mdl-card__actions {\n font-size: 16px;\n line-height: normal;\n width: 100%;\n background-color: transparent;\n padding: 8px;\n box-sizing: border-box; }\n .mdl-card__actions.mdl-card--border {\n border-top: 1px solid rgba(0, 0, 0, 0.1); }\n\n.mdl-card--expand {\n -webkit-box-flex: 1;\n -webkit-flex-grow: 1;\n -ms-flex-positive: 1;\n flex-grow: 1; }\n\n.mdl-card__menu {\n position: absolute;\n right: 16px;\n top: 16px; }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* Typography */\n/* Shadows */\n/* Animations */\n.mdl-checkbox {\n position: relative;\n z-index: 1;\n vertical-align: middle;\n display: inline-block;\n box-sizing: border-box;\n width: 100%;\n height: 24px;\n margin: 0;\n padding: 0; }\n .mdl-checkbox.is-upgraded {\n padding-left: 24px; }\n\n.mdl-checkbox__input {\n line-height: 24px; }\n .mdl-checkbox.is-upgraded .mdl-checkbox__input {\n position: absolute;\n width: 0;\n height: 0;\n margin: 0;\n padding: 0;\n opacity: 0;\n -ms-appearance: none;\n -moz-appearance: none;\n -webkit-appearance: none;\n appearance: none;\n border: none; }\n\n.mdl-checkbox__box-outline {\n position: absolute;\n top: 3px;\n left: 0;\n display: inline-block;\n box-sizing: border-box;\n width: 16px;\n height: 16px;\n margin: 0;\n cursor: pointer;\n overflow: hidden;\n border: 2px solid rgba(0,0,0, 0.54);\n border-radius: 2px;\n z-index: 2; }\n .mdl-checkbox.is-checked .mdl-checkbox__box-outline {\n border: 2px solid rgb(63,81,181); }\n .mdl-checkbox.is-disabled .mdl-checkbox__box-outline {\n border: 2px solid rgba(0,0,0, 0.26);\n cursor: auto; }\n\n.mdl-checkbox__focus-helper {\n position: absolute;\n top: 3px;\n left: 0;\n display: inline-block;\n box-sizing: border-box;\n width: 16px;\n height: 16px;\n border-radius: 50%;\n background-color: transparent; }\n .mdl-checkbox.is-focused .mdl-checkbox__focus-helper {\n box-shadow: 0 0 0px 8px rgba(0, 0, 0, 0.1);\n background-color: rgba(0, 0, 0, 0.1); }\n .mdl-checkbox.is-focused.is-checked .mdl-checkbox__focus-helper {\n box-shadow: 0 0 0px 8px rgba(63,81,181, 0.26);\n background-color: rgba(63,81,181, 0.26); }\n\n.mdl-checkbox__tick-outline {\n position: absolute;\n top: 0;\n left: 0;\n height: 100%;\n width: 100%;\n -webkit-mask: url(\"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcKICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICB4bWxuczpjYz0iaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbnMjIgogICB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiCiAgIHhtbG5zOnN2Zz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgdmVyc2lvbj0iMS4xIgogICB2aWV3Qm94PSIwIDAgMSAxIgogICBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWluWU1pbiBtZWV0Ij4KICA8ZGVmcz4KICAgIDxjbGlwUGF0aCBpZD0iY2xpcCI+CiAgICAgIDxwYXRoCiAgICAgICAgIGQ9Ik0gMCwwIDAsMSAxLDEgMSwwIDAsMCB6IE0gMC44NTM0Mzc1LDAuMTY3MTg3NSAwLjk1OTY4NzUsMC4yNzMxMjUgMC40MjkzNzUsMC44MDM0Mzc1IDAuMzIzMTI1LDAuOTA5Njg3NSAwLjIxNzE4NzUsMC44MDM0Mzc1IDAuMDQwMzEyNSwwLjYyNjg3NSAwLjE0NjU2MjUsMC41MjA2MjUgMC4zMjMxMjUsMC42OTc1IDAuODUzNDM3NSwwLjE2NzE4NzUgeiIKICAgICAgICAgc3R5bGU9ImZpbGw6I2ZmZmZmZjtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZSIgLz4KICAgIDwvY2xpcFBhdGg+CiAgICA8bWFzayBpZD0ibWFzayIgbWFza1VuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgbWFza0NvbnRlbnRVbml0cz0ib2JqZWN0Qm91bmRpbmdCb3giPgogICAgICA8cGF0aAogICAgICAgICBkPSJNIDAsMCAwLDEgMSwxIDEsMCAwLDAgeiBNIDAuODUzNDM3NSwwLjE2NzE4NzUgMC45NTk2ODc1LDAuMjczMTI1IDAuNDI5Mzc1LDAuODAzNDM3NSAwLjMyMzEyNSwwLjkwOTY4NzUgMC4yMTcxODc1LDAuODAzNDM3NSAwLjA0MDMxMjUsMC42MjY4NzUgMC4xNDY1NjI1LDAuNTIwNjI1IDAuMzIzMTI1LDAuNjk3NSAwLjg1MzQzNzUsMC4xNjcxODc1IHoiCiAgICAgICAgIHN0eWxlPSJmaWxsOiNmZmZmZmY7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmUiIC8+CiAgICA8L21hc2s+CiAgPC9kZWZzPgogIDxyZWN0CiAgICAgd2lkdGg9IjEiCiAgICAgaGVpZ2h0PSIxIgogICAgIHg9IjAiCiAgICAgeT0iMCIKICAgICBjbGlwLXBhdGg9InVybCgjY2xpcCkiCiAgICAgc3R5bGU9ImZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZSIgLz4KPC9zdmc+Cg==\");\n mask: url(\"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcKICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICB4bWxuczpjYz0iaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbnMjIgogICB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiCiAgIHhtbG5zOnN2Zz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgdmVyc2lvbj0iMS4xIgogICB2aWV3Qm94PSIwIDAgMSAxIgogICBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWluWU1pbiBtZWV0Ij4KICA8ZGVmcz4KICAgIDxjbGlwUGF0aCBpZD0iY2xpcCI+CiAgICAgIDxwYXRoCiAgICAgICAgIGQ9Ik0gMCwwIDAsMSAxLDEgMSwwIDAsMCB6IE0gMC44NTM0Mzc1LDAuMTY3MTg3NSAwLjk1OTY4NzUsMC4yNzMxMjUgMC40MjkzNzUsMC44MDM0Mzc1IDAuMzIzMTI1LDAuOTA5Njg3NSAwLjIxNzE4NzUsMC44MDM0Mzc1IDAuMDQwMzEyNSwwLjYyNjg3NSAwLjE0NjU2MjUsMC41MjA2MjUgMC4zMjMxMjUsMC42OTc1IDAuODUzNDM3NSwwLjE2NzE4NzUgeiIKICAgICAgICAgc3R5bGU9ImZpbGw6I2ZmZmZmZjtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZSIgLz4KICAgIDwvY2xpcFBhdGg+CiAgICA8bWFzayBpZD0ibWFzayIgbWFza1VuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgbWFza0NvbnRlbnRVbml0cz0ib2JqZWN0Qm91bmRpbmdCb3giPgogICAgICA8cGF0aAogICAgICAgICBkPSJNIDAsMCAwLDEgMSwxIDEsMCAwLDAgeiBNIDAuODUzNDM3NSwwLjE2NzE4NzUgMC45NTk2ODc1LDAuMjczMTI1IDAuNDI5Mzc1LDAuODAzNDM3NSAwLjMyMzEyNSwwLjkwOTY4NzUgMC4yMTcxODc1LDAuODAzNDM3NSAwLjA0MDMxMjUsMC42MjY4NzUgMC4xNDY1NjI1LDAuNTIwNjI1IDAuMzIzMTI1LDAuNjk3NSAwLjg1MzQzNzUsMC4xNjcxODc1IHoiCiAgICAgICAgIHN0eWxlPSJmaWxsOiNmZmZmZmY7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmUiIC8+CiAgICA8L21hc2s+CiAgPC9kZWZzPgogIDxyZWN0CiAgICAgd2lkdGg9IjEiCiAgICAgaGVpZ2h0PSIxIgogICAgIHg9IjAiCiAgICAgeT0iMCIKICAgICBjbGlwLXBhdGg9InVybCgjY2xpcCkiCiAgICAgc3R5bGU9ImZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZSIgLz4KPC9zdmc+Cg==\");\n background: transparent;\n -webkit-transition-duration: 0.28s;\n transition-duration: 0.28s;\n -webkit-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n -webkit-transition-property: background;\n transition-property: background; }\n .mdl-checkbox.is-checked .mdl-checkbox__tick-outline {\n background: rgb(63,81,181) url(\"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcKICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICB4bWxuczpjYz0iaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbnMjIgogICB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiCiAgIHhtbG5zOnN2Zz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgdmVyc2lvbj0iMS4xIgogICB2aWV3Qm94PSIwIDAgMSAxIgogICBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWluWU1pbiBtZWV0Ij4KICA8cGF0aAogICAgIGQ9Ik0gMC4wNDAzODA1OSwwLjYyNjc3NjcgMC4xNDY0NDY2MSwwLjUyMDcxMDY4IDAuNDI5Mjg5MzIsMC44MDM1NTMzOSAwLjMyMzIyMzMsMC45MDk2MTk0MSB6IE0gMC4yMTcxNTcyOSwwLjgwMzU1MzM5IDAuODUzNTUzMzksMC4xNjcxNTcyOSAwLjk1OTYxOTQxLDAuMjczMjIzMyAwLjMyMzIyMzMsMC45MDk2MTk0MSB6IgogICAgIGlkPSJyZWN0Mzc4MCIKICAgICBzdHlsZT0iZmlsbDojZmZmZmZmO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lIiAvPgo8L3N2Zz4K\"); }\n .mdl-checkbox.is-checked.is-disabled .mdl-checkbox__tick-outline {\n background: rgba(0,0,0, 0.26) url(\"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcKICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICB4bWxuczpjYz0iaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbnMjIgogICB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiCiAgIHhtbG5zOnN2Zz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgdmVyc2lvbj0iMS4xIgogICB2aWV3Qm94PSIwIDAgMSAxIgogICBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWluWU1pbiBtZWV0Ij4KICA8cGF0aAogICAgIGQ9Ik0gMC4wNDAzODA1OSwwLjYyNjc3NjcgMC4xNDY0NDY2MSwwLjUyMDcxMDY4IDAuNDI5Mjg5MzIsMC44MDM1NTMzOSAwLjMyMzIyMzMsMC45MDk2MTk0MSB6IE0gMC4yMTcxNTcyOSwwLjgwMzU1MzM5IDAuODUzNTUzMzksMC4xNjcxNTcyOSAwLjk1OTYxOTQxLDAuMjczMjIzMyAwLjMyMzIyMzMsMC45MDk2MTk0MSB6IgogICAgIGlkPSJyZWN0Mzc4MCIKICAgICBzdHlsZT0iZmlsbDojZmZmZmZmO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lIiAvPgo8L3N2Zz4K\"); }\n\n.mdl-checkbox__label {\n position: relative;\n cursor: pointer;\n font-size: 16px;\n line-height: 24px;\n margin: 0; }\n .mdl-checkbox.is-disabled .mdl-checkbox__label {\n color: rgba(0,0,0, 0.26);\n cursor: auto; }\n\n.mdl-checkbox__ripple-container {\n position: absolute;\n z-index: 2;\n top: -6px;\n left: -10px;\n box-sizing: border-box;\n width: 36px;\n height: 36px;\n border-radius: 50%;\n cursor: pointer;\n overflow: hidden;\n -webkit-mask-image: -webkit-radial-gradient(circle, white, black); }\n .mdl-checkbox__ripple-container .mdl-ripple {\n background: rgb(63,81,181); }\n .mdl-checkbox.is-disabled .mdl-checkbox__ripple-container {\n cursor: auto; }\n .mdl-checkbox.is-disabled .mdl-checkbox__ripple-container .mdl-ripple {\n background: transparent; }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* Typography */\n/* Shadows */\n/* Animations */\n.mdl-data-table {\n position: relative;\n border: 1px solid rgba(0, 0, 0, 0.12);\n border-collapse: collapse;\n white-space: nowrap;\n font-size: 13px;\n background-color: rgb(255,255,255); }\n .mdl-data-table thead {\n padding-bottom: 3px; }\n .mdl-data-table thead .mdl-data-table__select {\n margin-top: 0; }\n .mdl-data-table tbody tr {\n position: relative;\n height: 48px;\n -webkit-transition-duration: 0.28s;\n transition-duration: 0.28s;\n -webkit-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n -webkit-transition-property: background-color;\n transition-property: background-color; }\n .mdl-data-table tbody tr.is-selected {\n background-color: #e0e0e0; }\n .mdl-data-table tbody tr:hover {\n background-color: #eeeeee; }\n .mdl-data-table td, .mdl-data-table th {\n padding: 0 18px 0 18px;\n text-align: right; }\n .mdl-data-table td:first-of-type, .mdl-data-table th:first-of-type {\n padding-left: 24px; }\n .mdl-data-table td:last-of-type, .mdl-data-table th:last-of-type {\n padding-right: 24px; }\n .mdl-data-table td {\n position: relative;\n vertical-align: top;\n height: 48px;\n border-top: 1px solid rgba(0, 0, 0, 0.12);\n border-bottom: 1px solid rgba(0, 0, 0, 0.12);\n padding-top: 12px;\n box-sizing: border-box; }\n .mdl-data-table td .mdl-data-table__select {\n vertical-align: top;\n position: absolute;\n left: 24px; }\n .mdl-data-table th {\n position: relative;\n vertical-align: bottom;\n text-overflow: ellipsis;\n font-size: 14px;\n font-weight: bold;\n line-height: 24px;\n letter-spacing: 0;\n height: 48px;\n font-size: 12px;\n color: rgba(0, 0, 0, 0.54);\n padding-bottom: 8px;\n box-sizing: border-box; }\n .mdl-data-table th .mdl-data-table__select {\n position: absolute;\n bottom: 8px;\n left: 24px; }\n\n.mdl-data-table__select {\n width: 16px; }\n\n.mdl-data-table__cell--non-numeric.mdl-data-table__cell--non-numeric {\n text-align: left; }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* Typography */\n/* Shadows */\n/* Animations */\n.mdl-mega-footer {\n padding: 16px 40px;\n color: rgb(158,158,158);\n background-color: rgb(66,66,66); }\n\n.mdl-mega-footer--top-section:after,\n.mdl-mega-footer--middle-section:after,\n.mdl-mega-footer--bottom-section:after,\n.mdl-mega-footer__top-section:after,\n.mdl-mega-footer__middle-section:after,\n.mdl-mega-footer__bottom-section:after {\n content: '';\n display: block;\n clear: both; }\n\n.mdl-mega-footer--left-section,\n.mdl-mega-footer__left-section {\n margin-bottom: 16px; }\n\n.mdl-mega-footer--right-section,\n.mdl-mega-footer__right-section {\n margin-bottom: 16px; }\n\n.mdl-mega-footer--right-section a,\n.mdl-mega-footer__right-section a {\n display: block;\n margin-bottom: 16px;\n color: inherit;\n text-decoration: none; }\n\n@media screen and (min-width: 760px) {\n .mdl-mega-footer--left-section,\n .mdl-mega-footer__left-section {\n float: left; }\n .mdl-mega-footer--right-section,\n .mdl-mega-footer__right-section {\n float: right; }\n .mdl-mega-footer--right-section a,\n .mdl-mega-footer__right-section a {\n display: inline-block;\n margin-left: 16px;\n line-height: 36px;\n vertical-align: middle; } }\n\n.mdl-mega-footer--social-btn,\n.mdl-mega-footer__social-btn {\n width: 36px;\n height: 36px;\n padding: 0;\n margin: 0;\n background-color: rgb(158,158,158);\n border: none; }\n\n.mdl-mega-footer--drop-down-section,\n.mdl-mega-footer__drop-down-section {\n display: block;\n position: relative; }\n\n@media screen and (min-width: 760px) {\n .mdl-mega-footer--drop-down-section,\n .mdl-mega-footer__drop-down-section {\n width: 33%; }\n .mdl-mega-footer--drop-down-section:nth-child(1),\n .mdl-mega-footer--drop-down-section:nth-child(2),\n .mdl-mega-footer__drop-down-section:nth-child(1),\n .mdl-mega-footer__drop-down-section:nth-child(2) {\n float: left; }\n .mdl-mega-footer--drop-down-section:nth-child(3),\n .mdl-mega-footer__drop-down-section:nth-child(3) {\n float: right; }\n .mdl-mega-footer--drop-down-section:nth-child(3):after,\n .mdl-mega-footer__drop-down-section:nth-child(3):after {\n clear: right; }\n .mdl-mega-footer--drop-down-section:nth-child(4),\n .mdl-mega-footer__drop-down-section:nth-child(4) {\n clear: right;\n float: right; }\n .mdl-mega-footer--middle-section:after,\n .mdl-mega-footer__middle-section:after {\n content: '';\n display: block;\n clear: both; }\n .mdl-mega-footer--bottom-section,\n .mdl-mega-footer__bottom-section {\n padding-top: 0; } }\n\n@media screen and (min-width: 1024px) {\n .mdl-mega-footer--drop-down-section,\n .mdl-mega-footer--drop-down-section:nth-child(3),\n .mdl-mega-footer--drop-down-section:nth-child(4),\n .mdl-mega-footer__drop-down-section,\n .mdl-mega-footer__drop-down-section:nth-child(3),\n .mdl-mega-footer__drop-down-section:nth-child(4) {\n width: 24%;\n float: left; } }\n\n.mdl-mega-footer--heading-checkbox,\n.mdl-mega-footer__heading-checkbox {\n position: absolute;\n width: 100%;\n height: 55.8px;\n padding: 32px;\n margin: 0;\n margin-top: -16px;\n cursor: pointer;\n z-index: 1;\n opacity: 0; }\n .mdl-mega-footer--heading-checkbox + .mdl-mega-footer--heading:after,\n .mdl-mega-footer--heading-checkbox + .mdl-mega-footer__heading:after,\n .mdl-mega-footer__heading-checkbox + .mdl-mega-footer--heading:after,\n .mdl-mega-footer__heading-checkbox + .mdl-mega-footer__heading:after {\n font-family: 'Material Icons';\n content: '\\E5CE'; }\n\n.mdl-mega-footer--heading-checkbox:checked ~ .mdl-mega-footer--link-list,\n.mdl-mega-footer--heading-checkbox:checked ~ .mdl-mega-footer__link-list,\n.mdl-mega-footer--heading-checkbox:checked + .mdl-mega-footer--heading + .mdl-mega-footer--link-list,\n.mdl-mega-footer--heading-checkbox:checked + .mdl-mega-footer__heading + .mdl-mega-footer__link-list,\n.mdl-mega-footer__heading-checkbox:checked ~ .mdl-mega-footer--link-list,\n.mdl-mega-footer__heading-checkbox:checked ~ .mdl-mega-footer__link-list,\n.mdl-mega-footer__heading-checkbox:checked + .mdl-mega-footer--heading + .mdl-mega-footer--link-list,\n.mdl-mega-footer__heading-checkbox:checked + .mdl-mega-footer__heading + .mdl-mega-footer__link-list {\n display: none; }\n\n.mdl-mega-footer--heading-checkbox:checked + .mdl-mega-footer--heading:after,\n.mdl-mega-footer--heading-checkbox:checked + .mdl-mega-footer__heading:after,\n.mdl-mega-footer__heading-checkbox:checked + .mdl-mega-footer--heading:after,\n.mdl-mega-footer__heading-checkbox:checked + .mdl-mega-footer__heading:after {\n font-family: 'Material Icons';\n content: '\\E5CF'; }\n\n.mdl-mega-footer--heading,\n.mdl-mega-footer__heading {\n position: relative;\n width: 100%;\n padding-right: 39.8px;\n margin-bottom: 16px;\n box-sizing: border-box;\n font-size: 14px;\n line-height: 23.8px;\n font-weight: 500;\n white-space: nowrap;\n text-overflow: ellipsis;\n overflow: hidden;\n color: rgb(224,224,224); }\n\n.mdl-mega-footer--heading:after,\n.mdl-mega-footer__heading:after {\n content: '';\n position: absolute;\n top: 0;\n right: 0;\n display: block;\n width: 23.8px;\n height: 23.8px;\n background-size: cover; }\n\n.mdl-mega-footer--link-list,\n.mdl-mega-footer__link-list {\n list-style: none;\n margin: 0;\n padding: 0;\n margin-bottom: 32px; }\n .mdl-mega-footer--link-list:after,\n .mdl-mega-footer__link-list:after {\n clear: both;\n display: block;\n content: ''; }\n\n.mdl-mega-footer--link-list li,\n.mdl-mega-footer__link-list li {\n font-size: 14px;\n font-weight: 400;\n line-height: 24px;\n letter-spacing: 0;\n line-height: 20px; }\n\n.mdl-mega-footer--link-list a,\n.mdl-mega-footer__link-list a {\n color: inherit;\n text-decoration: none;\n white-space: nowrap; }\n\n@media screen and (min-width: 760px) {\n .mdl-mega-footer--heading-checkbox,\n .mdl-mega-footer__heading-checkbox {\n display: none; }\n .mdl-mega-footer--heading-checkbox + .mdl-mega-footer--heading:after,\n .mdl-mega-footer--heading-checkbox + .mdl-mega-footer__heading:after,\n .mdl-mega-footer__heading-checkbox + .mdl-mega-footer--heading:after,\n .mdl-mega-footer__heading-checkbox + .mdl-mega-footer__heading:after {\n background-image: none; }\n .mdl-mega-footer--heading-checkbox:checked ~ .mdl-mega-footer--link-list,\n .mdl-mega-footer--heading-checkbox:checked ~ .mdl-mega-footer__link-list,\n .mdl-mega-footer--heading-checkbox:checked + .mdl-mega-footer__heading + .mdl-mega-footer__link-list,\n .mdl-mega-footer--heading-checkbox:checked + .mdl-mega-footer--heading + .mdl-mega-footer--link-list,\n .mdl-mega-footer__heading-checkbox:checked ~ .mdl-mega-footer--link-list,\n .mdl-mega-footer__heading-checkbox:checked ~ .mdl-mega-footer__link-list,\n .mdl-mega-footer__heading-checkbox:checked + .mdl-mega-footer__heading + .mdl-mega-footer__link-list,\n .mdl-mega-footer__heading-checkbox:checked + .mdl-mega-footer--heading + .mdl-mega-footer--link-list {\n display: block; }\n .mdl-mega-footer--heading-checkbox:checked + .mdl-mega-footer--heading:after,\n .mdl-mega-footer--heading-checkbox:checked + .mdl-mega-footer__heading:after,\n .mdl-mega-footer__heading-checkbox:checked + .mdl-mega-footer--heading:after,\n .mdl-mega-footer__heading-checkbox:checked + .mdl-mega-footer__heading:after {\n content: ''; } }\n\n.mdl-mega-footer--bottom-section,\n.mdl-mega-footer__bottom-section {\n padding-top: 16px;\n margin-bottom: 16px; }\n\n.mdl-logo {\n margin-bottom: 16px;\n color: white; }\n\n.mdl-mega-footer--bottom-section .mdl-mega-footer--link-list li,\n.mdl-mega-footer__bottom-section .mdl-mega-footer__link-list li {\n float: left;\n margin-bottom: 0;\n margin-right: 16px; }\n\n@media screen and (min-width: 760px) {\n .mdl-logo {\n float: left;\n margin-bottom: 0;\n margin-right: 16px; } }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n.mdl-mini-footer {\n display: -webkit-box;\n display: -webkit-flex;\n display: -ms-flexbox;\n display: flex;\n -webkit-flex-flow: row wrap;\n -ms-flex-flow: row wrap;\n flex-flow: row wrap;\n -webkit-box-pack: justify;\n -webkit-justify-content: space-between;\n -ms-flex-pack: justify;\n justify-content: space-between;\n padding: 32px 16px;\n color: rgb(158,158,158);\n background-color: rgb(66,66,66); }\n .mdl-mini-footer:after {\n content: '';\n display: block; }\n .mdl-mini-footer .mdl-logo {\n line-height: 36px; }\n\n.mdl-mini-footer--link-list,\n.mdl-mini-footer__link-list {\n display: -webkit-box;\n display: -webkit-flex;\n display: -ms-flexbox;\n display: flex;\n -webkit-flex-flow: row nowrap;\n -ms-flex-flow: row nowrap;\n flex-flow: row nowrap;\n list-style: none;\n margin: 0;\n padding: 0; }\n .mdl-mini-footer--link-list li,\n .mdl-mini-footer__link-list li {\n margin-bottom: 0;\n margin-right: 16px; }\n @media screen and (min-width: 760px) {\n .mdl-mini-footer--link-list li,\n .mdl-mini-footer__link-list li {\n line-height: 36px; } }\n .mdl-mini-footer--link-list a,\n .mdl-mini-footer__link-list a {\n color: inherit;\n text-decoration: none;\n white-space: nowrap; }\n\n.mdl-mini-footer--left-section,\n.mdl-mini-footer__left-section {\n display: inline-block;\n -webkit-box-ordinal-group: 1;\n -webkit-order: 0;\n -ms-flex-order: 0;\n order: 0; }\n\n.mdl-mini-footer--right-section,\n.mdl-mini-footer__right-section {\n display: inline-block;\n -webkit-box-ordinal-group: 2;\n -webkit-order: 1;\n -ms-flex-order: 1;\n order: 1; }\n\n.mdl-mini-footer--social-btn,\n.mdl-mini-footer__social-btn {\n width: 36px;\n height: 36px;\n padding: 0;\n margin: 0;\n background-color: rgb(158,158,158);\n border: none; }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n.mdl-icon-toggle {\n position: relative;\n z-index: 1;\n vertical-align: middle;\n display: inline-block;\n height: 32px;\n margin: 0;\n padding: 0; }\n\n.mdl-icon-toggle__input {\n line-height: 32px; }\n .mdl-icon-toggle.is-upgraded .mdl-icon-toggle__input {\n position: absolute;\n width: 0;\n height: 0;\n margin: 0;\n padding: 0;\n opacity: 0;\n -ms-appearance: none;\n -moz-appearance: none;\n -webkit-appearance: none;\n appearance: none;\n border: none; }\n\n.mdl-icon-toggle__label {\n display: inline-block;\n position: relative;\n cursor: pointer;\n height: 32px;\n width: 32px;\n min-width: 32px;\n color: rgb(97,97,97);\n border-radius: 50%;\n padding: 0;\n margin-left: 0;\n margin-right: 0;\n text-align: center;\n background-color: transparent;\n will-change: background-color;\n -webkit-transition: background-color 0.2s cubic-bezier(0.4, 0, 0.2, 1), color 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n transition: background-color 0.2s cubic-bezier(0.4, 0, 0.2, 1), color 0.2s cubic-bezier(0.4, 0, 0.2, 1); }\n .mdl-icon-toggle__label.material-icons {\n line-height: 32px;\n font-size: 24px; }\n .mdl-icon-toggle.is-checked .mdl-icon-toggle__label {\n color: rgb(63,81,181); }\n .mdl-icon-toggle.is-disabled .mdl-icon-toggle__label {\n color: rgba(0,0,0, 0.26);\n cursor: auto;\n -webkit-transition: none;\n transition: none; }\n .mdl-icon-toggle.is-focused .mdl-icon-toggle__label {\n background-color: rgba(0,0,0, 0.12); }\n .mdl-icon-toggle.is-focused.is-checked .mdl-icon-toggle__label {\n background-color: rgba(63,81,181, 0.26); }\n\n.mdl-icon-toggle__ripple-container {\n position: absolute;\n z-index: 2;\n top: -2px;\n left: -2px;\n box-sizing: border-box;\n width: 36px;\n height: 36px;\n border-radius: 50%;\n cursor: pointer;\n overflow: hidden;\n -webkit-mask-image: -webkit-radial-gradient(circle, white, black); }\n .mdl-icon-toggle__ripple-container .mdl-ripple {\n background: rgb(97,97,97); }\n .mdl-icon-toggle.is-disabled .mdl-icon-toggle__ripple-container {\n cursor: auto; }\n .mdl-icon-toggle.is-disabled .mdl-icon-toggle__ripple-container .mdl-ripple {\n background: transparent; }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* Typography */\n/* Shadows */\n/* Animations */\n.mdl-menu__container {\n display: block;\n margin: 0;\n padding: 0;\n border: none;\n position: absolute;\n overflow: visible;\n height: 0;\n width: 0;\n visibility: hidden;\n z-index: -1; }\n .mdl-menu__container.is-visible, .mdl-menu__container.is-animating {\n z-index: 999;\n visibility: visible; }\n\n.mdl-menu__outline {\n display: block;\n background: rgb(255,255,255);\n margin: 0;\n padding: 0;\n border: none;\n border-radius: 2px;\n position: absolute;\n top: 0;\n left: 0;\n overflow: hidden;\n opacity: 0;\n -webkit-transform: scale(0);\n -ms-transform: scale(0);\n transform: scale(0);\n -webkit-transform-origin: 0 0;\n -ms-transform-origin: 0 0;\n transform-origin: 0 0;\n box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12);\n will-change: transform;\n -webkit-transition: -webkit-transform 0.3s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n z-index: -1; }\n .mdl-menu__container.is-visible .mdl-menu__outline {\n opacity: 1;\n -webkit-transform: scale(1);\n -ms-transform: scale(1);\n transform: scale(1);\n z-index: 999; }\n .mdl-menu__outline.mdl-menu--bottom-right {\n -webkit-transform-origin: 100% 0;\n -ms-transform-origin: 100% 0;\n transform-origin: 100% 0; }\n .mdl-menu__outline.mdl-menu--top-left {\n -webkit-transform-origin: 0 100%;\n -ms-transform-origin: 0 100%;\n transform-origin: 0 100%; }\n .mdl-menu__outline.mdl-menu--top-right {\n -webkit-transform-origin: 100% 100%;\n -ms-transform-origin: 100% 100%;\n transform-origin: 100% 100%; }\n\n.mdl-menu {\n position: absolute;\n list-style: none;\n top: 0;\n left: 0;\n height: auto;\n width: auto;\n min-width: 124px;\n padding: 8px 0;\n margin: 0;\n opacity: 0;\n clip: rect(0 0 0 0);\n z-index: -1; }\n .mdl-menu__container.is-visible .mdl-menu {\n opacity: 1;\n z-index: 999; }\n .mdl-menu.is-animating {\n -webkit-transition: opacity 0.2s cubic-bezier(0.4, 0, 0.2, 1), clip 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n transition: opacity 0.2s cubic-bezier(0.4, 0, 0.2, 1), clip 0.3s cubic-bezier(0.4, 0, 0.2, 1); }\n .mdl-menu.mdl-menu--bottom-right {\n left: auto;\n right: 0; }\n .mdl-menu.mdl-menu--top-left {\n top: auto;\n bottom: 0; }\n .mdl-menu.mdl-menu--top-right {\n top: auto;\n left: auto;\n bottom: 0;\n right: 0; }\n .mdl-menu.mdl-menu--unaligned {\n top: auto;\n left: auto; }\n\n.mdl-menu__item {\n display: block;\n border: none;\n color: rgba(0,0,0, 0.87);\n background-color: transparent;\n text-align: left;\n margin: 0;\n padding: 0 16px;\n outline-color: rgb(189,189,189);\n position: relative;\n overflow: hidden;\n font-size: 14px;\n font-weight: 400;\n line-height: 24px;\n letter-spacing: 0;\n text-decoration: none;\n cursor: pointer;\n height: 48px;\n line-height: 48px;\n white-space: nowrap;\n opacity: 0;\n -webkit-transition: opacity 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n transition: opacity 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n -webkit-user-select: none;\n -moz-user-select: none;\n -ms-user-select: none;\n user-select: none; }\n .mdl-menu__container.is-visible .mdl-menu__item {\n opacity: 1; }\n .mdl-menu__item::-moz-focus-inner {\n border: 0; }\n .mdl-menu__item[disabled] {\n color: rgb(189,189,189);\n background-color: transparent;\n cursor: auto; }\n .mdl-menu__item[disabled]:hover {\n background-color: transparent; }\n .mdl-menu__item[disabled]:focus {\n background-color: transparent; }\n .mdl-menu__item[disabled] .mdl-ripple {\n background: transparent; }\n .mdl-menu__item:hover {\n background-color: rgb(238,238,238); }\n .mdl-menu__item:focus {\n outline: none;\n background-color: rgb(238,238,238); }\n .mdl-menu__item:active {\n background-color: rgb(224,224,224); }\n\n.mdl-menu__item--ripple-container {\n display: block;\n height: 100%;\n left: 0px;\n position: absolute;\n top: 0px;\n width: 100%;\n z-index: 0;\n overflow: hidden; }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n.mdl-progress {\n display: block;\n position: relative;\n height: 4px;\n width: 500px; }\n\n.mdl-progress > .bar {\n display: block;\n position: absolute;\n top: 0;\n bottom: 0;\n width: 0%;\n -webkit-transition: width 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n transition: width 0.2s cubic-bezier(0.4, 0, 0.2, 1); }\n\n.mdl-progress > .progressbar {\n background-color: rgb(63,81,181);\n z-index: 1;\n left: 0; }\n\n.mdl-progress > .bufferbar {\n background-image: -webkit-linear-gradient(left, rgba(255,255,255, 0.7), rgba(255,255,255, 0.7)), -webkit-linear-gradient(left, rgb(63,81,181), rgb(63,81,181));\n background-image: linear-gradient(to right, rgba(255,255,255, 0.7), rgba(255,255,255, 0.7)), linear-gradient(to right, rgb(63,81,181), rgb(63,81,181));\n z-index: 0;\n left: 0; }\n\n.mdl-progress > .auxbar {\n right: 0; }\n\n@supports (-webkit-appearance: none) {\n .mdl-progress:not(.mdl-progress__indeterminate):not(.mdl-progress__indeterminate) > .auxbar {\n background-image: -webkit-linear-gradient(left, rgba(255,255,255, 0.7), rgba(255,255,255, 0.7)), -webkit-linear-gradient(left, rgb(63,81,181), rgb(63,81,181));\n background-image: linear-gradient(to right, rgba(255,255,255, 0.7), rgba(255,255,255, 0.7)), linear-gradient(to right, rgb(63,81,181), rgb(63,81,181));\n -webkit-mask: url(\"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIj8+Cjxzdmcgd2lkdGg9IjEyIiBoZWlnaHQ9IjQiIHZpZXdQb3J0PSIwIDAgMTIgNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgogIDxlbGxpcHNlIGN4PSIyIiBjeT0iMiIgcng9IjIiIHJ5PSIyIj4KICAgIDxhbmltYXRlIGF0dHJpYnV0ZU5hbWU9ImN4IiBmcm9tPSIyIiB0bz0iLTEwIiBkdXI9IjAuNnMiIHJlcGVhdENvdW50PSJpbmRlZmluaXRlIiAvPgogIDwvZWxsaXBzZT4KICA8ZWxsaXBzZSBjeD0iMTQiIGN5PSIyIiByeD0iMiIgcnk9IjIiIGNsYXNzPSJsb2FkZXIiPgogICAgPGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iY3giIGZyb209IjE0IiB0bz0iMiIgZHVyPSIwLjZzIiByZXBlYXRDb3VudD0iaW5kZWZpbml0ZSIgLz4KICA8L2VsbGlwc2U+Cjwvc3ZnPgo=\");\n mask: url(\"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIj8+Cjxzdmcgd2lkdGg9IjEyIiBoZWlnaHQ9IjQiIHZpZXdQb3J0PSIwIDAgMTIgNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgogIDxlbGxpcHNlIGN4PSIyIiBjeT0iMiIgcng9IjIiIHJ5PSIyIj4KICAgIDxhbmltYXRlIGF0dHJpYnV0ZU5hbWU9ImN4IiBmcm9tPSIyIiB0bz0iLTEwIiBkdXI9IjAuNnMiIHJlcGVhdENvdW50PSJpbmRlZmluaXRlIiAvPgogIDwvZWxsaXBzZT4KICA8ZWxsaXBzZSBjeD0iMTQiIGN5PSIyIiByeD0iMiIgcnk9IjIiIGNsYXNzPSJsb2FkZXIiPgogICAgPGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iY3giIGZyb209IjE0IiB0bz0iMiIgZHVyPSIwLjZzIiByZXBlYXRDb3VudD0iaW5kZWZpbml0ZSIgLz4KICA8L2VsbGlwc2U+Cjwvc3ZnPgo=\"); } }\n\n.mdl-progress:not(.mdl-progress__indeterminate) > .auxbar {\n background-image: -webkit-linear-gradient(left, rgba(255,255,255, 0.9), rgba(255,255,255, 0.9)), -webkit-linear-gradient(left, rgb(63,81,181), rgb(63,81,181));\n background-image: linear-gradient(to right, rgba(255,255,255, 0.9), rgba(255,255,255, 0.9)), linear-gradient(to right, rgb(63,81,181), rgb(63,81,181)); }\n\n.mdl-progress.mdl-progress__indeterminate > .bar1 {\n background-color: rgb(63,81,181);\n -webkit-animation-name: indeterminate1;\n animation-name: indeterminate1;\n -webkit-animation-duration: 2s;\n animation-duration: 2s;\n -webkit-animation-iteration-count: infinite;\n animation-iteration-count: infinite;\n -webkit-animation-timing-function: linear;\n animation-timing-function: linear; }\n\n.mdl-progress.mdl-progress__indeterminate > .bar3 {\n background-image: none;\n background-color: rgb(63,81,181);\n -webkit-animation-name: indeterminate2;\n animation-name: indeterminate2;\n -webkit-animation-duration: 2s;\n animation-duration: 2s;\n -webkit-animation-iteration-count: infinite;\n animation-iteration-count: infinite;\n -webkit-animation-timing-function: linear;\n animation-timing-function: linear; }\n\n@-webkit-keyframes indeterminate1 {\n 0% {\n left: 0%;\n width: 0%; }\n 50% {\n left: 25%;\n width: 75%; }\n 75% {\n left: 100%;\n width: 0%; } }\n\n@keyframes indeterminate1 {\n 0% {\n left: 0%;\n width: 0%; }\n 50% {\n left: 25%;\n width: 75%; }\n 75% {\n left: 100%;\n width: 0%; } }\n\n@-webkit-keyframes indeterminate2 {\n 0% {\n left: 0%;\n width: 0%; }\n 50% {\n left: 0%;\n width: 0%; }\n 75% {\n left: 0%;\n width: 25%; }\n 100% {\n left: 100%;\n width: 0%; } }\n\n@keyframes indeterminate2 {\n 0% {\n left: 0%;\n width: 0%; }\n 50% {\n left: 0%;\n width: 0%; }\n 75% {\n left: 0%;\n width: 25%; }\n 100% {\n left: 100%;\n width: 0%; } }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* Typography */\n/* Shadows */\n/* Animations */\n.mdl-navigation {\n display: -webkit-box;\n display: -webkit-flex;\n display: -ms-flexbox;\n display: flex;\n -webkit-flex-wrap: nowrap;\n -ms-flex-wrap: nowrap;\n flex-wrap: nowrap;\n box-sizing: border-box; }\n\n.mdl-navigation__link {\n color: rgb(66,66,66);\n text-decoration: none;\n font-weight: 500;\n font-size: 13px;\n margin: 0; }\n\n.mdl-layout {\n width: 100%;\n height: 100%;\n display: -webkit-box;\n display: -webkit-flex;\n display: -ms-flexbox;\n display: flex;\n -webkit-box-orient: vertical;\n -webkit-box-direction: normal;\n -webkit-flex-direction: column;\n -ms-flex-direction: column;\n flex-direction: column;\n overflow-y: auto;\n overflow-x: hidden;\n position: relative;\n -webkit-overflow-scrolling: touch; }\n\n.mdl-layout.is-small-screen .mdl-layout--large-screen-only {\n display: none; }\n\n.mdl-layout:not(.is-small-screen) .mdl-layout--small-screen-only {\n display: none; }\n\n.mdl-layout__container {\n position: absolute;\n width: 100%;\n height: 100%; }\n\n.mdl-layout__title,\n.mdl-layout-title {\n display: block;\n position: relative;\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 20px;\n font-weight: 500;\n line-height: 1;\n letter-spacing: 0.02em;\n font-weight: 400;\n box-sizing: border-box; }\n\n.mdl-layout-spacer {\n -webkit-box-flex: 1;\n -webkit-flex-grow: 1;\n -ms-flex-positive: 1;\n flex-grow: 1; }\n\n.mdl-layout__drawer {\n display: -webkit-box;\n display: -webkit-flex;\n display: -ms-flexbox;\n display: flex;\n -webkit-box-orient: vertical;\n -webkit-box-direction: normal;\n -webkit-flex-direction: column;\n -ms-flex-direction: column;\n flex-direction: column;\n -webkit-flex-wrap: nowrap;\n -ms-flex-wrap: nowrap;\n flex-wrap: nowrap;\n width: 240px;\n height: 100%;\n max-height: 100%;\n position: absolute;\n top: 0;\n left: 0;\n box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12);\n box-sizing: border-box;\n border-right: 1px solid rgb(224,224,224);\n background: rgb(250,250,250);\n -webkit-transform: translateX(-250px);\n -ms-transform: translateX(-250px);\n transform: translateX(-250px);\n -webkit-transform-style: preserve-3d;\n transform-style: preserve-3d;\n will-change: transform;\n -webkit-transition-duration: 0.2s;\n transition-duration: 0.2s;\n -webkit-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n -webkit-transition-property: -webkit-transform;\n transition-property: transform;\n color: rgb(66,66,66);\n overflow: visible;\n overflow-y: auto;\n z-index: 5; }\n .mdl-layout__drawer.is-visible {\n -webkit-transform: translateX(0);\n -ms-transform: translateX(0);\n transform: translateX(0); }\n .mdl-layout__drawer.is-visible ~ .mdl-layout__content.mdl-layout__content {\n overflow: hidden; }\n .mdl-layout__drawer > * {\n -webkit-flex-shrink: 0;\n -ms-flex-negative: 0;\n flex-shrink: 0; }\n .mdl-layout__drawer > .mdl-layout__title,\n .mdl-layout__drawer > .mdl-layout-title {\n line-height: 64px;\n padding-left: 40px; }\n @media screen and (max-width: 1024px) {\n .mdl-layout__drawer > .mdl-layout__title,\n .mdl-layout__drawer > .mdl-layout-title {\n line-height: 56px;\n padding-left: 16px; } }\n .mdl-layout__drawer .mdl-navigation {\n -webkit-box-orient: vertical;\n -webkit-box-direction: normal;\n -webkit-flex-direction: column;\n -ms-flex-direction: column;\n flex-direction: column;\n -webkit-box-align: stretch;\n -webkit-align-items: stretch;\n -ms-flex-align: stretch;\n align-items: stretch;\n padding-top: 16px; }\n .mdl-layout__drawer .mdl-navigation .mdl-navigation__link {\n display: block;\n -webkit-flex-shrink: 0;\n -ms-flex-negative: 0;\n flex-shrink: 0;\n padding: 16px 40px;\n margin: 0;\n color: #757575; }\n @media screen and (max-width: 1024px) {\n .mdl-layout__drawer .mdl-navigation .mdl-navigation__link {\n padding: 16px 16px; } }\n .mdl-layout__drawer .mdl-navigation .mdl-navigation__link:hover {\n background-color: rgb(224,224,224); }\n .mdl-layout__drawer .mdl-navigation .mdl-navigation__link--current {\n background-color: rgb(0,0,0);\n color: rgb(224,224,224); }\n @media screen and (min-width: 1025px) {\n .mdl-layout--fixed-drawer > .mdl-layout__drawer {\n -webkit-transform: translateX(0);\n -ms-transform: translateX(0);\n transform: translateX(0); } }\n\n.mdl-layout__drawer-button {\n display: block;\n position: absolute;\n height: 48px;\n width: 48px;\n border: 0;\n -webkit-flex-shrink: 0;\n -ms-flex-negative: 0;\n flex-shrink: 0;\n overflow: hidden;\n text-align: center;\n cursor: pointer;\n font-size: 26px;\n line-height: 50px;\n font-family: Helvetica, Arial, sans-serif;\n margin: 10px 12px;\n top: 0;\n left: 0;\n color: rgb(255,255,255);\n z-index: 4; }\n .mdl-layout__header .mdl-layout__drawer-button {\n position: absolute;\n color: rgb(255,255,255);\n background-color: inherit; }\n @media screen and (max-width: 1024px) {\n .mdl-layout__header .mdl-layout__drawer-button {\n margin: 4px; } }\n @media screen and (max-width: 1024px) {\n .mdl-layout__drawer-button {\n margin: 4px;\n color: rgba(0, 0, 0, 0.5); } }\n @media screen and (min-width: 1025px) {\n .mdl-layout--fixed-drawer > .mdl-layout__drawer-button {\n display: none; } }\n\n.mdl-layout__header {\n display: -webkit-box;\n display: -webkit-flex;\n display: -ms-flexbox;\n display: flex;\n -webkit-box-orient: vertical;\n -webkit-box-direction: normal;\n -webkit-flex-direction: column;\n -ms-flex-direction: column;\n flex-direction: column;\n -webkit-flex-wrap: nowrap;\n -ms-flex-wrap: nowrap;\n flex-wrap: nowrap;\n -webkit-box-pack: start;\n -webkit-justify-content: flex-start;\n -ms-flex-pack: start;\n justify-content: flex-start;\n box-sizing: border-box;\n -webkit-flex-shrink: 0;\n -ms-flex-negative: 0;\n flex-shrink: 0;\n width: 100%;\n margin: 0;\n padding: 0;\n border: none;\n min-height: 64px;\n max-height: 1000px;\n z-index: 3;\n background-color: rgb(63,81,181);\n color: rgb(255,255,255);\n box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12);\n -webkit-transition-duration: 0.2s;\n transition-duration: 0.2s;\n -webkit-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n -webkit-transition-property: max-height, box-shadow;\n transition-property: max-height, box-shadow; }\n @media screen and (max-width: 1024px) {\n .mdl-layout__header {\n min-height: 56px; } }\n .mdl-layout--fixed-drawer.is-upgraded:not(.is-small-screen) > .mdl-layout__header {\n margin-left: 240px;\n width: calc(100% - 240px); }\n @media screen and (min-width: 1025px) {\n .mdl-layout--fixed-drawer > .mdl-layout__header .mdl-layout__header-row {\n padding-left: 40px; } }\n .mdl-layout__header > .mdl-layout-icon {\n position: absolute;\n left: 40px;\n top: 16px;\n height: 32px;\n width: 32px;\n overflow: hidden;\n z-index: 3;\n display: block; }\n @media screen and (max-width: 1024px) {\n .mdl-layout__header > .mdl-layout-icon {\n left: 16px;\n top: 12px; } }\n .mdl-layout.has-drawer .mdl-layout__header > .mdl-layout-icon {\n display: none; }\n .mdl-layout__header.is-compact {\n max-height: 64px; }\n @media screen and (max-width: 1024px) {\n .mdl-layout__header.is-compact {\n max-height: 56px; } }\n .mdl-layout__header.is-compact.has-tabs {\n height: 112px; }\n @media screen and (max-width: 1024px) {\n .mdl-layout__header.is-compact.has-tabs {\n min-height: 104px; } }\n @media screen and (max-width: 1024px) {\n .mdl-layout__header {\n display: none; }\n .mdl-layout--fixed-header > .mdl-layout__header {\n display: -webkit-box;\n display: -webkit-flex;\n display: -ms-flexbox;\n display: flex; } }\n\n.mdl-layout__header--transparent.mdl-layout__header--transparent {\n background-color: transparent;\n box-shadow: none; }\n\n.mdl-layout__header--seamed {\n box-shadow: none; }\n\n.mdl-layout__header--scroll {\n box-shadow: none; }\n\n.mdl-layout__header--waterfall {\n box-shadow: none;\n overflow: hidden; }\n .mdl-layout__header--waterfall.is-casting-shadow {\n box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12); }\n\n.mdl-layout__header-row {\n display: -webkit-box;\n display: -webkit-flex;\n display: -ms-flexbox;\n display: flex;\n -webkit-box-orient: horizontal;\n -webkit-box-direction: normal;\n -webkit-flex-direction: row;\n -ms-flex-direction: row;\n flex-direction: row;\n -webkit-flex-wrap: nowrap;\n -ms-flex-wrap: nowrap;\n flex-wrap: nowrap;\n -webkit-flex-shrink: 0;\n -ms-flex-negative: 0;\n flex-shrink: 0;\n box-sizing: border-box;\n -webkit-align-self: stretch;\n -ms-flex-item-align: stretch;\n align-self: stretch;\n -webkit-box-align: center;\n -webkit-align-items: center;\n -ms-flex-align: center;\n align-items: center;\n height: 64px;\n margin: 0;\n padding: 0 40px 0 80px; }\n @media screen and (max-width: 1024px) {\n .mdl-layout__header-row {\n height: 56px;\n padding: 0 16px 0 72px; } }\n .mdl-layout__header-row > * {\n -webkit-flex-shrink: 0;\n -ms-flex-negative: 0;\n flex-shrink: 0; }\n .mdl-layout__header--scroll .mdl-layout__header-row {\n width: 100%; }\n .mdl-layout__header-row .mdl-navigation {\n margin: 0;\n padding: 0;\n height: 64px;\n -webkit-box-orient: horizontal;\n -webkit-box-direction: normal;\n -webkit-flex-direction: row;\n -ms-flex-direction: row;\n flex-direction: row;\n -webkit-box-align: center;\n -webkit-align-items: center;\n -ms-flex-align: center;\n align-items: center; }\n @media screen and (max-width: 1024px) {\n .mdl-layout__header-row .mdl-navigation {\n height: 56px; } }\n .mdl-layout__header-row .mdl-navigation__link {\n display: block;\n color: rgb(255,255,255);\n line-height: 64px;\n padding: 0 24px; }\n @media screen and (max-width: 1024px) {\n .mdl-layout__header-row .mdl-navigation__link {\n line-height: 56px;\n padding: 0 16px; } }\n\n.mdl-layout__obfuscator {\n background-color: transparent;\n position: absolute;\n top: 0;\n left: 0;\n height: 100%;\n width: 100%;\n z-index: 4;\n visibility: hidden;\n -webkit-transition-property: background-color;\n transition-property: background-color;\n -webkit-transition-duration: 0.2s;\n transition-duration: 0.2s;\n -webkit-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); }\n .mdl-layout__obfuscator.is-visible {\n background-color: rgba(0, 0, 0, 0.5);\n visibility: visible; }\n\n.mdl-layout__content {\n -ms-flex: 0 1 auto;\n display: inline-block;\n overflow-y: auto;\n overflow-x: hidden;\n -webkit-box-flex: 1;\n -webkit-flex-grow: 1;\n -ms-flex-positive: 1;\n flex-grow: 1;\n z-index: 1;\n -webkit-overflow-scrolling: touch; }\n .mdl-layout--fixed-drawer > .mdl-layout__content {\n margin-left: 240px; }\n .mdl-layout__container.has-scrolling-header .mdl-layout__content {\n overflow: visible; }\n @media screen and (max-width: 1024px) {\n .mdl-layout--fixed-drawer > .mdl-layout__content {\n margin-left: 0; }\n .mdl-layout__container.has-scrolling-header .mdl-layout__content {\n overflow-y: auto;\n overflow-x: hidden; } }\n\n.mdl-layout__tab-bar {\n height: 96px;\n margin: 0;\n width: calc(100% - 112px);\n padding: 0 0 0 56px;\n display: -webkit-box;\n display: -webkit-flex;\n display: -ms-flexbox;\n display: flex;\n background-color: rgb(63,81,181);\n overflow-y: hidden;\n overflow-x: scroll; }\n .mdl-layout__tab-bar::-webkit-scrollbar {\n display: none; }\n @media screen and (max-width: 1024px) {\n .mdl-layout__tab-bar {\n width: calc(100% - 60px);\n padding: 0 0 0 60px; } }\n .mdl-layout--fixed-tabs .mdl-layout__tab-bar {\n padding: 0;\n overflow: hidden;\n width: 100%; }\n\n.mdl-layout__tab-bar-container {\n position: relative;\n height: 48px;\n width: 100%;\n border: none;\n margin: 0;\n z-index: 2;\n -webkit-box-flex: 0;\n -webkit-flex-grow: 0;\n -ms-flex-positive: 0;\n flex-grow: 0;\n -webkit-flex-shrink: 0;\n -ms-flex-negative: 0;\n flex-shrink: 0;\n overflow: hidden; }\n .mdl-layout__container > .mdl-layout__tab-bar-container {\n position: absolute;\n top: 0;\n left: 0; }\n\n.mdl-layout__tab-bar-button {\n display: inline-block;\n position: absolute;\n top: 0;\n height: 48px;\n width: 56px;\n z-index: 4;\n text-align: center;\n background-color: rgb(63,81,181);\n color: transparent;\n cursor: pointer;\n -webkit-user-select: none;\n -moz-user-select: none;\n -ms-user-select: none;\n user-select: none; }\n @media screen and (max-width: 1024px) {\n .mdl-layout__tab-bar-button {\n display: none;\n width: 60px; } }\n .mdl-layout--fixed-tabs .mdl-layout__tab-bar-button {\n display: none; }\n .mdl-layout__tab-bar-button .material-icons {\n line-height: 48px; }\n .mdl-layout__tab-bar-button.is-active {\n color: rgb(255,255,255); }\n\n.mdl-layout__tab-bar-left-button {\n left: 0; }\n\n.mdl-layout__tab-bar-right-button {\n right: 0; }\n\n.mdl-layout__tab {\n margin: 0;\n border: none;\n padding: 0 24px 0 24px;\n float: left;\n position: relative;\n display: block;\n -webkit-box-flex: 0;\n -webkit-flex-grow: 0;\n -ms-flex-positive: 0;\n flex-grow: 0;\n -webkit-flex-shrink: 0;\n -ms-flex-negative: 0;\n flex-shrink: 0;\n text-decoration: none;\n height: 48px;\n line-height: 48px;\n text-align: center;\n font-weight: 500;\n font-size: 14px;\n text-transform: uppercase;\n color: rgba(255,255,255, 0.6);\n overflow: hidden; }\n @media screen and (max-width: 1024px) {\n .mdl-layout__tab {\n padding: 0 12px 0 12px; } }\n .mdl-layout--fixed-tabs .mdl-layout__tab {\n float: none;\n -webkit-box-flex: 1;\n -webkit-flex-grow: 1;\n -ms-flex-positive: 1;\n flex-grow: 1;\n padding: 0; }\n .mdl-layout.is-upgraded .mdl-layout__tab.is-active {\n color: rgb(255,255,255); }\n .mdl-layout.is-upgraded .mdl-layout__tab.is-active::after {\n height: 2px;\n width: 100%;\n display: block;\n content: \" \";\n bottom: 0;\n left: 0;\n position: absolute;\n background: rgb(255,64,129);\n -webkit-animation: border-expand 0.2s cubic-bezier(0.4, 0, 0.4, 1) 0.01s alternate forwards;\n animation: border-expand 0.2s cubic-bezier(0.4, 0, 0.4, 1) 0.01s alternate forwards;\n -webkit-transition: all 1s cubic-bezier(0.4, 0, 1, 1);\n transition: all 1s cubic-bezier(0.4, 0, 1, 1); }\n .mdl-layout__tab .mdl-layout__tab-ripple-container {\n display: block;\n position: absolute;\n height: 100%;\n width: 100%;\n left: 0;\n top: 0;\n z-index: 1;\n overflow: hidden; }\n .mdl-layout__tab .mdl-layout__tab-ripple-container .mdl-ripple {\n background-color: rgb(255,255,255); }\n\n.mdl-layout__tab-panel {\n display: block; }\n .mdl-layout.is-upgraded .mdl-layout__tab-panel {\n display: none; }\n .mdl-layout.is-upgraded .mdl-layout__tab-panel.is-active {\n display: block; }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* Typography */\n/* Shadows */\n/* Animations */\n.mdl-radio {\n position: relative;\n font-size: 16px;\n line-height: 24px;\n display: inline-block;\n box-sizing: border-box;\n margin: 0;\n padding-left: 0; }\n .mdl-radio.is-upgraded {\n padding-left: 24px; }\n\n.mdl-radio__button {\n line-height: 24px; }\n .mdl-radio.is-upgraded .mdl-radio__button {\n position: absolute;\n width: 0;\n height: 0;\n margin: 0;\n padding: 0;\n opacity: 0;\n -ms-appearance: none;\n -moz-appearance: none;\n -webkit-appearance: none;\n appearance: none;\n border: none; }\n\n.mdl-radio__outer-circle {\n position: absolute;\n top: 4px;\n left: 0;\n display: inline-block;\n box-sizing: border-box;\n width: 16px;\n height: 16px;\n margin: 0;\n cursor: pointer;\n border: 2px solid rgba(0,0,0, 0.54);\n border-radius: 50%;\n z-index: 2; }\n .mdl-radio.is-checked .mdl-radio__outer-circle {\n border: 2px solid rgb(63,81,181); }\n .mdl-radio.is-disabled .mdl-radio__outer-circle {\n border: 2px solid rgba(0,0,0, 0.26);\n cursor: auto; }\n\n.mdl-radio__inner-circle {\n position: absolute;\n z-index: 1;\n margin: 0;\n top: 8px;\n left: 4px;\n box-sizing: border-box;\n width: 8px;\n height: 8px;\n cursor: pointer;\n -webkit-transition-duration: 0.28s;\n transition-duration: 0.28s;\n -webkit-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n -webkit-transition-property: -webkit-transform;\n transition-property: transform;\n -webkit-transform: scale3d(0, 0, 0);\n transform: scale3d(0, 0, 0);\n border-radius: 50%;\n background: rgb(63,81,181); }\n .mdl-radio.is-checked .mdl-radio__inner-circle {\n -webkit-transform: scale3d(1, 1, 1);\n transform: scale3d(1, 1, 1); }\n .mdl-radio.is-disabled .mdl-radio__inner-circle {\n background: rgba(0,0,0, 0.26);\n cursor: auto; }\n .mdl-radio.is-focused .mdl-radio__inner-circle {\n box-shadow: 0 0 0px 10px rgba(0, 0, 0, 0.1); }\n\n.mdl-radio__label {\n cursor: pointer; }\n .mdl-radio.is-disabled .mdl-radio__label {\n color: rgba(0,0,0, 0.26);\n cursor: auto; }\n\n.mdl-radio__ripple-container {\n position: absolute;\n z-index: 2;\n top: -9px;\n left: -13px;\n box-sizing: border-box;\n width: 42px;\n height: 42px;\n border-radius: 50%;\n cursor: pointer;\n overflow: hidden;\n -webkit-mask-image: -webkit-radial-gradient(circle, white, black); }\n .mdl-radio__ripple-container .mdl-ripple {\n background: rgb(63,81,181); }\n .mdl-radio.is-disabled .mdl-radio__ripple-container {\n cursor: auto; }\n .mdl-radio.is-disabled .mdl-radio__ripple-container .mdl-ripple {\n background: transparent; }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n_:-ms-input-placeholder, :root .mdl-slider.mdl-slider.is-upgraded {\n -ms-appearance: none;\n height: 32px;\n margin: 0; }\n\n.mdl-slider {\n width: calc(100% - 40px);\n margin: 0 20px; }\n .mdl-slider.is-upgraded {\n -webkit-appearance: none;\n -moz-appearance: none;\n appearance: none;\n height: 2px;\n background: transparent;\n -webkit-user-select: none;\n -moz-user-select: none;\n -ms-user-select: none;\n user-select: none;\n outline: 0;\n padding: 0;\n color: rgb(63,81,181);\n -webkit-align-self: center;\n -ms-flex-item-align: center;\n align-self: center;\n z-index: 1;\n cursor: pointer;\n /**************************** Tracks ****************************/\n /**************************** Thumbs ****************************/\n /**************************** 0-value ****************************/\n /**************************** Disabled ****************************/ }\n .mdl-slider.is-upgraded::-moz-focus-outer {\n border: 0; }\n .mdl-slider.is-upgraded::-ms-tooltip {\n display: none; }\n .mdl-slider.is-upgraded::-webkit-slider-runnable-track {\n background: transparent; }\n .mdl-slider.is-upgraded::-moz-range-track {\n background: transparent;\n border: none; }\n .mdl-slider.is-upgraded::-ms-track {\n background: none;\n color: transparent;\n height: 2px;\n width: 100%;\n border: none; }\n .mdl-slider.is-upgraded::-ms-fill-lower {\n padding: 0;\n background: linear-gradient(to right, transparent, transparent 16px, rgb(63,81,181) 16px, rgb(63,81,181) 0); }\n .mdl-slider.is-upgraded::-ms-fill-upper {\n padding: 0;\n background: linear-gradient(to left, transparent, transparent 16px, rgba(0,0,0, 0.26) 16px, rgba(0,0,0, 0.26) 0); }\n .mdl-slider.is-upgraded::-webkit-slider-thumb {\n -webkit-appearance: none;\n width: 12px;\n height: 12px;\n box-sizing: border-box;\n border-radius: 50%;\n background: rgb(63,81,181);\n border: none;\n -webkit-transition: -webkit-transform 0.18s cubic-bezier(0.4, 0, 0.2, 1), border 0.18s cubic-bezier(0.4, 0, 0.2, 1), box-shadow 0.18s cubic-bezier(0.4, 0, 0.2, 1), background 0.28s cubic-bezier(0.4, 0, 0.2, 1);\n transition: transform 0.18s cubic-bezier(0.4, 0, 0.2, 1), border 0.18s cubic-bezier(0.4, 0, 0.2, 1), box-shadow 0.18s cubic-bezier(0.4, 0, 0.2, 1), background 0.28s cubic-bezier(0.4, 0, 0.2, 1); }\n .mdl-slider.is-upgraded::-moz-range-thumb {\n -moz-appearance: none;\n width: 12px;\n height: 12px;\n box-sizing: border-box;\n border-radius: 50%;\n background-image: none;\n background: rgb(63,81,181);\n border: none; }\n .mdl-slider.is-upgraded:focus:not(:active)::-webkit-slider-thumb {\n box-shadow: 0 0 0 10px rgba(63,81,181, 0.26); }\n .mdl-slider.is-upgraded:focus:not(:active)::-moz-range-thumb {\n box-shadow: 0 0 0 10px rgba(63,81,181, 0.26); }\n .mdl-slider.is-upgraded:active::-webkit-slider-thumb {\n background-image: none;\n background: rgb(63,81,181);\n -webkit-transform: scale(1.5);\n transform: scale(1.5); }\n .mdl-slider.is-upgraded:active::-moz-range-thumb {\n background-image: none;\n background: rgb(63,81,181);\n transform: scale(1.5); }\n .mdl-slider.is-upgraded::-ms-thumb {\n width: 32px;\n height: 32px;\n border: none;\n border-radius: 50%;\n background: rgb(63,81,181);\n -ms-transform: scale(0.375);\n transform: scale(0.375);\n transition: transform 0.18s cubic-bezier(0.4, 0, 0.2, 1), background 0.28s cubic-bezier(0.4, 0, 0.2, 1); }\n .mdl-slider.is-upgraded:focus:not(:active)::-ms-thumb {\n background: radial-gradient(circle closest-side, rgb(63,81,181) 0%, rgb(63,81,181) 37.5%, rgba(63,81,181, 0.26) 37.5%, rgba(63,81,181, 0.26) 100%);\n -ms-transform: scale(1);\n transform: scale(1); }\n .mdl-slider.is-upgraded:active::-ms-thumb {\n background: rgb(63,81,181);\n -ms-transform: scale(0.5625);\n transform: scale(0.5625); }\n .mdl-slider.is-upgraded.is-lowest-value::-webkit-slider-thumb {\n border: 2px solid rgba(0,0,0, 0.26);\n background: transparent; }\n .mdl-slider.is-upgraded.is-lowest-value::-moz-range-thumb {\n border: 2px solid rgba(0,0,0, 0.26);\n background: transparent; }\n .mdl-slider.is-upgraded.is-lowest-value +\n.mdl-slider__background-flex > .mdl-slider__background-upper {\n left: 6px; }\n .mdl-slider.is-upgraded.is-lowest-value:focus:not(:active)::-webkit-slider-thumb {\n box-shadow: 0 0 0 10px rgba(0,0,0, 0.12);\n background: rgba(0,0,0, 0.12); }\n .mdl-slider.is-upgraded.is-lowest-value:focus:not(:active)::-moz-range-thumb {\n box-shadow: 0 0 0 10px rgba(0,0,0, 0.12);\n background: rgba(0,0,0, 0.12); }\n .mdl-slider.is-upgraded.is-lowest-value:active::-webkit-slider-thumb {\n border: 1.6px solid rgba(0,0,0, 0.26);\n -webkit-transform: scale(1.5);\n transform: scale(1.5); }\n .mdl-slider.is-upgraded.is-lowest-value:active +\n.mdl-slider__background-flex > .mdl-slider__background-upper {\n left: 9px; }\n .mdl-slider.is-upgraded.is-lowest-value:active::-moz-range-thumb {\n border: 1.5px solid rgba(0,0,0, 0.26);\n transform: scale(1.5); }\n .mdl-slider.is-upgraded.is-lowest-value::-ms-thumb {\n background: radial-gradient(circle closest-side, transparent 0%, transparent 66.67%, rgba(0,0,0, 0.26) 66.67%, rgba(0,0,0, 0.26) 100%); }\n .mdl-slider.is-upgraded.is-lowest-value:focus:not(:active)::-ms-thumb {\n background: radial-gradient(circle closest-side, rgba(0,0,0, 0.12) 0%, rgba(0,0,0, 0.12) 25%, rgba(0,0,0, 0.26) 25%, rgba(0,0,0, 0.26) 37.5%, rgba(0,0,0, 0.12) 37.5%, rgba(0,0,0, 0.12) 100%);\n -ms-transform: scale(1);\n transform: scale(1); }\n .mdl-slider.is-upgraded.is-lowest-value:active::-ms-thumb {\n -ms-transform: scale(0.5625);\n transform: scale(0.5625);\n background: radial-gradient(circle closest-side, transparent 0%, transparent 77.78%, rgba(0,0,0, 0.26) 77.78%, rgba(0,0,0, 0.26) 100%); }\n .mdl-slider.is-upgraded.is-lowest-value::-ms-fill-lower {\n background: transparent; }\n .mdl-slider.is-upgraded.is-lowest-value::-ms-fill-upper {\n margin-left: 6px; }\n .mdl-slider.is-upgraded.is-lowest-value:active::-ms-fill-upper {\n margin-left: 9px; }\n .mdl-slider.is-upgraded:disabled:focus::-webkit-slider-thumb, .mdl-slider.is-upgraded:disabled:active::-webkit-slider-thumb, .mdl-slider.is-upgraded:disabled::-webkit-slider-thumb {\n -webkit-transform: scale(0.667);\n transform: scale(0.667);\n background: rgba(0,0,0, 0.26); }\n .mdl-slider.is-upgraded:disabled:focus::-moz-range-thumb, .mdl-slider.is-upgraded:disabled:active::-moz-range-thumb, .mdl-slider.is-upgraded:disabled::-moz-range-thumb {\n transform: scale(0.667);\n background: rgba(0,0,0, 0.26); }\n .mdl-slider.is-upgraded:disabled +\n.mdl-slider__background-flex > .mdl-slider__background-lower {\n background-color: rgba(0,0,0, 0.26);\n left: -6px; }\n .mdl-slider.is-upgraded:disabled +\n.mdl-slider__background-flex > .mdl-slider__background-upper {\n left: 6px; }\n .mdl-slider.is-upgraded.is-lowest-value:disabled:focus::-webkit-slider-thumb, .mdl-slider.is-upgraded.is-lowest-value:disabled:active::-webkit-slider-thumb, .mdl-slider.is-upgraded.is-lowest-value:disabled::-webkit-slider-thumb {\n border: 3px solid rgba(0,0,0, 0.26);\n background: transparent;\n -webkit-transform: scale(0.667);\n transform: scale(0.667); }\n .mdl-slider.is-upgraded.is-lowest-value:disabled:focus::-moz-range-thumb, .mdl-slider.is-upgraded.is-lowest-value:disabled:active::-moz-range-thumb, .mdl-slider.is-upgraded.is-lowest-value:disabled::-moz-range-thumb {\n border: 3px solid rgba(0,0,0, 0.26);\n background: transparent;\n transform: scale(0.667); }\n .mdl-slider.is-upgraded.is-lowest-value:disabled:active +\n.mdl-slider__background-flex > .mdl-slider__background-upper {\n left: 6px; }\n .mdl-slider.is-upgraded:disabled:focus::-ms-thumb, .mdl-slider.is-upgraded:disabled:active::-ms-thumb, .mdl-slider.is-upgraded:disabled::-ms-thumb {\n -ms-transform: scale(0.25);\n transform: scale(0.25);\n background: rgba(0,0,0, 0.26); }\n .mdl-slider.is-upgraded.is-lowest-value:disabled:focus::-ms-thumb, .mdl-slider.is-upgraded.is-lowest-value:disabled:active::-ms-thumb, .mdl-slider.is-upgraded.is-lowest-value:disabled::-ms-thumb {\n -ms-transform: scale(0.25);\n transform: scale(0.25);\n background: radial-gradient(circle closest-side, transparent 0%, transparent 50%, rgba(0,0,0, 0.26) 50%, rgba(0,0,0, 0.26) 100%); }\n .mdl-slider.is-upgraded:disabled::-ms-fill-lower {\n margin-right: 6px;\n background: linear-gradient(to right, transparent, transparent 25px, rgba(0,0,0, 0.26) 25px, rgba(0,0,0, 0.26) 0); }\n .mdl-slider.is-upgraded:disabled::-ms-fill-upper {\n margin-left: 6px; }\n .mdl-slider.is-upgraded.is-lowest-value:disabled:active::-ms-fill-upper {\n margin-left: 6px; }\n\n.mdl-slider__ie-container {\n height: 18px;\n overflow: visible;\n border: none;\n margin: none;\n padding: none; }\n\n.mdl-slider__container {\n height: 18px;\n position: relative;\n background: none;\n display: -webkit-box;\n display: -webkit-flex;\n display: -ms-flexbox;\n display: flex;\n -webkit-box-orient: horizontal;\n -webkit-box-direction: normal;\n -webkit-flex-direction: row;\n -ms-flex-direction: row;\n flex-direction: row; }\n\n.mdl-slider__background-flex {\n background: transparent;\n position: absolute;\n height: 2px;\n width: calc(100% - 52px);\n top: 50%;\n left: 0;\n margin: 0 26px;\n display: -webkit-box;\n display: -webkit-flex;\n display: -ms-flexbox;\n display: flex;\n overflow: hidden;\n border: 0;\n padding: 0;\n -webkit-transform: translate(0, -1px);\n -ms-transform: translate(0, -1px);\n transform: translate(0, -1px); }\n\n.mdl-slider__background-lower {\n background: rgb(63,81,181);\n -webkit-box-flex: 0;\n -webkit-flex: 0;\n -ms-flex: 0;\n flex: 0;\n position: relative;\n border: 0;\n padding: 0; }\n\n.mdl-slider__background-upper {\n background: rgba(0,0,0, 0.26);\n -webkit-box-flex: 0;\n -webkit-flex: 0;\n -ms-flex: 0;\n flex: 0;\n position: relative;\n border: 0;\n padding: 0;\n -webkit-transition: left 0.18s cubic-bezier(0.4, 0, 0.2, 1);\n transition: left 0.18s cubic-bezier(0.4, 0, 0.2, 1); }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n.mdl-spinner {\n display: inline-block;\n position: relative;\n width: 28px;\n height: 28px; }\n .mdl-spinner:not(.is-upgraded).is-active:after {\n content: \"Loading...\"; }\n .mdl-spinner.is-upgraded.is-active {\n -webkit-animation: mdl-spinner__container-rotate 1568.23529412ms linear infinite;\n animation: mdl-spinner__container-rotate 1568.23529412ms linear infinite; }\n\n@-webkit-keyframes mdl-spinner__container-rotate {\n to {\n -webkit-transform: rotate(360deg);\n transform: rotate(360deg); } }\n\n@keyframes mdl-spinner__container-rotate {\n to {\n -webkit-transform: rotate(360deg);\n transform: rotate(360deg); } }\n\n.mdl-spinner__layer {\n position: absolute;\n width: 100%;\n height: 100%;\n opacity: 0; }\n\n.mdl-spinner__layer-1 {\n border-color: rgb(66,165,245); }\n .mdl-spinner--single-color .mdl-spinner__layer-1 {\n border-color: rgb(63,81,181); }\n .mdl-spinner.is-active .mdl-spinner__layer-1 {\n -webkit-animation: mdl-spinner__fill-unfill-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both, mdl-spinner__layer-1-fade-in-out 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both;\n animation: mdl-spinner__fill-unfill-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both, mdl-spinner__layer-1-fade-in-out 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both; }\n\n.mdl-spinner__layer-2 {\n border-color: rgb(244,67,54); }\n .mdl-spinner--single-color .mdl-spinner__layer-2 {\n border-color: rgb(63,81,181); }\n .mdl-spinner.is-active .mdl-spinner__layer-2 {\n -webkit-animation: mdl-spinner__fill-unfill-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both, mdl-spinner__layer-2-fade-in-out 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both;\n animation: mdl-spinner__fill-unfill-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both, mdl-spinner__layer-2-fade-in-out 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both; }\n\n.mdl-spinner__layer-3 {\n border-color: rgb(253,216,53); }\n .mdl-spinner--single-color .mdl-spinner__layer-3 {\n border-color: rgb(63,81,181); }\n .mdl-spinner.is-active .mdl-spinner__layer-3 {\n -webkit-animation: mdl-spinner__fill-unfill-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both, mdl-spinner__layer-3-fade-in-out 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both;\n animation: mdl-spinner__fill-unfill-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both, mdl-spinner__layer-3-fade-in-out 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both; }\n\n.mdl-spinner__layer-4 {\n border-color: rgb(76,175,80); }\n .mdl-spinner--single-color .mdl-spinner__layer-4 {\n border-color: rgb(63,81,181); }\n .mdl-spinner.is-active .mdl-spinner__layer-4 {\n -webkit-animation: mdl-spinner__fill-unfill-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both, mdl-spinner__layer-4-fade-in-out 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both;\n animation: mdl-spinner__fill-unfill-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both, mdl-spinner__layer-4-fade-in-out 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both; }\n\n@-webkit-keyframes mdl-spinner__fill-unfill-rotate {\n 12.5% {\n -webkit-transform: rotate(135deg);\n transform: rotate(135deg); }\n 25% {\n -webkit-transform: rotate(270deg);\n transform: rotate(270deg); }\n 37.5% {\n -webkit-transform: rotate(405deg);\n transform: rotate(405deg); }\n 50% {\n -webkit-transform: rotate(540deg);\n transform: rotate(540deg); }\n 62.5% {\n -webkit-transform: rotate(675deg);\n transform: rotate(675deg); }\n 75% {\n -webkit-transform: rotate(810deg);\n transform: rotate(810deg); }\n 87.5% {\n -webkit-transform: rotate(945deg);\n transform: rotate(945deg); }\n to {\n -webkit-transform: rotate(1080deg);\n transform: rotate(1080deg); } }\n\n@keyframes mdl-spinner__fill-unfill-rotate {\n 12.5% {\n -webkit-transform: rotate(135deg);\n transform: rotate(135deg); }\n 25% {\n -webkit-transform: rotate(270deg);\n transform: rotate(270deg); }\n 37.5% {\n -webkit-transform: rotate(405deg);\n transform: rotate(405deg); }\n 50% {\n -webkit-transform: rotate(540deg);\n transform: rotate(540deg); }\n 62.5% {\n -webkit-transform: rotate(675deg);\n transform: rotate(675deg); }\n 75% {\n -webkit-transform: rotate(810deg);\n transform: rotate(810deg); }\n 87.5% {\n -webkit-transform: rotate(945deg);\n transform: rotate(945deg); }\n to {\n -webkit-transform: rotate(1080deg);\n transform: rotate(1080deg); } }\n\n/**\n* HACK: Even though the intention is to have the current .mdl-spinner__layer-N\n* at `opacity: 1`, we set it to `opacity: 0.99` instead since this forces Chrome\n* to do proper subpixel rendering for the elements being animated. This is\n* especially visible in Chrome 39 on Ubuntu 14.04. See:\n*\n* - https://github.com/Polymer/paper-spinner/issues/9\n* - https://code.google.com/p/chromium/issues/detail?id=436255\n*/\n@-webkit-keyframes mdl-spinner__layer-1-fade-in-out {\n from {\n opacity: 0.99; }\n 25% {\n opacity: 0.99; }\n 26% {\n opacity: 0; }\n 89% {\n opacity: 0; }\n 90% {\n opacity: 0.99; }\n 100% {\n opacity: 0.99; } }\n@keyframes mdl-spinner__layer-1-fade-in-out {\n from {\n opacity: 0.99; }\n 25% {\n opacity: 0.99; }\n 26% {\n opacity: 0; }\n 89% {\n opacity: 0; }\n 90% {\n opacity: 0.99; }\n 100% {\n opacity: 0.99; } }\n\n@-webkit-keyframes mdl-spinner__layer-2-fade-in-out {\n from {\n opacity: 0; }\n 15% {\n opacity: 0; }\n 25% {\n opacity: 0.99; }\n 50% {\n opacity: 0.99; }\n 51% {\n opacity: 0; } }\n\n@keyframes mdl-spinner__layer-2-fade-in-out {\n from {\n opacity: 0; }\n 15% {\n opacity: 0; }\n 25% {\n opacity: 0.99; }\n 50% {\n opacity: 0.99; }\n 51% {\n opacity: 0; } }\n\n@-webkit-keyframes mdl-spinner__layer-3-fade-in-out {\n from {\n opacity: 0; }\n 40% {\n opacity: 0; }\n 50% {\n opacity: 0.99; }\n 75% {\n opacity: 0.99; }\n 76% {\n opacity: 0; } }\n\n@keyframes mdl-spinner__layer-3-fade-in-out {\n from {\n opacity: 0; }\n 40% {\n opacity: 0; }\n 50% {\n opacity: 0.99; }\n 75% {\n opacity: 0.99; }\n 76% {\n opacity: 0; } }\n\n@-webkit-keyframes mdl-spinner__layer-4-fade-in-out {\n from {\n opacity: 0; }\n 65% {\n opacity: 0; }\n 75% {\n opacity: 0.99; }\n 90% {\n opacity: 0.99; }\n 100% {\n opacity: 0; } }\n\n@keyframes mdl-spinner__layer-4-fade-in-out {\n from {\n opacity: 0; }\n 65% {\n opacity: 0; }\n 75% {\n opacity: 0.99; }\n 90% {\n opacity: 0.99; }\n 100% {\n opacity: 0; } }\n\n/**\n* Patch the gap that appear between the two adjacent\n* div.mdl-spinner__circle-clipper while the spinner is rotating\n* (appears on Chrome 38, Safari 7.1, and IE 11).\n*\n* Update: the gap no longer appears on Chrome when .mdl-spinner__layer-N's\n* opacity is 0.99, but still does on Safari and IE.\n*/\n.mdl-spinner__gap-patch {\n position: absolute;\n box-sizing: border-box;\n top: 0;\n left: 45%;\n width: 10%;\n height: 100%;\n overflow: hidden;\n border-color: inherit; }\n .mdl-spinner__gap-patch .mdl-spinner__circle {\n width: 1000%;\n left: -450%; }\n\n.mdl-spinner__circle-clipper {\n display: inline-block;\n position: relative;\n width: 50%;\n height: 100%;\n overflow: hidden;\n border-color: inherit; }\n .mdl-spinner__circle-clipper .mdl-spinner__circle {\n width: 200%; }\n\n.mdl-spinner__circle {\n box-sizing: border-box;\n height: 100%;\n border-width: 3px;\n border-style: solid;\n border-color: inherit;\n border-bottom-color: transparent !important;\n border-radius: 50%;\n -webkit-animation: none;\n animation: none;\n position: absolute;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0; }\n .mdl-spinner__left .mdl-spinner__circle {\n border-right-color: transparent !important;\n -webkit-transform: rotate(129deg);\n -ms-transform: rotate(129deg);\n transform: rotate(129deg); }\n .mdl-spinner.is-active .mdl-spinner__left .mdl-spinner__circle {\n -webkit-animation: mdl-spinner__left-spin 1333ms cubic-bezier(0.4, 0, 0.2, 1) infinite both;\n animation: mdl-spinner__left-spin 1333ms cubic-bezier(0.4, 0, 0.2, 1) infinite both; }\n .mdl-spinner__right .mdl-spinner__circle {\n left: -100%;\n border-left-color: transparent !important;\n -webkit-transform: rotate(-129deg);\n -ms-transform: rotate(-129deg);\n transform: rotate(-129deg); }\n .mdl-spinner.is-active .mdl-spinner__right .mdl-spinner__circle {\n -webkit-animation: mdl-spinner__right-spin 1333ms cubic-bezier(0.4, 0, 0.2, 1) infinite both;\n animation: mdl-spinner__right-spin 1333ms cubic-bezier(0.4, 0, 0.2, 1) infinite both; }\n\n@-webkit-keyframes mdl-spinner__left-spin {\n from {\n -webkit-transform: rotate(130deg);\n transform: rotate(130deg); }\n 50% {\n -webkit-transform: rotate(-5deg);\n transform: rotate(-5deg); }\n to {\n -webkit-transform: rotate(130deg);\n transform: rotate(130deg); } }\n\n@keyframes mdl-spinner__left-spin {\n from {\n -webkit-transform: rotate(130deg);\n transform: rotate(130deg); }\n 50% {\n -webkit-transform: rotate(-5deg);\n transform: rotate(-5deg); }\n to {\n -webkit-transform: rotate(130deg);\n transform: rotate(130deg); } }\n\n@-webkit-keyframes mdl-spinner__right-spin {\n from {\n -webkit-transform: rotate(-130deg);\n transform: rotate(-130deg); }\n 50% {\n -webkit-transform: rotate(5deg);\n transform: rotate(5deg); }\n to {\n -webkit-transform: rotate(-130deg);\n transform: rotate(-130deg); } }\n\n@keyframes mdl-spinner__right-spin {\n from {\n -webkit-transform: rotate(-130deg);\n transform: rotate(-130deg); }\n 50% {\n -webkit-transform: rotate(5deg);\n transform: rotate(5deg); }\n to {\n -webkit-transform: rotate(-130deg);\n transform: rotate(-130deg); } }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* Typography */\n/* Shadows */\n/* Animations */\n.mdl-switch {\n position: relative;\n z-index: 1;\n vertical-align: middle;\n display: inline-block;\n box-sizing: border-box;\n width: 100%;\n height: 24px;\n margin: 0;\n padding: 0;\n overflow: visible;\n -webkit-touch-callout: none;\n -webkit-user-select: none;\n -moz-user-select: none;\n -ms-user-select: none;\n user-select: none; }\n .mdl-switch.is-upgraded {\n padding-left: 28px; }\n\n.mdl-switch__input {\n line-height: 24px; }\n .mdl-switch.is-upgraded .mdl-switch__input {\n position: absolute;\n width: 0;\n height: 0;\n margin: 0;\n padding: 0;\n opacity: 0;\n -ms-appearance: none;\n -moz-appearance: none;\n -webkit-appearance: none;\n appearance: none;\n border: none; }\n\n.mdl-switch__track {\n background: rgba(0,0,0, 0.26);\n position: absolute;\n left: 0;\n top: 5px;\n height: 14px;\n width: 36px;\n border-radius: 14px;\n cursor: pointer; }\n .mdl-switch.is-checked .mdl-switch__track {\n background: rgba(63,81,181, 0.5); }\n .mdl-switch.is-disabled .mdl-switch__track {\n background: rgba(0,0,0, 0.12);\n cursor: auto; }\n\n.mdl-switch__thumb {\n background: rgb(250,250,250);\n position: absolute;\n left: 0;\n top: 2px;\n height: 20px;\n width: 20px;\n border-radius: 50%;\n cursor: pointer;\n box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12);\n -webkit-transition-duration: 0.28s;\n transition-duration: 0.28s;\n -webkit-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n -webkit-transition-property: left;\n transition-property: left; }\n .mdl-switch.is-checked .mdl-switch__thumb {\n background: rgb(63,81,181);\n left: 16px;\n box-shadow: 0 3px 4px 0 rgba(0, 0, 0, 0.14), 0 3px 3px -2px rgba(0, 0, 0, 0.2), 0 1px 8px 0 rgba(0, 0, 0, 0.12); }\n .mdl-switch.is-disabled .mdl-switch__thumb {\n background: rgb(189,189,189);\n cursor: auto; }\n\n.mdl-switch__focus-helper {\n position: absolute;\n top: 50%;\n left: 50%;\n -webkit-transform: translate(-4px, -4px);\n -ms-transform: translate(-4px, -4px);\n transform: translate(-4px, -4px);\n display: inline-block;\n box-sizing: border-box;\n width: 8px;\n height: 8px;\n border-radius: 50%;\n background-color: transparent; }\n .mdl-switch.is-focused .mdl-switch__focus-helper {\n box-shadow: 0 0 0px 20px rgba(0, 0, 0, 0.1);\n background-color: rgba(0, 0, 0, 0.1); }\n .mdl-switch.is-focused.is-checked .mdl-switch__focus-helper {\n box-shadow: 0 0 0px 20px rgba(63,81,181, 0.26);\n background-color: rgba(63,81,181, 0.26); }\n\n.mdl-switch__label {\n position: relative;\n cursor: pointer;\n font-size: 16px;\n line-height: 24px;\n margin: 0;\n left: 24px; }\n .mdl-switch.is-disabled .mdl-switch__label {\n color: rgb(189,189,189);\n cursor: auto; }\n\n.mdl-switch__ripple-container {\n position: absolute;\n z-index: 2;\n top: -12px;\n left: -14px;\n box-sizing: border-box;\n width: 48px;\n height: 48px;\n border-radius: 50%;\n cursor: pointer;\n overflow: hidden;\n -webkit-mask-image: -webkit-radial-gradient(circle, white, black);\n -webkit-transition-duration: 0.40s;\n transition-duration: 0.40s;\n -webkit-transition-timing-function: step-end;\n transition-timing-function: step-end;\n -webkit-transition-property: left;\n transition-property: left; }\n .mdl-switch__ripple-container .mdl-ripple {\n background: rgb(63,81,181); }\n .mdl-switch.is-disabled .mdl-switch__ripple-container {\n cursor: auto; }\n .mdl-switch.is-disabled .mdl-switch__ripple-container .mdl-ripple {\n background: transparent; }\n .mdl-switch.is-checked .mdl-switch__ripple-container {\n cursor: auto;\n left: 2px; }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n.mdl-tabs {\n display: block;\n width: 100%; }\n\n.mdl-tabs__tab-bar {\n display: -webkit-box;\n display: -webkit-flex;\n display: -ms-flexbox;\n display: flex;\n -webkit-box-orient: horizontal;\n -webkit-box-direction: normal;\n -webkit-flex-direction: row;\n -ms-flex-direction: row;\n flex-direction: row;\n -webkit-box-pack: center;\n -webkit-justify-content: center;\n -ms-flex-pack: center;\n justify-content: center;\n -webkit-align-content: space-between;\n -ms-flex-line-pack: justify;\n align-content: space-between;\n -webkit-box-align: start;\n -webkit-align-items: flex-start;\n -ms-flex-align: start;\n align-items: flex-start;\n height: 48px;\n padding: 0 0 0 0;\n margin: 0;\n border-bottom: 1px solid rgb(224,224,224); }\n\n.mdl-tabs__tab {\n margin: 0;\n border: none;\n padding: 0 24px 0 24px;\n float: left;\n position: relative;\n display: block;\n color: red;\n text-decoration: none;\n height: 48px;\n line-height: 48px;\n text-align: center;\n font-weight: 500;\n font-size: 14px;\n text-transform: uppercase;\n color: rgba(0,0,0, 0.54);\n overflow: hidden; }\n .mdl-tabs.is-upgraded .mdl-tabs__tab.is-active {\n color: rgba(0,0,0, 0.87); }\n .mdl-tabs.is-upgraded .mdl-tabs__tab.is-active:after {\n height: 2px;\n width: 100%;\n display: block;\n content: \" \";\n bottom: 0px;\n left: 0px;\n position: absolute;\n background: rgb(63,81,181);\n -webkit-animation: border-expand 0.2s cubic-bezier(0.4, 0, 0.4, 1) 0.01s alternate forwards;\n animation: border-expand 0.2s cubic-bezier(0.4, 0, 0.4, 1) 0.01s alternate forwards;\n -webkit-transition: all 1s cubic-bezier(0.4, 0, 1, 1);\n transition: all 1s cubic-bezier(0.4, 0, 1, 1); }\n .mdl-tabs__tab .mdl-tabs__ripple-container {\n display: block;\n position: absolute;\n height: 100%;\n width: 100%;\n left: 0px;\n top: 0px;\n z-index: 1;\n overflow: hidden; }\n .mdl-tabs__tab .mdl-tabs__ripple-container .mdl-ripple {\n background: rgb(63,81,181); }\n\n.mdl-tabs__panel {\n display: block; }\n .mdl-tabs.is-upgraded .mdl-tabs__panel {\n display: none; }\n .mdl-tabs.is-upgraded .mdl-tabs__panel.is-active {\n display: block; }\n\n@-webkit-keyframes border-expand {\n 0% {\n opacity: 0;\n width: 0; }\n 100% {\n opacity: 1;\n width: 100%; } }\n\n@keyframes border-expand {\n 0% {\n opacity: 0;\n width: 0; }\n 100% {\n opacity: 1;\n width: 100%; } }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* Typography */\n/* Shadows */\n/* Animations */\n.mdl-textfield {\n position: relative;\n font-size: 16px;\n display: inline-block;\n box-sizing: border-box;\n width: 300px;\n max-width: 100%;\n margin: 0;\n padding: 20px 0; }\n .mdl-textfield .mdl-button {\n position: absolute;\n bottom: 20px; }\n\n.mdl-textfield--align-right {\n text-align: right; }\n\n.mdl-textfield--full-width {\n width: 100%; }\n\n.mdl-textfield--expandable {\n min-width: 32px;\n width: auto;\n min-height: 32px; }\n\n.mdl-textfield__input {\n border: none;\n border-bottom: 1px solid rgba(0,0,0, 0.12);\n display: block;\n font-size: 16px;\n margin: 0;\n padding: 4px 0;\n width: 100%;\n background: none;\n text-align: left;\n color: inherit; }\n .mdl-textfield.is-focused .mdl-textfield__input {\n outline: none; }\n .mdl-textfield.is-invalid .mdl-textfield__input {\n border-color: rgb(222, 50, 38);\n box-shadow: none; }\n .mdl-textfield.is-disabled .mdl-textfield__input {\n background-color: transparent;\n border-bottom: 1px dotted rgba(0,0,0, 0.12);\n color: rgba(0,0,0, 0.26); }\n\n.mdl-textfield textarea.mdl-textfield__input {\n display: block; }\n\n.mdl-textfield__label {\n bottom: 0;\n color: rgba(0,0,0, 0.26);\n font-size: 16px;\n left: 0;\n right: 0;\n pointer-events: none;\n position: absolute;\n display: block;\n top: 24px;\n width: 100%;\n overflow: hidden;\n white-space: nowrap;\n text-align: left; }\n .mdl-textfield.is-dirty .mdl-textfield__label {\n visibility: hidden; }\n .mdl-textfield--floating-label .mdl-textfield__label {\n -webkit-transition-duration: 0.2s;\n transition-duration: 0.2s;\n -webkit-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); }\n .mdl-textfield.is-disabled.is-disabled .mdl-textfield__label {\n color: rgba(0,0,0, 0.26); }\n .mdl-textfield--floating-label.is-focused .mdl-textfield__label,\n .mdl-textfield--floating-label.is-dirty .mdl-textfield__label {\n color: rgb(63,81,181);\n font-size: 12px;\n top: 4px;\n visibility: visible; }\n .mdl-textfield--floating-label.is-focused .mdl-textfield__expandable-holder .mdl-textfield__label,\n .mdl-textfield--floating-label.is-dirty .mdl-textfield__expandable-holder .mdl-textfield__label {\n top: -16px; }\n .mdl-textfield--floating-label.is-invalid .mdl-textfield__label {\n color: rgb(222, 50, 38);\n font-size: 12px; }\n .mdl-textfield__label:after {\n background-color: rgb(63,81,181);\n bottom: 20px;\n content: '';\n height: 2px;\n left: 45%;\n position: absolute;\n -webkit-transition-duration: 0.2s;\n transition-duration: 0.2s;\n -webkit-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n visibility: hidden;\n width: 10px; }\n .mdl-textfield.is-focused .mdl-textfield__label:after {\n left: 0;\n visibility: visible;\n width: 100%; }\n .mdl-textfield.is-invalid .mdl-textfield__label:after {\n background-color: rgb(222, 50, 38); }\n\n.mdl-textfield__error {\n color: rgb(222, 50, 38);\n position: absolute;\n font-size: 12px;\n margin-top: 3px;\n visibility: hidden;\n display: block; }\n .mdl-textfield.is-invalid .mdl-textfield__error {\n visibility: visible; }\n\n.mdl-textfield__expandable-holder {\n display: inline-block;\n position: relative;\n margin-left: 32px;\n -webkit-transition-duration: 0.2s;\n transition-duration: 0.2s;\n -webkit-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n display: inline-block;\n max-width: 0.1px; }\n .mdl-textfield.is-focused .mdl-textfield__expandable-holder, .mdl-textfield.is-dirty .mdl-textfield__expandable-holder {\n max-width: 600px; }\n .mdl-textfield__expandable-holder .mdl-textfield__label:after {\n bottom: 0; }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n.mdl-tooltip {\n -webkit-transform: scale(0);\n -ms-transform: scale(0);\n transform: scale(0);\n -webkit-transform-origin: top center;\n -ms-transform-origin: top center;\n transform-origin: top center;\n will-change: transform;\n z-index: 999;\n background: rgba(97,97,97, 0.9);\n border-radius: 2px;\n color: rgb(255,255,255);\n display: inline-block;\n font-size: 10px;\n font-weight: 500;\n line-height: 14px;\n max-width: 170px;\n position: fixed;\n top: -500px;\n left: -500px;\n padding: 8px;\n text-align: center; }\n\n.mdl-tooltip.is-active {\n -webkit-animation: pulse 200ms cubic-bezier(0, 0, 0.2, 1) forwards;\n animation: pulse 200ms cubic-bezier(0, 0, 0.2, 1) forwards; }\n\n.mdl-tooltip--large {\n line-height: 14px;\n font-size: 14px;\n padding: 16px; }\n\n@-webkit-keyframes pulse {\n 0% {\n -webkit-transform: scale(0);\n transform: scale(0);\n opacity: 0; }\n 50% {\n -webkit-transform: scale(0.99);\n transform: scale(0.99); }\n 100% {\n -webkit-transform: scale(1);\n transform: scale(1);\n opacity: 1;\n visibility: visible; } }\n\n@keyframes pulse {\n 0% {\n -webkit-transform: scale(0);\n transform: scale(0);\n opacity: 0; }\n 50% {\n -webkit-transform: scale(0.99);\n transform: scale(0.99); }\n 100% {\n -webkit-transform: scale(1);\n transform: scale(1);\n opacity: 1;\n visibility: visible; } }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* Typography */\n/* Shadows */\n/* Animations */\n.mdl-shadow--2dp {\n box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12); }\n\n.mdl-shadow--3dp {\n box-shadow: 0 3px 4px 0 rgba(0, 0, 0, 0.14), 0 3px 3px -2px rgba(0, 0, 0, 0.2), 0 1px 8px 0 rgba(0, 0, 0, 0.12); }\n\n.mdl-shadow--4dp {\n box-shadow: 0 4px 5px 0 rgba(0, 0, 0, 0.14), 0 1px 10px 0 rgba(0, 0, 0, 0.12), 0 2px 4px -1px rgba(0, 0, 0, 0.2); }\n\n.mdl-shadow--6dp {\n box-shadow: 0 6px 10px 0 rgba(0, 0, 0, 0.14), 0 1px 18px 0 rgba(0, 0, 0, 0.12), 0 3px 5px -1px rgba(0, 0, 0, 0.2); }\n\n.mdl-shadow--8dp {\n box-shadow: 0 8px 10px 1px rgba(0, 0, 0, 0.14), 0 3px 14px 2px rgba(0, 0, 0, 0.12), 0 5px 5px -3px rgba(0, 0, 0, 0.2); }\n\n.mdl-shadow--16dp {\n box-shadow: 0 16px 24px 2px rgba(0, 0, 0, 0.14), 0 6px 30px 5px rgba(0, 0, 0, 0.12), 0 8px 10px -5px rgba(0, 0, 0, 0.2); }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*\n* NOTE: Some rules here are applied using duplicate selectors.\n* This is on purpose to increase their specificity when applied.\n* For example: `.mdl-cell--1-col-phone.mdl-cell--1-col-phone`\n*/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n.mdl-grid {\n display: -webkit-box;\n display: -webkit-flex;\n display: -ms-flexbox;\n display: flex;\n -webkit-flex-flow: row wrap;\n -ms-flex-flow: row wrap;\n flex-flow: row wrap;\n margin: 0 auto 0 auto;\n -webkit-box-align: stretch;\n -webkit-align-items: stretch;\n -ms-flex-align: stretch;\n align-items: stretch; }\n .mdl-grid.mdl-grid--no-spacing {\n padding: 0; }\n\n.mdl-cell {\n box-sizing: border-box; }\n\n.mdl-cell--top {\n -webkit-align-self: flex-start;\n -ms-flex-item-align: start;\n align-self: flex-start; }\n\n.mdl-cell--middle {\n -webkit-align-self: center;\n -ms-flex-item-align: center;\n align-self: center; }\n\n.mdl-cell--bottom {\n -webkit-align-self: flex-end;\n -ms-flex-item-align: end;\n align-self: flex-end; }\n\n.mdl-cell--stretch {\n -webkit-align-self: stretch;\n -ms-flex-item-align: stretch;\n align-self: stretch; }\n\n.mdl-grid.mdl-grid--no-spacing > .mdl-cell {\n margin: 0; }\n\n@media (max-width: 479px) {\n .mdl-grid {\n padding: 8px; }\n .mdl-cell {\n margin: 8px;\n width: calc(100% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell {\n width: 100%; }\n .mdl-cell--hide-phone {\n display: none !important; }\n .mdl-cell--1-col,\n .mdl-cell--1-col-phone.mdl-cell--1-col-phone {\n width: calc(25% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--1-col, .mdl-grid--no-spacing >\n .mdl-cell--1-col-phone.mdl-cell--1-col-phone {\n width: 25%; }\n .mdl-cell--2-col,\n .mdl-cell--2-col-phone.mdl-cell--2-col-phone {\n width: calc(50% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--2-col, .mdl-grid--no-spacing >\n .mdl-cell--2-col-phone.mdl-cell--2-col-phone {\n width: 50%; }\n .mdl-cell--3-col,\n .mdl-cell--3-col-phone.mdl-cell--3-col-phone {\n width: calc(75% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--3-col, .mdl-grid--no-spacing >\n .mdl-cell--3-col-phone.mdl-cell--3-col-phone {\n width: 75%; }\n .mdl-cell--4-col,\n .mdl-cell--4-col-phone.mdl-cell--4-col-phone {\n width: calc(100% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--4-col, .mdl-grid--no-spacing >\n .mdl-cell--4-col-phone.mdl-cell--4-col-phone {\n width: 100%; }\n .mdl-cell--5-col,\n .mdl-cell--5-col-phone.mdl-cell--5-col-phone {\n width: calc(100% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--5-col, .mdl-grid--no-spacing >\n .mdl-cell--5-col-phone.mdl-cell--5-col-phone {\n width: 100%; }\n .mdl-cell--6-col,\n .mdl-cell--6-col-phone.mdl-cell--6-col-phone {\n width: calc(100% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--6-col, .mdl-grid--no-spacing >\n .mdl-cell--6-col-phone.mdl-cell--6-col-phone {\n width: 100%; }\n .mdl-cell--7-col,\n .mdl-cell--7-col-phone.mdl-cell--7-col-phone {\n width: calc(100% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--7-col, .mdl-grid--no-spacing >\n .mdl-cell--7-col-phone.mdl-cell--7-col-phone {\n width: 100%; }\n .mdl-cell--8-col,\n .mdl-cell--8-col-phone.mdl-cell--8-col-phone {\n width: calc(100% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--8-col, .mdl-grid--no-spacing >\n .mdl-cell--8-col-phone.mdl-cell--8-col-phone {\n width: 100%; }\n .mdl-cell--9-col,\n .mdl-cell--9-col-phone.mdl-cell--9-col-phone {\n width: calc(100% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--9-col, .mdl-grid--no-spacing >\n .mdl-cell--9-col-phone.mdl-cell--9-col-phone {\n width: 100%; }\n .mdl-cell--10-col,\n .mdl-cell--10-col-phone.mdl-cell--10-col-phone {\n width: calc(100% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--10-col, .mdl-grid--no-spacing >\n .mdl-cell--10-col-phone.mdl-cell--10-col-phone {\n width: 100%; }\n .mdl-cell--11-col,\n .mdl-cell--11-col-phone.mdl-cell--11-col-phone {\n width: calc(100% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--11-col, .mdl-grid--no-spacing >\n .mdl-cell--11-col-phone.mdl-cell--11-col-phone {\n width: 100%; }\n .mdl-cell--12-col,\n .mdl-cell--12-col-phone.mdl-cell--12-col-phone {\n width: calc(100% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--12-col, .mdl-grid--no-spacing >\n .mdl-cell--12-col-phone.mdl-cell--12-col-phone {\n width: 100%; } }\n\n@media (min-width: 480px) and (max-width: 839px) {\n .mdl-grid {\n padding: 8px; }\n .mdl-cell {\n margin: 8px;\n width: calc(50% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell {\n width: 50%; }\n .mdl-cell--hide-tablet {\n display: none !important; }\n .mdl-cell--1-col,\n .mdl-cell--1-col-tablet.mdl-cell--1-col-tablet {\n width: calc(12.5% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--1-col, .mdl-grid--no-spacing >\n .mdl-cell--1-col-tablet.mdl-cell--1-col-tablet {\n width: 12.5%; }\n .mdl-cell--2-col,\n .mdl-cell--2-col-tablet.mdl-cell--2-col-tablet {\n width: calc(25% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--2-col, .mdl-grid--no-spacing >\n .mdl-cell--2-col-tablet.mdl-cell--2-col-tablet {\n width: 25%; }\n .mdl-cell--3-col,\n .mdl-cell--3-col-tablet.mdl-cell--3-col-tablet {\n width: calc(37.5% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--3-col, .mdl-grid--no-spacing >\n .mdl-cell--3-col-tablet.mdl-cell--3-col-tablet {\n width: 37.5%; }\n .mdl-cell--4-col,\n .mdl-cell--4-col-tablet.mdl-cell--4-col-tablet {\n width: calc(50% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--4-col, .mdl-grid--no-spacing >\n .mdl-cell--4-col-tablet.mdl-cell--4-col-tablet {\n width: 50%; }\n .mdl-cell--5-col,\n .mdl-cell--5-col-tablet.mdl-cell--5-col-tablet {\n width: calc(62.5% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--5-col, .mdl-grid--no-spacing >\n .mdl-cell--5-col-tablet.mdl-cell--5-col-tablet {\n width: 62.5%; }\n .mdl-cell--6-col,\n .mdl-cell--6-col-tablet.mdl-cell--6-col-tablet {\n width: calc(75% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--6-col, .mdl-grid--no-spacing >\n .mdl-cell--6-col-tablet.mdl-cell--6-col-tablet {\n width: 75%; }\n .mdl-cell--7-col,\n .mdl-cell--7-col-tablet.mdl-cell--7-col-tablet {\n width: calc(87.5% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--7-col, .mdl-grid--no-spacing >\n .mdl-cell--7-col-tablet.mdl-cell--7-col-tablet {\n width: 87.5%; }\n .mdl-cell--8-col,\n .mdl-cell--8-col-tablet.mdl-cell--8-col-tablet {\n width: calc(100% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--8-col, .mdl-grid--no-spacing >\n .mdl-cell--8-col-tablet.mdl-cell--8-col-tablet {\n width: 100%; }\n .mdl-cell--9-col,\n .mdl-cell--9-col-tablet.mdl-cell--9-col-tablet {\n width: calc(100% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--9-col, .mdl-grid--no-spacing >\n .mdl-cell--9-col-tablet.mdl-cell--9-col-tablet {\n width: 100%; }\n .mdl-cell--10-col,\n .mdl-cell--10-col-tablet.mdl-cell--10-col-tablet {\n width: calc(100% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--10-col, .mdl-grid--no-spacing >\n .mdl-cell--10-col-tablet.mdl-cell--10-col-tablet {\n width: 100%; }\n .mdl-cell--11-col,\n .mdl-cell--11-col-tablet.mdl-cell--11-col-tablet {\n width: calc(100% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--11-col, .mdl-grid--no-spacing >\n .mdl-cell--11-col-tablet.mdl-cell--11-col-tablet {\n width: 100%; }\n .mdl-cell--12-col,\n .mdl-cell--12-col-tablet.mdl-cell--12-col-tablet {\n width: calc(100% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--12-col, .mdl-grid--no-spacing >\n .mdl-cell--12-col-tablet.mdl-cell--12-col-tablet {\n width: 100%; } }\n\n@media (min-width: 840px) {\n .mdl-grid {\n padding: 8px; }\n .mdl-cell {\n margin: 8px;\n width: calc(33.3333333333% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell {\n width: 33.3333333333%; }\n .mdl-cell--hide-desktop {\n display: none !important; }\n .mdl-cell--1-col,\n .mdl-cell--1-col-desktop.mdl-cell--1-col-desktop {\n width: calc(8.3333333333% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--1-col, .mdl-grid--no-spacing >\n .mdl-cell--1-col-desktop.mdl-cell--1-col-desktop {\n width: 8.3333333333%; }\n .mdl-cell--2-col,\n .mdl-cell--2-col-desktop.mdl-cell--2-col-desktop {\n width: calc(16.6666666667% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--2-col, .mdl-grid--no-spacing >\n .mdl-cell--2-col-desktop.mdl-cell--2-col-desktop {\n width: 16.6666666667%; }\n .mdl-cell--3-col,\n .mdl-cell--3-col-desktop.mdl-cell--3-col-desktop {\n width: calc(25% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--3-col, .mdl-grid--no-spacing >\n .mdl-cell--3-col-desktop.mdl-cell--3-col-desktop {\n width: 25%; }\n .mdl-cell--4-col,\n .mdl-cell--4-col-desktop.mdl-cell--4-col-desktop {\n width: calc(33.3333333333% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--4-col, .mdl-grid--no-spacing >\n .mdl-cell--4-col-desktop.mdl-cell--4-col-desktop {\n width: 33.3333333333%; }\n .mdl-cell--5-col,\n .mdl-cell--5-col-desktop.mdl-cell--5-col-desktop {\n width: calc(41.6666666667% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--5-col, .mdl-grid--no-spacing >\n .mdl-cell--5-col-desktop.mdl-cell--5-col-desktop {\n width: 41.6666666667%; }\n .mdl-cell--6-col,\n .mdl-cell--6-col-desktop.mdl-cell--6-col-desktop {\n width: calc(50% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--6-col, .mdl-grid--no-spacing >\n .mdl-cell--6-col-desktop.mdl-cell--6-col-desktop {\n width: 50%; }\n .mdl-cell--7-col,\n .mdl-cell--7-col-desktop.mdl-cell--7-col-desktop {\n width: calc(58.3333333333% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--7-col, .mdl-grid--no-spacing >\n .mdl-cell--7-col-desktop.mdl-cell--7-col-desktop {\n width: 58.3333333333%; }\n .mdl-cell--8-col,\n .mdl-cell--8-col-desktop.mdl-cell--8-col-desktop {\n width: calc(66.6666666667% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--8-col, .mdl-grid--no-spacing >\n .mdl-cell--8-col-desktop.mdl-cell--8-col-desktop {\n width: 66.6666666667%; }\n .mdl-cell--9-col,\n .mdl-cell--9-col-desktop.mdl-cell--9-col-desktop {\n width: calc(75% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--9-col, .mdl-grid--no-spacing >\n .mdl-cell--9-col-desktop.mdl-cell--9-col-desktop {\n width: 75%; }\n .mdl-cell--10-col,\n .mdl-cell--10-col-desktop.mdl-cell--10-col-desktop {\n width: calc(83.3333333333% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--10-col, .mdl-grid--no-spacing >\n .mdl-cell--10-col-desktop.mdl-cell--10-col-desktop {\n width: 83.3333333333%; }\n .mdl-cell--11-col,\n .mdl-cell--11-col-desktop.mdl-cell--11-col-desktop {\n width: calc(91.6666666667% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--11-col, .mdl-grid--no-spacing >\n .mdl-cell--11-col-desktop.mdl-cell--11-col-desktop {\n width: 91.6666666667%; }\n .mdl-cell--12-col,\n .mdl-cell--12-col-desktop.mdl-cell--12-col-desktop {\n width: calc(100% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--12-col, .mdl-grid--no-spacing >\n .mdl-cell--12-col-desktop.mdl-cell--12-col-desktop {\n width: 100%; } }\n","@charset \"UTF-8\";\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* Material Design Lite */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* Typography */\n/* Shadows */\n/* Animations */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n/*\n * What follows is the result of much research on cross-browser styling.\n * Credit left inline and big thanks to Nicolas Gallagher, Jonathan Neal,\n * Kroc Camen, and the H5BP dev community and team.\n */\n/* ==========================================================================\n Base styles: opinionated defaults\n ========================================================================== */\nhtml {\n color: rgba(0,0,0, 0.87);\n font-size: 1em;\n line-height: 1.4; }\n\n/*\n * Remove text-shadow in selection highlight:\n * https://twitter.com/miketaylr/status/12228805301\n *\n * These selection rule sets have to be separate.\n * Customize the background color to match your design.\n */\n::selection {\n background: #b3d4fc;\n text-shadow: none; }\n\n/*\n * A better looking default horizontal rule\n */\nhr {\n display: block;\n height: 1px;\n border: 0;\n border-top: 1px solid #ccc;\n margin: 1em 0;\n padding: 0; }\n\n/*\n * Remove the gap between audio, canvas, iframes,\n * images, videos and the bottom of their containers:\n * https://github.com/h5bp/html5-boilerplate/issues/440\n */\naudio,\ncanvas,\niframe,\nimg,\nsvg,\nvideo {\n vertical-align: middle; }\n\n/*\n * Remove default fieldset styles.\n */\nfieldset {\n border: 0;\n margin: 0;\n padding: 0; }\n\n/*\n * Allow only vertical resizing of textareas.\n */\ntextarea {\n resize: vertical; }\n\n/* ==========================================================================\n Browser Upgrade Prompt\n ========================================================================== */\n.browserupgrade {\n margin: 0.2em 0;\n background: #ccc;\n color: #000;\n padding: 0.2em 0; }\n\n/* ==========================================================================\n Author's custom styles\n ========================================================================== */\n/* ==========================================================================\n Helper classes\n ========================================================================== */\n/*\n * Hide visually and from screen readers:\n */\n.hidden {\n display: none !important; }\n\n/*\n * Hide only visually, but have it available for screen readers:\n * http://snook.ca/archives/html_and_css/hiding-content-for-accessibility\n */\n.visuallyhidden {\n border: 0;\n clip: rect(0 0 0 0);\n height: 1px;\n margin: -1px;\n overflow: hidden;\n padding: 0;\n position: absolute;\n width: 1px; }\n\n/*\n * Extends the .visuallyhidden class to allow the element\n * to be focusable when navigated to via the keyboard:\n * https://www.drupal.org/node/897638\n */\n.visuallyhidden.focusable:active,\n.visuallyhidden.focusable:focus {\n clip: auto;\n height: auto;\n margin: 0;\n overflow: visible;\n position: static;\n width: auto; }\n\n/*\n * Hide visually and from screen readers, but maintain layout\n */\n.invisible {\n visibility: hidden; }\n\n/*\n * Clearfix: contain floats\n *\n * For modern browsers\n * 1. The space content is one way to avoid an Opera bug when the\n * `contenteditable` attribute is included anywhere else in the document.\n * Otherwise it causes space to appear at the top and bottom of elements\n * that receive the `clearfix` class.\n * 2. The use of `table` rather than `block` is only necessary if using\n * `:before` to contain the top-margins of child elements.\n */\n.clearfix:before,\n.clearfix:after {\n content: \" \";\n /* 1 */\n display: table;\n /* 2 */ }\n\n.clearfix:after {\n clear: both; }\n\n/* ==========================================================================\n EXAMPLE Media Queries for Responsive Design.\n These examples override the primary ('mobile first') styles.\n Modify as content requires.\n ========================================================================== */\n/* ==========================================================================\n Print styles.\n Inlined to avoid the additional HTTP request:\n http://www.phpied.com/delay-loading-your-print-css/\n ========================================================================== */\n@media print {\n *,\n *:before,\n *:after,\n *:first-letter,\n *:first-line {\n background: transparent !important;\n color: #000 !important;\n /* Black prints faster: http://www.sanbeiji.com/archives/953 */\n box-shadow: none !important;\n text-shadow: none !important; }\n a,\n a:visited {\n text-decoration: underline; }\n a[href]:after {\n content: \" (\" attr(href) \")\"; }\n abbr[title]:after {\n content: \" (\" attr(title) \")\"; }\n /*\n * Don't show links that are fragment identifiers,\n * or use the `javascript:` pseudo protocol\n */\n a[href^=\"#\"]:after,\n a[href^=\"javascript:\"]:after {\n content: \"\"; }\n pre,\n blockquote {\n border: 1px solid #999;\n page-break-inside: avoid; }\n /*\n * Printing Tables:\n * http://css-discuss.incutio.com/wiki/Printing_Tables\n */\n thead {\n display: table-header-group; }\n tr,\n img {\n page-break-inside: avoid; }\n img {\n max-width: 100% !important; }\n p,\n h2,\n h3 {\n orphans: 3;\n widows: 3; }\n h2,\n h3 {\n page-break-after: avoid; } }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* Remove the unwanted box around FAB buttons */\n/* More info: http://goo.gl/IPwKi */\na, .mdl-accordion, .mdl-button, .mdl-card, .mdl-checkbox, .mdl-dropdown-menu,\n.mdl-icon-toggle, .mdl-item, .mdl-radio, .mdl-slider, .mdl-switch, .mdl-tabs__tab {\n -webkit-tap-highlight-color: transparent;\n -webkit-tap-highlight-color: rgba(255, 255, 255, 0); }\n\n/*\n * Make html take up the entire screen\n * Then set touch-action to avoid touch delay on mobile IE\n */\nhtml {\n width: 100%;\n height: 100%;\n -ms-touch-action: manipulation;\n touch-action: manipulation; }\n\n/*\n* Make body take up the entire screen\n* Remove body margin so layout containers don't cause extra overflow.\n*/\nbody {\n width: 100%;\n min-height: 100%;\n margin: 0; }\n\n/*\n * Main display reset for IE support.\n * Source: http://weblog.west-wind.com/posts/2015/Jan/12/main-HTML5-Tag-not-working-in-Internet-Explorer-91011\n */\nmain {\n display: block; }\n\n/*\n* Apply no display to elements with the hidden attribute.\n* IE 9 and 10 support.\n*/\n*[hidden] {\n display: none !important; }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* Typography */\n/* Shadows */\n/* Animations */\nhtml, body {\n font-family: \"Helvetica\", \"Arial\", sans-serif;\n font-size: 14px;\n font-weight: 400;\n line-height: 20px; }\n\nh1, h2, h3, h4, h5, h6, p {\n margin: 0;\n padding: 0; }\n\n/**\n * Styles for HTML elements\n */\nh1 small, h2 small, h3 small, h4 small, h5 small, h6 small {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 56px;\n font-weight: 400;\n line-height: 1.35;\n letter-spacing: -0.02em;\n opacity: 0.54;\n font-size: 0.6em; }\n\nh1 {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 56px;\n font-weight: 400;\n line-height: 1.35;\n letter-spacing: -0.02em;\n margin-top: 24px;\n margin-bottom: 24px; }\n\nh2 {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 45px;\n font-weight: 400;\n line-height: 48px;\n margin-top: 24px;\n margin-bottom: 24px; }\n\nh3 {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 34px;\n font-weight: 400;\n line-height: 40px;\n margin-top: 24px;\n margin-bottom: 24px; }\n\nh4 {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 24px;\n font-weight: 400;\n line-height: 32px;\n -moz-osx-font-smoothing: grayscale;\n margin-top: 24px;\n margin-bottom: 16px; }\n\nh5 {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 20px;\n font-weight: 500;\n line-height: 1;\n letter-spacing: 0.02em;\n margin-top: 24px;\n margin-bottom: 16px; }\n\nh6 {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 16px;\n font-weight: 400;\n line-height: 24px;\n letter-spacing: 0.04em;\n margin-top: 24px;\n margin-bottom: 16px; }\n\np {\n font-size: 14px;\n font-weight: 400;\n line-height: 24px;\n letter-spacing: 0;\n margin-bottom: 16px; }\n\na {\n color: rgb(255,64,129);\n font-weight: 500; }\n\nblockquote {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n position: relative;\n font-size: 24px;\n font-weight: 300;\n font-style: italic;\n line-height: 1.35;\n letter-spacing: 0.08em; }\n blockquote:before {\n position: absolute;\n left: -0.5em;\n content: '“'; }\n blockquote:after {\n content: '”';\n margin-left: -0.05em; }\n\nmark {\n background-color: #f4ff81; }\n\ndt {\n font-weight: 700; }\n\naddress {\n font-size: 12px;\n font-weight: 400;\n line-height: 1;\n letter-spacing: 0;\n font-style: normal; }\n\nul, ol {\n font-size: 14px;\n font-weight: 400;\n line-height: 24px;\n letter-spacing: 0; }\n\n/**\n * Class Name Styles\n */\n.mdl-typography--display-4 {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 112px;\n font-weight: 300;\n line-height: 1;\n letter-spacing: -0.04em; }\n\n.mdl-typography--display-4-color-contrast {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 112px;\n font-weight: 300;\n line-height: 1;\n letter-spacing: -0.04em;\n opacity: 0.54; }\n\n.mdl-typography--display-3 {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 56px;\n font-weight: 400;\n line-height: 1.35;\n letter-spacing: -0.02em; }\n\n.mdl-typography--display-3-color-contrast {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 56px;\n font-weight: 400;\n line-height: 1.35;\n letter-spacing: -0.02em;\n opacity: 0.54; }\n\n.mdl-typography--display-2 {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 45px;\n font-weight: 400;\n line-height: 48px; }\n\n.mdl-typography--display-2-color-contrast {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 45px;\n font-weight: 400;\n line-height: 48px;\n opacity: 0.54; }\n\n.mdl-typography--display-1 {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 34px;\n font-weight: 400;\n line-height: 40px; }\n\n.mdl-typography--display-1-color-contrast {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 34px;\n font-weight: 400;\n line-height: 40px;\n opacity: 0.54; }\n\n.mdl-typography--headline {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 24px;\n font-weight: 400;\n line-height: 32px;\n -moz-osx-font-smoothing: grayscale; }\n\n.mdl-typography--headline-color-contrast {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 24px;\n font-weight: 400;\n line-height: 32px;\n -moz-osx-font-smoothing: grayscale;\n opacity: 0.87; }\n\n.mdl-typography--title {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 20px;\n font-weight: 500;\n line-height: 1;\n letter-spacing: 0.02em; }\n\n.mdl-typography--title-color-contrast {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 20px;\n font-weight: 500;\n line-height: 1;\n letter-spacing: 0.02em;\n opacity: 0.87; }\n\n.mdl-typography--subhead {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 16px;\n font-weight: 400;\n line-height: 24px;\n letter-spacing: 0.04em; }\n\n.mdl-typography--subhead-color-contrast {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 16px;\n font-weight: 400;\n line-height: 24px;\n letter-spacing: 0.04em;\n opacity: 0.87; }\n\n.mdl-typography--body-2 {\n font-size: 14px;\n font-weight: bold;\n line-height: 24px;\n letter-spacing: 0; }\n\n.mdl-typography--body-2-color-contrast {\n font-size: 14px;\n font-weight: bold;\n line-height: 24px;\n letter-spacing: 0;\n opacity: 0.87; }\n\n.mdl-typography--body-1 {\n font-size: 14px;\n font-weight: 400;\n line-height: 24px;\n letter-spacing: 0; }\n\n.mdl-typography--body-1-color-contrast {\n font-size: 14px;\n font-weight: 400;\n line-height: 24px;\n letter-spacing: 0;\n opacity: 0.87; }\n\n.mdl-typography--body-2-force-preferred-font {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 14px;\n font-weight: 500;\n line-height: 24px;\n letter-spacing: 0; }\n\n.mdl-typography--body-2-force-preferred-font-color-contrast {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 14px;\n font-weight: 500;\n line-height: 24px;\n letter-spacing: 0;\n opacity: 0.87; }\n\n.mdl-typography--body-1-force-preferred-font {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 14px;\n font-weight: 400;\n line-height: 24px;\n letter-spacing: 0; }\n\n.mdl-typography--body-1-force-preferred-font-color-contrast {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 14px;\n font-weight: 400;\n line-height: 24px;\n letter-spacing: 0;\n opacity: 0.87; }\n\n.mdl-typography--caption {\n font-size: 12px;\n font-weight: 400;\n line-height: 1;\n letter-spacing: 0; }\n\n.mdl-typography--caption-force-preferred-font {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 12px;\n font-weight: 400;\n line-height: 1;\n letter-spacing: 0; }\n\n.mdl-typography--caption-color-contrast {\n font-size: 12px;\n font-weight: 400;\n line-height: 1;\n letter-spacing: 0;\n opacity: 0.54; }\n\n.mdl-typography--caption-force-preferred-font-color-contrast {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 12px;\n font-weight: 400;\n line-height: 1;\n letter-spacing: 0;\n opacity: 0.54; }\n\n.mdl-typography--menu {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 14px;\n font-weight: 500;\n line-height: 1;\n letter-spacing: 0; }\n\n.mdl-typography--menu-color-contrast {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 14px;\n font-weight: 500;\n line-height: 1;\n letter-spacing: 0;\n opacity: 0.87; }\n\n.mdl-typography--button {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 14px;\n font-weight: 500;\n text-transform: uppercase;\n line-height: 1;\n letter-spacing: 0; }\n\n.mdl-typography--button-color-contrast {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 14px;\n font-weight: 500;\n text-transform: uppercase;\n line-height: 1;\n letter-spacing: 0;\n opacity: 0.87; }\n\n.mdl-typography--text-left {\n text-align: left; }\n\n.mdl-typography--text-right {\n text-align: right; }\n\n.mdl-typography--text-center {\n text-align: center; }\n\n.mdl-typography--text-justify {\n text-align: justify; }\n\n.mdl-typography--text-nowrap {\n white-space: nowrap; }\n\n.mdl-typography--text-lowercase {\n text-transform: lowercase; }\n\n.mdl-typography--text-uppercase {\n text-transform: uppercase; }\n\n.mdl-typography--text-capitalize {\n text-transform: capitalize; }\n\n.mdl-typography--font-thin {\n font-weight: 200 !important; }\n\n.mdl-typography--font-light {\n font-weight: 300 !important; }\n\n.mdl-typography--font-regular {\n font-weight: 400 !important; }\n\n.mdl-typography--font-medium {\n font-weight: 500 !important; }\n\n.mdl-typography--font-bold {\n font-weight: 700 !important; }\n\n.mdl-typography--font-black {\n font-weight: 900 !important; }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n.mdl-color-text--red {\n color: rgb(244,67,54) !important; }\n\n.mdl-color--red {\n background-color: rgb(244,67,54) !important; }\n\n.mdl-color-text--red-50 {\n color: rgb(255,235,238) !important; }\n\n.mdl-color--red-50 {\n background-color: rgb(255,235,238) !important; }\n\n.mdl-color-text--red-100 {\n color: rgb(255,205,210) !important; }\n\n.mdl-color--red-100 {\n background-color: rgb(255,205,210) !important; }\n\n.mdl-color-text--red-200 {\n color: rgb(239,154,154) !important; }\n\n.mdl-color--red-200 {\n background-color: rgb(239,154,154) !important; }\n\n.mdl-color-text--red-300 {\n color: rgb(229,115,115) !important; }\n\n.mdl-color--red-300 {\n background-color: rgb(229,115,115) !important; }\n\n.mdl-color-text--red-400 {\n color: rgb(239,83,80) !important; }\n\n.mdl-color--red-400 {\n background-color: rgb(239,83,80) !important; }\n\n.mdl-color-text--red-500 {\n color: rgb(244,67,54) !important; }\n\n.mdl-color--red-500 {\n background-color: rgb(244,67,54) !important; }\n\n.mdl-color-text--red-600 {\n color: rgb(229,57,53) !important; }\n\n.mdl-color--red-600 {\n background-color: rgb(229,57,53) !important; }\n\n.mdl-color-text--red-700 {\n color: rgb(211,47,47) !important; }\n\n.mdl-color--red-700 {\n background-color: rgb(211,47,47) !important; }\n\n.mdl-color-text--red-800 {\n color: rgb(198,40,40) !important; }\n\n.mdl-color--red-800 {\n background-color: rgb(198,40,40) !important; }\n\n.mdl-color-text--red-900 {\n color: rgb(183,28,28) !important; }\n\n.mdl-color--red-900 {\n background-color: rgb(183,28,28) !important; }\n\n.mdl-color-text--red-A100 {\n color: rgb(255,138,128) !important; }\n\n.mdl-color--red-A100 {\n background-color: rgb(255,138,128) !important; }\n\n.mdl-color-text--red-A200 {\n color: rgb(255,82,82) !important; }\n\n.mdl-color--red-A200 {\n background-color: rgb(255,82,82) !important; }\n\n.mdl-color-text--red-A400 {\n color: rgb(255,23,68) !important; }\n\n.mdl-color--red-A400 {\n background-color: rgb(255,23,68) !important; }\n\n.mdl-color-text--red-A700 {\n color: rgb(213,0,0) !important; }\n\n.mdl-color--red-A700 {\n background-color: rgb(213,0,0) !important; }\n\n.mdl-color-text--pink {\n color: rgb(233,30,99) !important; }\n\n.mdl-color--pink {\n background-color: rgb(233,30,99) !important; }\n\n.mdl-color-text--pink-50 {\n color: rgb(252,228,236) !important; }\n\n.mdl-color--pink-50 {\n background-color: rgb(252,228,236) !important; }\n\n.mdl-color-text--pink-100 {\n color: rgb(248,187,208) !important; }\n\n.mdl-color--pink-100 {\n background-color: rgb(248,187,208) !important; }\n\n.mdl-color-text--pink-200 {\n color: rgb(244,143,177) !important; }\n\n.mdl-color--pink-200 {\n background-color: rgb(244,143,177) !important; }\n\n.mdl-color-text--pink-300 {\n color: rgb(240,98,146) !important; }\n\n.mdl-color--pink-300 {\n background-color: rgb(240,98,146) !important; }\n\n.mdl-color-text--pink-400 {\n color: rgb(236,64,122) !important; }\n\n.mdl-color--pink-400 {\n background-color: rgb(236,64,122) !important; }\n\n.mdl-color-text--pink-500 {\n color: rgb(233,30,99) !important; }\n\n.mdl-color--pink-500 {\n background-color: rgb(233,30,99) !important; }\n\n.mdl-color-text--pink-600 {\n color: rgb(216,27,96) !important; }\n\n.mdl-color--pink-600 {\n background-color: rgb(216,27,96) !important; }\n\n.mdl-color-text--pink-700 {\n color: rgb(194,24,91) !important; }\n\n.mdl-color--pink-700 {\n background-color: rgb(194,24,91) !important; }\n\n.mdl-color-text--pink-800 {\n color: rgb(173,20,87) !important; }\n\n.mdl-color--pink-800 {\n background-color: rgb(173,20,87) !important; }\n\n.mdl-color-text--pink-900 {\n color: rgb(136,14,79) !important; }\n\n.mdl-color--pink-900 {\n background-color: rgb(136,14,79) !important; }\n\n.mdl-color-text--pink-A100 {\n color: rgb(255,128,171) !important; }\n\n.mdl-color--pink-A100 {\n background-color: rgb(255,128,171) !important; }\n\n.mdl-color-text--pink-A200 {\n color: rgb(255,64,129) !important; }\n\n.mdl-color--pink-A200 {\n background-color: rgb(255,64,129) !important; }\n\n.mdl-color-text--pink-A400 {\n color: rgb(245,0,87) !important; }\n\n.mdl-color--pink-A400 {\n background-color: rgb(245,0,87) !important; }\n\n.mdl-color-text--pink-A700 {\n color: rgb(197,17,98) !important; }\n\n.mdl-color--pink-A700 {\n background-color: rgb(197,17,98) !important; }\n\n.mdl-color-text--purple {\n color: rgb(156,39,176) !important; }\n\n.mdl-color--purple {\n background-color: rgb(156,39,176) !important; }\n\n.mdl-color-text--purple-50 {\n color: rgb(243,229,245) !important; }\n\n.mdl-color--purple-50 {\n background-color: rgb(243,229,245) !important; }\n\n.mdl-color-text--purple-100 {\n color: rgb(225,190,231) !important; }\n\n.mdl-color--purple-100 {\n background-color: rgb(225,190,231) !important; }\n\n.mdl-color-text--purple-200 {\n color: rgb(206,147,216) !important; }\n\n.mdl-color--purple-200 {\n background-color: rgb(206,147,216) !important; }\n\n.mdl-color-text--purple-300 {\n color: rgb(186,104,200) !important; }\n\n.mdl-color--purple-300 {\n background-color: rgb(186,104,200) !important; }\n\n.mdl-color-text--purple-400 {\n color: rgb(171,71,188) !important; }\n\n.mdl-color--purple-400 {\n background-color: rgb(171,71,188) !important; }\n\n.mdl-color-text--purple-500 {\n color: rgb(156,39,176) !important; }\n\n.mdl-color--purple-500 {\n background-color: rgb(156,39,176) !important; }\n\n.mdl-color-text--purple-600 {\n color: rgb(142,36,170) !important; }\n\n.mdl-color--purple-600 {\n background-color: rgb(142,36,170) !important; }\n\n.mdl-color-text--purple-700 {\n color: rgb(123,31,162) !important; }\n\n.mdl-color--purple-700 {\n background-color: rgb(123,31,162) !important; }\n\n.mdl-color-text--purple-800 {\n color: rgb(106,27,154) !important; }\n\n.mdl-color--purple-800 {\n background-color: rgb(106,27,154) !important; }\n\n.mdl-color-text--purple-900 {\n color: rgb(74,20,140) !important; }\n\n.mdl-color--purple-900 {\n background-color: rgb(74,20,140) !important; }\n\n.mdl-color-text--purple-A100 {\n color: rgb(234,128,252) !important; }\n\n.mdl-color--purple-A100 {\n background-color: rgb(234,128,252) !important; }\n\n.mdl-color-text--purple-A200 {\n color: rgb(224,64,251) !important; }\n\n.mdl-color--purple-A200 {\n background-color: rgb(224,64,251) !important; }\n\n.mdl-color-text--purple-A400 {\n color: rgb(213,0,249) !important; }\n\n.mdl-color--purple-A400 {\n background-color: rgb(213,0,249) !important; }\n\n.mdl-color-text--purple-A700 {\n color: rgb(170,0,255) !important; }\n\n.mdl-color--purple-A700 {\n background-color: rgb(170,0,255) !important; }\n\n.mdl-color-text--deep-purple {\n color: rgb(103,58,183) !important; }\n\n.mdl-color--deep-purple {\n background-color: rgb(103,58,183) !important; }\n\n.mdl-color-text--deep-purple-50 {\n color: rgb(237,231,246) !important; }\n\n.mdl-color--deep-purple-50 {\n background-color: rgb(237,231,246) !important; }\n\n.mdl-color-text--deep-purple-100 {\n color: rgb(209,196,233) !important; }\n\n.mdl-color--deep-purple-100 {\n background-color: rgb(209,196,233) !important; }\n\n.mdl-color-text--deep-purple-200 {\n color: rgb(179,157,219) !important; }\n\n.mdl-color--deep-purple-200 {\n background-color: rgb(179,157,219) !important; }\n\n.mdl-color-text--deep-purple-300 {\n color: rgb(149,117,205) !important; }\n\n.mdl-color--deep-purple-300 {\n background-color: rgb(149,117,205) !important; }\n\n.mdl-color-text--deep-purple-400 {\n color: rgb(126,87,194) !important; }\n\n.mdl-color--deep-purple-400 {\n background-color: rgb(126,87,194) !important; }\n\n.mdl-color-text--deep-purple-500 {\n color: rgb(103,58,183) !important; }\n\n.mdl-color--deep-purple-500 {\n background-color: rgb(103,58,183) !important; }\n\n.mdl-color-text--deep-purple-600 {\n color: rgb(94,53,177) !important; }\n\n.mdl-color--deep-purple-600 {\n background-color: rgb(94,53,177) !important; }\n\n.mdl-color-text--deep-purple-700 {\n color: rgb(81,45,168) !important; }\n\n.mdl-color--deep-purple-700 {\n background-color: rgb(81,45,168) !important; }\n\n.mdl-color-text--deep-purple-800 {\n color: rgb(69,39,160) !important; }\n\n.mdl-color--deep-purple-800 {\n background-color: rgb(69,39,160) !important; }\n\n.mdl-color-text--deep-purple-900 {\n color: rgb(49,27,146) !important; }\n\n.mdl-color--deep-purple-900 {\n background-color: rgb(49,27,146) !important; }\n\n.mdl-color-text--deep-purple-A100 {\n color: rgb(179,136,255) !important; }\n\n.mdl-color--deep-purple-A100 {\n background-color: rgb(179,136,255) !important; }\n\n.mdl-color-text--deep-purple-A200 {\n color: rgb(124,77,255) !important; }\n\n.mdl-color--deep-purple-A200 {\n background-color: rgb(124,77,255) !important; }\n\n.mdl-color-text--deep-purple-A400 {\n color: rgb(101,31,255) !important; }\n\n.mdl-color--deep-purple-A400 {\n background-color: rgb(101,31,255) !important; }\n\n.mdl-color-text--deep-purple-A700 {\n color: rgb(98,0,234) !important; }\n\n.mdl-color--deep-purple-A700 {\n background-color: rgb(98,0,234) !important; }\n\n.mdl-color-text--indigo {\n color: rgb(63,81,181) !important; }\n\n.mdl-color--indigo {\n background-color: rgb(63,81,181) !important; }\n\n.mdl-color-text--indigo-50 {\n color: rgb(232,234,246) !important; }\n\n.mdl-color--indigo-50 {\n background-color: rgb(232,234,246) !important; }\n\n.mdl-color-text--indigo-100 {\n color: rgb(197,202,233) !important; }\n\n.mdl-color--indigo-100 {\n background-color: rgb(197,202,233) !important; }\n\n.mdl-color-text--indigo-200 {\n color: rgb(159,168,218) !important; }\n\n.mdl-color--indigo-200 {\n background-color: rgb(159,168,218) !important; }\n\n.mdl-color-text--indigo-300 {\n color: rgb(121,134,203) !important; }\n\n.mdl-color--indigo-300 {\n background-color: rgb(121,134,203) !important; }\n\n.mdl-color-text--indigo-400 {\n color: rgb(92,107,192) !important; }\n\n.mdl-color--indigo-400 {\n background-color: rgb(92,107,192) !important; }\n\n.mdl-color-text--indigo-500 {\n color: rgb(63,81,181) !important; }\n\n.mdl-color--indigo-500 {\n background-color: rgb(63,81,181) !important; }\n\n.mdl-color-text--indigo-600 {\n color: rgb(57,73,171) !important; }\n\n.mdl-color--indigo-600 {\n background-color: rgb(57,73,171) !important; }\n\n.mdl-color-text--indigo-700 {\n color: rgb(48,63,159) !important; }\n\n.mdl-color--indigo-700 {\n background-color: rgb(48,63,159) !important; }\n\n.mdl-color-text--indigo-800 {\n color: rgb(40,53,147) !important; }\n\n.mdl-color--indigo-800 {\n background-color: rgb(40,53,147) !important; }\n\n.mdl-color-text--indigo-900 {\n color: rgb(26,35,126) !important; }\n\n.mdl-color--indigo-900 {\n background-color: rgb(26,35,126) !important; }\n\n.mdl-color-text--indigo-A100 {\n color: rgb(140,158,255) !important; }\n\n.mdl-color--indigo-A100 {\n background-color: rgb(140,158,255) !important; }\n\n.mdl-color-text--indigo-A200 {\n color: rgb(83,109,254) !important; }\n\n.mdl-color--indigo-A200 {\n background-color: rgb(83,109,254) !important; }\n\n.mdl-color-text--indigo-A400 {\n color: rgb(61,90,254) !important; }\n\n.mdl-color--indigo-A400 {\n background-color: rgb(61,90,254) !important; }\n\n.mdl-color-text--indigo-A700 {\n color: rgb(48,79,254) !important; }\n\n.mdl-color--indigo-A700 {\n background-color: rgb(48,79,254) !important; }\n\n.mdl-color-text--blue {\n color: rgb(33,150,243) !important; }\n\n.mdl-color--blue {\n background-color: rgb(33,150,243) !important; }\n\n.mdl-color-text--blue-50 {\n color: rgb(227,242,253) !important; }\n\n.mdl-color--blue-50 {\n background-color: rgb(227,242,253) !important; }\n\n.mdl-color-text--blue-100 {\n color: rgb(187,222,251) !important; }\n\n.mdl-color--blue-100 {\n background-color: rgb(187,222,251) !important; }\n\n.mdl-color-text--blue-200 {\n color: rgb(144,202,249) !important; }\n\n.mdl-color--blue-200 {\n background-color: rgb(144,202,249) !important; }\n\n.mdl-color-text--blue-300 {\n color: rgb(100,181,246) !important; }\n\n.mdl-color--blue-300 {\n background-color: rgb(100,181,246) !important; }\n\n.mdl-color-text--blue-400 {\n color: rgb(66,165,245) !important; }\n\n.mdl-color--blue-400 {\n background-color: rgb(66,165,245) !important; }\n\n.mdl-color-text--blue-500 {\n color: rgb(33,150,243) !important; }\n\n.mdl-color--blue-500 {\n background-color: rgb(33,150,243) !important; }\n\n.mdl-color-text--blue-600 {\n color: rgb(30,136,229) !important; }\n\n.mdl-color--blue-600 {\n background-color: rgb(30,136,229) !important; }\n\n.mdl-color-text--blue-700 {\n color: rgb(25,118,210) !important; }\n\n.mdl-color--blue-700 {\n background-color: rgb(25,118,210) !important; }\n\n.mdl-color-text--blue-800 {\n color: rgb(21,101,192) !important; }\n\n.mdl-color--blue-800 {\n background-color: rgb(21,101,192) !important; }\n\n.mdl-color-text--blue-900 {\n color: rgb(13,71,161) !important; }\n\n.mdl-color--blue-900 {\n background-color: rgb(13,71,161) !important; }\n\n.mdl-color-text--blue-A100 {\n color: rgb(130,177,255) !important; }\n\n.mdl-color--blue-A100 {\n background-color: rgb(130,177,255) !important; }\n\n.mdl-color-text--blue-A200 {\n color: rgb(68,138,255) !important; }\n\n.mdl-color--blue-A200 {\n background-color: rgb(68,138,255) !important; }\n\n.mdl-color-text--blue-A400 {\n color: rgb(41,121,255) !important; }\n\n.mdl-color--blue-A400 {\n background-color: rgb(41,121,255) !important; }\n\n.mdl-color-text--blue-A700 {\n color: rgb(41,98,255) !important; }\n\n.mdl-color--blue-A700 {\n background-color: rgb(41,98,255) !important; }\n\n.mdl-color-text--light-blue {\n color: rgb(3,169,244) !important; }\n\n.mdl-color--light-blue {\n background-color: rgb(3,169,244) !important; }\n\n.mdl-color-text--light-blue-50 {\n color: rgb(225,245,254) !important; }\n\n.mdl-color--light-blue-50 {\n background-color: rgb(225,245,254) !important; }\n\n.mdl-color-text--light-blue-100 {\n color: rgb(179,229,252) !important; }\n\n.mdl-color--light-blue-100 {\n background-color: rgb(179,229,252) !important; }\n\n.mdl-color-text--light-blue-200 {\n color: rgb(129,212,250) !important; }\n\n.mdl-color--light-blue-200 {\n background-color: rgb(129,212,250) !important; }\n\n.mdl-color-text--light-blue-300 {\n color: rgb(79,195,247) !important; }\n\n.mdl-color--light-blue-300 {\n background-color: rgb(79,195,247) !important; }\n\n.mdl-color-text--light-blue-400 {\n color: rgb(41,182,246) !important; }\n\n.mdl-color--light-blue-400 {\n background-color: rgb(41,182,246) !important; }\n\n.mdl-color-text--light-blue-500 {\n color: rgb(3,169,244) !important; }\n\n.mdl-color--light-blue-500 {\n background-color: rgb(3,169,244) !important; }\n\n.mdl-color-text--light-blue-600 {\n color: rgb(3,155,229) !important; }\n\n.mdl-color--light-blue-600 {\n background-color: rgb(3,155,229) !important; }\n\n.mdl-color-text--light-blue-700 {\n color: rgb(2,136,209) !important; }\n\n.mdl-color--light-blue-700 {\n background-color: rgb(2,136,209) !important; }\n\n.mdl-color-text--light-blue-800 {\n color: rgb(2,119,189) !important; }\n\n.mdl-color--light-blue-800 {\n background-color: rgb(2,119,189) !important; }\n\n.mdl-color-text--light-blue-900 {\n color: rgb(1,87,155) !important; }\n\n.mdl-color--light-blue-900 {\n background-color: rgb(1,87,155) !important; }\n\n.mdl-color-text--light-blue-A100 {\n color: rgb(128,216,255) !important; }\n\n.mdl-color--light-blue-A100 {\n background-color: rgb(128,216,255) !important; }\n\n.mdl-color-text--light-blue-A200 {\n color: rgb(64,196,255) !important; }\n\n.mdl-color--light-blue-A200 {\n background-color: rgb(64,196,255) !important; }\n\n.mdl-color-text--light-blue-A400 {\n color: rgb(0,176,255) !important; }\n\n.mdl-color--light-blue-A400 {\n background-color: rgb(0,176,255) !important; }\n\n.mdl-color-text--light-blue-A700 {\n color: rgb(0,145,234) !important; }\n\n.mdl-color--light-blue-A700 {\n background-color: rgb(0,145,234) !important; }\n\n.mdl-color-text--cyan {\n color: rgb(0,188,212) !important; }\n\n.mdl-color--cyan {\n background-color: rgb(0,188,212) !important; }\n\n.mdl-color-text--cyan-50 {\n color: rgb(224,247,250) !important; }\n\n.mdl-color--cyan-50 {\n background-color: rgb(224,247,250) !important; }\n\n.mdl-color-text--cyan-100 {\n color: rgb(178,235,242) !important; }\n\n.mdl-color--cyan-100 {\n background-color: rgb(178,235,242) !important; }\n\n.mdl-color-text--cyan-200 {\n color: rgb(128,222,234) !important; }\n\n.mdl-color--cyan-200 {\n background-color: rgb(128,222,234) !important; }\n\n.mdl-color-text--cyan-300 {\n color: rgb(77,208,225) !important; }\n\n.mdl-color--cyan-300 {\n background-color: rgb(77,208,225) !important; }\n\n.mdl-color-text--cyan-400 {\n color: rgb(38,198,218) !important; }\n\n.mdl-color--cyan-400 {\n background-color: rgb(38,198,218) !important; }\n\n.mdl-color-text--cyan-500 {\n color: rgb(0,188,212) !important; }\n\n.mdl-color--cyan-500 {\n background-color: rgb(0,188,212) !important; }\n\n.mdl-color-text--cyan-600 {\n color: rgb(0,172,193) !important; }\n\n.mdl-color--cyan-600 {\n background-color: rgb(0,172,193) !important; }\n\n.mdl-color-text--cyan-700 {\n color: rgb(0,151,167) !important; }\n\n.mdl-color--cyan-700 {\n background-color: rgb(0,151,167) !important; }\n\n.mdl-color-text--cyan-800 {\n color: rgb(0,131,143) !important; }\n\n.mdl-color--cyan-800 {\n background-color: rgb(0,131,143) !important; }\n\n.mdl-color-text--cyan-900 {\n color: rgb(0,96,100) !important; }\n\n.mdl-color--cyan-900 {\n background-color: rgb(0,96,100) !important; }\n\n.mdl-color-text--cyan-A100 {\n color: rgb(132,255,255) !important; }\n\n.mdl-color--cyan-A100 {\n background-color: rgb(132,255,255) !important; }\n\n.mdl-color-text--cyan-A200 {\n color: rgb(24,255,255) !important; }\n\n.mdl-color--cyan-A200 {\n background-color: rgb(24,255,255) !important; }\n\n.mdl-color-text--cyan-A400 {\n color: rgb(0,229,255) !important; }\n\n.mdl-color--cyan-A400 {\n background-color: rgb(0,229,255) !important; }\n\n.mdl-color-text--cyan-A700 {\n color: rgb(0,184,212) !important; }\n\n.mdl-color--cyan-A700 {\n background-color: rgb(0,184,212) !important; }\n\n.mdl-color-text--teal {\n color: rgb(0,150,136) !important; }\n\n.mdl-color--teal {\n background-color: rgb(0,150,136) !important; }\n\n.mdl-color-text--teal-50 {\n color: rgb(224,242,241) !important; }\n\n.mdl-color--teal-50 {\n background-color: rgb(224,242,241) !important; }\n\n.mdl-color-text--teal-100 {\n color: rgb(178,223,219) !important; }\n\n.mdl-color--teal-100 {\n background-color: rgb(178,223,219) !important; }\n\n.mdl-color-text--teal-200 {\n color: rgb(128,203,196) !important; }\n\n.mdl-color--teal-200 {\n background-color: rgb(128,203,196) !important; }\n\n.mdl-color-text--teal-300 {\n color: rgb(77,182,172) !important; }\n\n.mdl-color--teal-300 {\n background-color: rgb(77,182,172) !important; }\n\n.mdl-color-text--teal-400 {\n color: rgb(38,166,154) !important; }\n\n.mdl-color--teal-400 {\n background-color: rgb(38,166,154) !important; }\n\n.mdl-color-text--teal-500 {\n color: rgb(0,150,136) !important; }\n\n.mdl-color--teal-500 {\n background-color: rgb(0,150,136) !important; }\n\n.mdl-color-text--teal-600 {\n color: rgb(0,137,123) !important; }\n\n.mdl-color--teal-600 {\n background-color: rgb(0,137,123) !important; }\n\n.mdl-color-text--teal-700 {\n color: rgb(0,121,107) !important; }\n\n.mdl-color--teal-700 {\n background-color: rgb(0,121,107) !important; }\n\n.mdl-color-text--teal-800 {\n color: rgb(0,105,92) !important; }\n\n.mdl-color--teal-800 {\n background-color: rgb(0,105,92) !important; }\n\n.mdl-color-text--teal-900 {\n color: rgb(0,77,64) !important; }\n\n.mdl-color--teal-900 {\n background-color: rgb(0,77,64) !important; }\n\n.mdl-color-text--teal-A100 {\n color: rgb(167,255,235) !important; }\n\n.mdl-color--teal-A100 {\n background-color: rgb(167,255,235) !important; }\n\n.mdl-color-text--teal-A200 {\n color: rgb(100,255,218) !important; }\n\n.mdl-color--teal-A200 {\n background-color: rgb(100,255,218) !important; }\n\n.mdl-color-text--teal-A400 {\n color: rgb(29,233,182) !important; }\n\n.mdl-color--teal-A400 {\n background-color: rgb(29,233,182) !important; }\n\n.mdl-color-text--teal-A700 {\n color: rgb(0,191,165) !important; }\n\n.mdl-color--teal-A700 {\n background-color: rgb(0,191,165) !important; }\n\n.mdl-color-text--green {\n color: rgb(76,175,80) !important; }\n\n.mdl-color--green {\n background-color: rgb(76,175,80) !important; }\n\n.mdl-color-text--green-50 {\n color: rgb(232,245,233) !important; }\n\n.mdl-color--green-50 {\n background-color: rgb(232,245,233) !important; }\n\n.mdl-color-text--green-100 {\n color: rgb(200,230,201) !important; }\n\n.mdl-color--green-100 {\n background-color: rgb(200,230,201) !important; }\n\n.mdl-color-text--green-200 {\n color: rgb(165,214,167) !important; }\n\n.mdl-color--green-200 {\n background-color: rgb(165,214,167) !important; }\n\n.mdl-color-text--green-300 {\n color: rgb(129,199,132) !important; }\n\n.mdl-color--green-300 {\n background-color: rgb(129,199,132) !important; }\n\n.mdl-color-text--green-400 {\n color: rgb(102,187,106) !important; }\n\n.mdl-color--green-400 {\n background-color: rgb(102,187,106) !important; }\n\n.mdl-color-text--green-500 {\n color: rgb(76,175,80) !important; }\n\n.mdl-color--green-500 {\n background-color: rgb(76,175,80) !important; }\n\n.mdl-color-text--green-600 {\n color: rgb(67,160,71) !important; }\n\n.mdl-color--green-600 {\n background-color: rgb(67,160,71) !important; }\n\n.mdl-color-text--green-700 {\n color: rgb(56,142,60) !important; }\n\n.mdl-color--green-700 {\n background-color: rgb(56,142,60) !important; }\n\n.mdl-color-text--green-800 {\n color: rgb(46,125,50) !important; }\n\n.mdl-color--green-800 {\n background-color: rgb(46,125,50) !important; }\n\n.mdl-color-text--green-900 {\n color: rgb(27,94,32) !important; }\n\n.mdl-color--green-900 {\n background-color: rgb(27,94,32) !important; }\n\n.mdl-color-text--green-A100 {\n color: rgb(185,246,202) !important; }\n\n.mdl-color--green-A100 {\n background-color: rgb(185,246,202) !important; }\n\n.mdl-color-text--green-A200 {\n color: rgb(105,240,174) !important; }\n\n.mdl-color--green-A200 {\n background-color: rgb(105,240,174) !important; }\n\n.mdl-color-text--green-A400 {\n color: rgb(0,230,118) !important; }\n\n.mdl-color--green-A400 {\n background-color: rgb(0,230,118) !important; }\n\n.mdl-color-text--green-A700 {\n color: rgb(0,200,83) !important; }\n\n.mdl-color--green-A700 {\n background-color: rgb(0,200,83) !important; }\n\n.mdl-color-text--light-green {\n color: rgb(139,195,74) !important; }\n\n.mdl-color--light-green {\n background-color: rgb(139,195,74) !important; }\n\n.mdl-color-text--light-green-50 {\n color: rgb(241,248,233) !important; }\n\n.mdl-color--light-green-50 {\n background-color: rgb(241,248,233) !important; }\n\n.mdl-color-text--light-green-100 {\n color: rgb(220,237,200) !important; }\n\n.mdl-color--light-green-100 {\n background-color: rgb(220,237,200) !important; }\n\n.mdl-color-text--light-green-200 {\n color: rgb(197,225,165) !important; }\n\n.mdl-color--light-green-200 {\n background-color: rgb(197,225,165) !important; }\n\n.mdl-color-text--light-green-300 {\n color: rgb(174,213,129) !important; }\n\n.mdl-color--light-green-300 {\n background-color: rgb(174,213,129) !important; }\n\n.mdl-color-text--light-green-400 {\n color: rgb(156,204,101) !important; }\n\n.mdl-color--light-green-400 {\n background-color: rgb(156,204,101) !important; }\n\n.mdl-color-text--light-green-500 {\n color: rgb(139,195,74) !important; }\n\n.mdl-color--light-green-500 {\n background-color: rgb(139,195,74) !important; }\n\n.mdl-color-text--light-green-600 {\n color: rgb(124,179,66) !important; }\n\n.mdl-color--light-green-600 {\n background-color: rgb(124,179,66) !important; }\n\n.mdl-color-text--light-green-700 {\n color: rgb(104,159,56) !important; }\n\n.mdl-color--light-green-700 {\n background-color: rgb(104,159,56) !important; }\n\n.mdl-color-text--light-green-800 {\n color: rgb(85,139,47) !important; }\n\n.mdl-color--light-green-800 {\n background-color: rgb(85,139,47) !important; }\n\n.mdl-color-text--light-green-900 {\n color: rgb(51,105,30) !important; }\n\n.mdl-color--light-green-900 {\n background-color: rgb(51,105,30) !important; }\n\n.mdl-color-text--light-green-A100 {\n color: rgb(204,255,144) !important; }\n\n.mdl-color--light-green-A100 {\n background-color: rgb(204,255,144) !important; }\n\n.mdl-color-text--light-green-A200 {\n color: rgb(178,255,89) !important; }\n\n.mdl-color--light-green-A200 {\n background-color: rgb(178,255,89) !important; }\n\n.mdl-color-text--light-green-A400 {\n color: rgb(118,255,3) !important; }\n\n.mdl-color--light-green-A400 {\n background-color: rgb(118,255,3) !important; }\n\n.mdl-color-text--light-green-A700 {\n color: rgb(100,221,23) !important; }\n\n.mdl-color--light-green-A700 {\n background-color: rgb(100,221,23) !important; }\n\n.mdl-color-text--lime {\n color: rgb(205,220,57) !important; }\n\n.mdl-color--lime {\n background-color: rgb(205,220,57) !important; }\n\n.mdl-color-text--lime-50 {\n color: rgb(249,251,231) !important; }\n\n.mdl-color--lime-50 {\n background-color: rgb(249,251,231) !important; }\n\n.mdl-color-text--lime-100 {\n color: rgb(240,244,195) !important; }\n\n.mdl-color--lime-100 {\n background-color: rgb(240,244,195) !important; }\n\n.mdl-color-text--lime-200 {\n color: rgb(230,238,156) !important; }\n\n.mdl-color--lime-200 {\n background-color: rgb(230,238,156) !important; }\n\n.mdl-color-text--lime-300 {\n color: rgb(220,231,117) !important; }\n\n.mdl-color--lime-300 {\n background-color: rgb(220,231,117) !important; }\n\n.mdl-color-text--lime-400 {\n color: rgb(212,225,87) !important; }\n\n.mdl-color--lime-400 {\n background-color: rgb(212,225,87) !important; }\n\n.mdl-color-text--lime-500 {\n color: rgb(205,220,57) !important; }\n\n.mdl-color--lime-500 {\n background-color: rgb(205,220,57) !important; }\n\n.mdl-color-text--lime-600 {\n color: rgb(192,202,51) !important; }\n\n.mdl-color--lime-600 {\n background-color: rgb(192,202,51) !important; }\n\n.mdl-color-text--lime-700 {\n color: rgb(175,180,43) !important; }\n\n.mdl-color--lime-700 {\n background-color: rgb(175,180,43) !important; }\n\n.mdl-color-text--lime-800 {\n color: rgb(158,157,36) !important; }\n\n.mdl-color--lime-800 {\n background-color: rgb(158,157,36) !important; }\n\n.mdl-color-text--lime-900 {\n color: rgb(130,119,23) !important; }\n\n.mdl-color--lime-900 {\n background-color: rgb(130,119,23) !important; }\n\n.mdl-color-text--lime-A100 {\n color: rgb(244,255,129) !important; }\n\n.mdl-color--lime-A100 {\n background-color: rgb(244,255,129) !important; }\n\n.mdl-color-text--lime-A200 {\n color: rgb(238,255,65) !important; }\n\n.mdl-color--lime-A200 {\n background-color: rgb(238,255,65) !important; }\n\n.mdl-color-text--lime-A400 {\n color: rgb(198,255,0) !important; }\n\n.mdl-color--lime-A400 {\n background-color: rgb(198,255,0) !important; }\n\n.mdl-color-text--lime-A700 {\n color: rgb(174,234,0) !important; }\n\n.mdl-color--lime-A700 {\n background-color: rgb(174,234,0) !important; }\n\n.mdl-color-text--yellow {\n color: rgb(255,235,59) !important; }\n\n.mdl-color--yellow {\n background-color: rgb(255,235,59) !important; }\n\n.mdl-color-text--yellow-50 {\n color: rgb(255,253,231) !important; }\n\n.mdl-color--yellow-50 {\n background-color: rgb(255,253,231) !important; }\n\n.mdl-color-text--yellow-100 {\n color: rgb(255,249,196) !important; }\n\n.mdl-color--yellow-100 {\n background-color: rgb(255,249,196) !important; }\n\n.mdl-color-text--yellow-200 {\n color: rgb(255,245,157) !important; }\n\n.mdl-color--yellow-200 {\n background-color: rgb(255,245,157) !important; }\n\n.mdl-color-text--yellow-300 {\n color: rgb(255,241,118) !important; }\n\n.mdl-color--yellow-300 {\n background-color: rgb(255,241,118) !important; }\n\n.mdl-color-text--yellow-400 {\n color: rgb(255,238,88) !important; }\n\n.mdl-color--yellow-400 {\n background-color: rgb(255,238,88) !important; }\n\n.mdl-color-text--yellow-500 {\n color: rgb(255,235,59) !important; }\n\n.mdl-color--yellow-500 {\n background-color: rgb(255,235,59) !important; }\n\n.mdl-color-text--yellow-600 {\n color: rgb(253,216,53) !important; }\n\n.mdl-color--yellow-600 {\n background-color: rgb(253,216,53) !important; }\n\n.mdl-color-text--yellow-700 {\n color: rgb(251,192,45) !important; }\n\n.mdl-color--yellow-700 {\n background-color: rgb(251,192,45) !important; }\n\n.mdl-color-text--yellow-800 {\n color: rgb(249,168,37) !important; }\n\n.mdl-color--yellow-800 {\n background-color: rgb(249,168,37) !important; }\n\n.mdl-color-text--yellow-900 {\n color: rgb(245,127,23) !important; }\n\n.mdl-color--yellow-900 {\n background-color: rgb(245,127,23) !important; }\n\n.mdl-color-text--yellow-A100 {\n color: rgb(255,255,141) !important; }\n\n.mdl-color--yellow-A100 {\n background-color: rgb(255,255,141) !important; }\n\n.mdl-color-text--yellow-A200 {\n color: rgb(255,255,0) !important; }\n\n.mdl-color--yellow-A200 {\n background-color: rgb(255,255,0) !important; }\n\n.mdl-color-text--yellow-A400 {\n color: rgb(255,234,0) !important; }\n\n.mdl-color--yellow-A400 {\n background-color: rgb(255,234,0) !important; }\n\n.mdl-color-text--yellow-A700 {\n color: rgb(255,214,0) !important; }\n\n.mdl-color--yellow-A700 {\n background-color: rgb(255,214,0) !important; }\n\n.mdl-color-text--amber {\n color: rgb(255,193,7) !important; }\n\n.mdl-color--amber {\n background-color: rgb(255,193,7) !important; }\n\n.mdl-color-text--amber-50 {\n color: rgb(255,248,225) !important; }\n\n.mdl-color--amber-50 {\n background-color: rgb(255,248,225) !important; }\n\n.mdl-color-text--amber-100 {\n color: rgb(255,236,179) !important; }\n\n.mdl-color--amber-100 {\n background-color: rgb(255,236,179) !important; }\n\n.mdl-color-text--amber-200 {\n color: rgb(255,224,130) !important; }\n\n.mdl-color--amber-200 {\n background-color: rgb(255,224,130) !important; }\n\n.mdl-color-text--amber-300 {\n color: rgb(255,213,79) !important; }\n\n.mdl-color--amber-300 {\n background-color: rgb(255,213,79) !important; }\n\n.mdl-color-text--amber-400 {\n color: rgb(255,202,40) !important; }\n\n.mdl-color--amber-400 {\n background-color: rgb(255,202,40) !important; }\n\n.mdl-color-text--amber-500 {\n color: rgb(255,193,7) !important; }\n\n.mdl-color--amber-500 {\n background-color: rgb(255,193,7) !important; }\n\n.mdl-color-text--amber-600 {\n color: rgb(255,179,0) !important; }\n\n.mdl-color--amber-600 {\n background-color: rgb(255,179,0) !important; }\n\n.mdl-color-text--amber-700 {\n color: rgb(255,160,0) !important; }\n\n.mdl-color--amber-700 {\n background-color: rgb(255,160,0) !important; }\n\n.mdl-color-text--amber-800 {\n color: rgb(255,143,0) !important; }\n\n.mdl-color--amber-800 {\n background-color: rgb(255,143,0) !important; }\n\n.mdl-color-text--amber-900 {\n color: rgb(255,111,0) !important; }\n\n.mdl-color--amber-900 {\n background-color: rgb(255,111,0) !important; }\n\n.mdl-color-text--amber-A100 {\n color: rgb(255,229,127) !important; }\n\n.mdl-color--amber-A100 {\n background-color: rgb(255,229,127) !important; }\n\n.mdl-color-text--amber-A200 {\n color: rgb(255,215,64) !important; }\n\n.mdl-color--amber-A200 {\n background-color: rgb(255,215,64) !important; }\n\n.mdl-color-text--amber-A400 {\n color: rgb(255,196,0) !important; }\n\n.mdl-color--amber-A400 {\n background-color: rgb(255,196,0) !important; }\n\n.mdl-color-text--amber-A700 {\n color: rgb(255,171,0) !important; }\n\n.mdl-color--amber-A700 {\n background-color: rgb(255,171,0) !important; }\n\n.mdl-color-text--orange {\n color: rgb(255,152,0) !important; }\n\n.mdl-color--orange {\n background-color: rgb(255,152,0) !important; }\n\n.mdl-color-text--orange-50 {\n color: rgb(255,243,224) !important; }\n\n.mdl-color--orange-50 {\n background-color: rgb(255,243,224) !important; }\n\n.mdl-color-text--orange-100 {\n color: rgb(255,224,178) !important; }\n\n.mdl-color--orange-100 {\n background-color: rgb(255,224,178) !important; }\n\n.mdl-color-text--orange-200 {\n color: rgb(255,204,128) !important; }\n\n.mdl-color--orange-200 {\n background-color: rgb(255,204,128) !important; }\n\n.mdl-color-text--orange-300 {\n color: rgb(255,183,77) !important; }\n\n.mdl-color--orange-300 {\n background-color: rgb(255,183,77) !important; }\n\n.mdl-color-text--orange-400 {\n color: rgb(255,167,38) !important; }\n\n.mdl-color--orange-400 {\n background-color: rgb(255,167,38) !important; }\n\n.mdl-color-text--orange-500 {\n color: rgb(255,152,0) !important; }\n\n.mdl-color--orange-500 {\n background-color: rgb(255,152,0) !important; }\n\n.mdl-color-text--orange-600 {\n color: rgb(251,140,0) !important; }\n\n.mdl-color--orange-600 {\n background-color: rgb(251,140,0) !important; }\n\n.mdl-color-text--orange-700 {\n color: rgb(245,124,0) !important; }\n\n.mdl-color--orange-700 {\n background-color: rgb(245,124,0) !important; }\n\n.mdl-color-text--orange-800 {\n color: rgb(239,108,0) !important; }\n\n.mdl-color--orange-800 {\n background-color: rgb(239,108,0) !important; }\n\n.mdl-color-text--orange-900 {\n color: rgb(230,81,0) !important; }\n\n.mdl-color--orange-900 {\n background-color: rgb(230,81,0) !important; }\n\n.mdl-color-text--orange-A100 {\n color: rgb(255,209,128) !important; }\n\n.mdl-color--orange-A100 {\n background-color: rgb(255,209,128) !important; }\n\n.mdl-color-text--orange-A200 {\n color: rgb(255,171,64) !important; }\n\n.mdl-color--orange-A200 {\n background-color: rgb(255,171,64) !important; }\n\n.mdl-color-text--orange-A400 {\n color: rgb(255,145,0) !important; }\n\n.mdl-color--orange-A400 {\n background-color: rgb(255,145,0) !important; }\n\n.mdl-color-text--orange-A700 {\n color: rgb(255,109,0) !important; }\n\n.mdl-color--orange-A700 {\n background-color: rgb(255,109,0) !important; }\n\n.mdl-color-text--deep-orange {\n color: rgb(255,87,34) !important; }\n\n.mdl-color--deep-orange {\n background-color: rgb(255,87,34) !important; }\n\n.mdl-color-text--deep-orange-50 {\n color: rgb(251,233,231) !important; }\n\n.mdl-color--deep-orange-50 {\n background-color: rgb(251,233,231) !important; }\n\n.mdl-color-text--deep-orange-100 {\n color: rgb(255,204,188) !important; }\n\n.mdl-color--deep-orange-100 {\n background-color: rgb(255,204,188) !important; }\n\n.mdl-color-text--deep-orange-200 {\n color: rgb(255,171,145) !important; }\n\n.mdl-color--deep-orange-200 {\n background-color: rgb(255,171,145) !important; }\n\n.mdl-color-text--deep-orange-300 {\n color: rgb(255,138,101) !important; }\n\n.mdl-color--deep-orange-300 {\n background-color: rgb(255,138,101) !important; }\n\n.mdl-color-text--deep-orange-400 {\n color: rgb(255,112,67) !important; }\n\n.mdl-color--deep-orange-400 {\n background-color: rgb(255,112,67) !important; }\n\n.mdl-color-text--deep-orange-500 {\n color: rgb(255,87,34) !important; }\n\n.mdl-color--deep-orange-500 {\n background-color: rgb(255,87,34) !important; }\n\n.mdl-color-text--deep-orange-600 {\n color: rgb(244,81,30) !important; }\n\n.mdl-color--deep-orange-600 {\n background-color: rgb(244,81,30) !important; }\n\n.mdl-color-text--deep-orange-700 {\n color: rgb(230,74,25) !important; }\n\n.mdl-color--deep-orange-700 {\n background-color: rgb(230,74,25) !important; }\n\n.mdl-color-text--deep-orange-800 {\n color: rgb(216,67,21) !important; }\n\n.mdl-color--deep-orange-800 {\n background-color: rgb(216,67,21) !important; }\n\n.mdl-color-text--deep-orange-900 {\n color: rgb(191,54,12) !important; }\n\n.mdl-color--deep-orange-900 {\n background-color: rgb(191,54,12) !important; }\n\n.mdl-color-text--deep-orange-A100 {\n color: rgb(255,158,128) !important; }\n\n.mdl-color--deep-orange-A100 {\n background-color: rgb(255,158,128) !important; }\n\n.mdl-color-text--deep-orange-A200 {\n color: rgb(255,110,64) !important; }\n\n.mdl-color--deep-orange-A200 {\n background-color: rgb(255,110,64) !important; }\n\n.mdl-color-text--deep-orange-A400 {\n color: rgb(255,61,0) !important; }\n\n.mdl-color--deep-orange-A400 {\n background-color: rgb(255,61,0) !important; }\n\n.mdl-color-text--deep-orange-A700 {\n color: rgb(221,44,0) !important; }\n\n.mdl-color--deep-orange-A700 {\n background-color: rgb(221,44,0) !important; }\n\n.mdl-color-text--brown {\n color: rgb(121,85,72) !important; }\n\n.mdl-color--brown {\n background-color: rgb(121,85,72) !important; }\n\n.mdl-color-text--brown-50 {\n color: rgb(239,235,233) !important; }\n\n.mdl-color--brown-50 {\n background-color: rgb(239,235,233) !important; }\n\n.mdl-color-text--brown-100 {\n color: rgb(215,204,200) !important; }\n\n.mdl-color--brown-100 {\n background-color: rgb(215,204,200) !important; }\n\n.mdl-color-text--brown-200 {\n color: rgb(188,170,164) !important; }\n\n.mdl-color--brown-200 {\n background-color: rgb(188,170,164) !important; }\n\n.mdl-color-text--brown-300 {\n color: rgb(161,136,127) !important; }\n\n.mdl-color--brown-300 {\n background-color: rgb(161,136,127) !important; }\n\n.mdl-color-text--brown-400 {\n color: rgb(141,110,99) !important; }\n\n.mdl-color--brown-400 {\n background-color: rgb(141,110,99) !important; }\n\n.mdl-color-text--brown-500 {\n color: rgb(121,85,72) !important; }\n\n.mdl-color--brown-500 {\n background-color: rgb(121,85,72) !important; }\n\n.mdl-color-text--brown-600 {\n color: rgb(109,76,65) !important; }\n\n.mdl-color--brown-600 {\n background-color: rgb(109,76,65) !important; }\n\n.mdl-color-text--brown-700 {\n color: rgb(93,64,55) !important; }\n\n.mdl-color--brown-700 {\n background-color: rgb(93,64,55) !important; }\n\n.mdl-color-text--brown-800 {\n color: rgb(78,52,46) !important; }\n\n.mdl-color--brown-800 {\n background-color: rgb(78,52,46) !important; }\n\n.mdl-color-text--brown-900 {\n color: rgb(62,39,35) !important; }\n\n.mdl-color--brown-900 {\n background-color: rgb(62,39,35) !important; }\n\n.mdl-color-text--grey {\n color: rgb(158,158,158) !important; }\n\n.mdl-color--grey {\n background-color: rgb(158,158,158) !important; }\n\n.mdl-color-text--grey-50 {\n color: rgb(250,250,250) !important; }\n\n.mdl-color--grey-50 {\n background-color: rgb(250,250,250) !important; }\n\n.mdl-color-text--grey-100 {\n color: rgb(245,245,245) !important; }\n\n.mdl-color--grey-100 {\n background-color: rgb(245,245,245) !important; }\n\n.mdl-color-text--grey-200 {\n color: rgb(238,238,238) !important; }\n\n.mdl-color--grey-200 {\n background-color: rgb(238,238,238) !important; }\n\n.mdl-color-text--grey-300 {\n color: rgb(224,224,224) !important; }\n\n.mdl-color--grey-300 {\n background-color: rgb(224,224,224) !important; }\n\n.mdl-color-text--grey-400 {\n color: rgb(189,189,189) !important; }\n\n.mdl-color--grey-400 {\n background-color: rgb(189,189,189) !important; }\n\n.mdl-color-text--grey-500 {\n color: rgb(158,158,158) !important; }\n\n.mdl-color--grey-500 {\n background-color: rgb(158,158,158) !important; }\n\n.mdl-color-text--grey-600 {\n color: rgb(117,117,117) !important; }\n\n.mdl-color--grey-600 {\n background-color: rgb(117,117,117) !important; }\n\n.mdl-color-text--grey-700 {\n color: rgb(97,97,97) !important; }\n\n.mdl-color--grey-700 {\n background-color: rgb(97,97,97) !important; }\n\n.mdl-color-text--grey-800 {\n color: rgb(66,66,66) !important; }\n\n.mdl-color--grey-800 {\n background-color: rgb(66,66,66) !important; }\n\n.mdl-color-text--grey-900 {\n color: rgb(33,33,33) !important; }\n\n.mdl-color--grey-900 {\n background-color: rgb(33,33,33) !important; }\n\n.mdl-color-text--blue-grey {\n color: rgb(96,125,139) !important; }\n\n.mdl-color--blue-grey {\n background-color: rgb(96,125,139) !important; }\n\n.mdl-color-text--blue-grey-50 {\n color: rgb(236,239,241) !important; }\n\n.mdl-color--blue-grey-50 {\n background-color: rgb(236,239,241) !important; }\n\n.mdl-color-text--blue-grey-100 {\n color: rgb(207,216,220) !important; }\n\n.mdl-color--blue-grey-100 {\n background-color: rgb(207,216,220) !important; }\n\n.mdl-color-text--blue-grey-200 {\n color: rgb(176,190,197) !important; }\n\n.mdl-color--blue-grey-200 {\n background-color: rgb(176,190,197) !important; }\n\n.mdl-color-text--blue-grey-300 {\n color: rgb(144,164,174) !important; }\n\n.mdl-color--blue-grey-300 {\n background-color: rgb(144,164,174) !important; }\n\n.mdl-color-text--blue-grey-400 {\n color: rgb(120,144,156) !important; }\n\n.mdl-color--blue-grey-400 {\n background-color: rgb(120,144,156) !important; }\n\n.mdl-color-text--blue-grey-500 {\n color: rgb(96,125,139) !important; }\n\n.mdl-color--blue-grey-500 {\n background-color: rgb(96,125,139) !important; }\n\n.mdl-color-text--blue-grey-600 {\n color: rgb(84,110,122) !important; }\n\n.mdl-color--blue-grey-600 {\n background-color: rgb(84,110,122) !important; }\n\n.mdl-color-text--blue-grey-700 {\n color: rgb(69,90,100) !important; }\n\n.mdl-color--blue-grey-700 {\n background-color: rgb(69,90,100) !important; }\n\n.mdl-color-text--blue-grey-800 {\n color: rgb(55,71,79) !important; }\n\n.mdl-color--blue-grey-800 {\n background-color: rgb(55,71,79) !important; }\n\n.mdl-color-text--blue-grey-900 {\n color: rgb(38,50,56) !important; }\n\n.mdl-color--blue-grey-900 {\n background-color: rgb(38,50,56) !important; }\n\n.mdl-color--black {\n background-color: rgb(0,0,0) !important; }\n\n.mdl-color-text--black {\n color: rgb(0,0,0) !important; }\n\n.mdl-color--white {\n background-color: rgb(255,255,255) !important; }\n\n.mdl-color-text--white {\n color: rgb(255,255,255) !important; }\n\n.mdl-color--primary {\n background-color: rgb(63,81,181) !important; }\n\n.mdl-color--primary-contrast {\n background-color: rgb(255,255,255) !important; }\n\n.mdl-color--primary-dark {\n background-color: rgb(48,63,159) !important; }\n\n.mdl-color--accent {\n background-color: rgb(255,64,129) !important; }\n\n.mdl-color--accent-contrast {\n background-color: rgb(255,255,255) !important; }\n\n.mdl-color-text--primary {\n color: rgb(63,81,181) !important; }\n\n.mdl-color-text--primary-contrast {\n color: rgb(255,255,255) !important; }\n\n.mdl-color-text--primary-dark {\n color: rgb(48,63,159) !important; }\n\n.mdl-color-text--accent {\n color: rgb(255,64,129) !important; }\n\n.mdl-color-text--accent-contrast {\n color: rgb(255,255,255) !important; }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n.mdl-ripple {\n background: rgb(0,0,0);\n border-radius: 50%;\n height: 50px;\n left: 0;\n opacity: 0;\n pointer-events: none;\n position: absolute;\n top: 0;\n transform: translate(-50%, -50%);\n width: 50px;\n overflow: hidden; }\n .mdl-ripple.is-animating {\n transition: transform 0.3s cubic-bezier(0, 0, 0.2, 1), width 0.3s cubic-bezier(0, 0, 0.2, 1), height 0.3s cubic-bezier(0, 0, 0.2, 1), opacity 0.6s cubic-bezier(0, 0, 0.2, 1); }\n .mdl-ripple.is-visible {\n opacity: 0.3; }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n.mdl-animation--default {\n transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); }\n\n.mdl-animation--fast-out-slow-in {\n transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); }\n\n.mdl-animation--linear-out-slow-in {\n transition-timing-function: cubic-bezier(0, 0, 0.2, 1); }\n\n.mdl-animation--fast-out-linear-in {\n transition-timing-function: cubic-bezier(0.4, 0, 1, 1); }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n.mdl-badge {\n position: relative;\n white-space: nowrap;\n margin-right: 24px; }\n .mdl-badge:not([data-badge]) {\n margin-right: auto; }\n .mdl-badge[data-badge]:after {\n content: attr(data-badge);\n display: flex;\n flex-direction: row;\n flex-wrap: wrap;\n justify-content: center;\n align-content: center;\n align-items: center;\n position: absolute;\n top: -11px;\n right: -24px;\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-weight: 600;\n font-size: 12px;\n width: 22px;\n height: 22px;\n border-radius: 50%;\n background: rgb(255,64,129);\n color: rgb(255,255,255); }\n .mdl-button .mdl-badge[data-badge]:after {\n top: -10px;\n right: -5px; }\n .mdl-badge.mdl-badge--no-background[data-badge]:after {\n color: rgb(255,64,129);\n background: rgb(255,255,255);\n box-shadow: 0 0 1px gray; }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* Typography */\n/* Shadows */\n/* Animations */\n.mdl-button {\n background: transparent;\n border: none;\n border-radius: 2px;\n color: rgb(0,0,0);\n position: relative;\n height: 36px;\n min-width: 64px;\n padding: 0 16px;\n display: inline-block;\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 14px;\n font-weight: 500;\n text-transform: uppercase;\n line-height: 1;\n letter-spacing: 0;\n overflow: hidden;\n will-change: box-shadow, transform;\n transition: box-shadow 0.2s cubic-bezier(0.4, 0, 1, 1), background-color 0.2s cubic-bezier(0.4, 0, 0.2, 1), color 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n outline: none;\n cursor: pointer;\n text-decoration: none;\n text-align: center;\n line-height: 36px;\n vertical-align: middle; }\n .mdl-button::-moz-focus-inner {\n border: 0; }\n .mdl-button:hover {\n background-color: rgba(158,158,158, 0.20); }\n .mdl-button:focus:not(:active) {\n background-color: rgba(0,0,0, 0.12); }\n .mdl-button:active {\n background-color: rgba(158,158,158, 0.40); }\n .mdl-button.mdl-button--colored {\n color: rgb(63,81,181); }\n .mdl-button.mdl-button--colored:focus:not(:active) {\n background-color: rgba(0,0,0, 0.12); }\n\ninput.mdl-button[type=\"submit\"] {\n -webkit-appearance: none; }\n\n.mdl-button--raised {\n background: rgba(158,158,158, 0.20);\n box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12); }\n .mdl-button--raised:active {\n box-shadow: 0 4px 5px 0 rgba(0, 0, 0, 0.14), 0 1px 10px 0 rgba(0, 0, 0, 0.12), 0 2px 4px -1px rgba(0, 0, 0, 0.2);\n background-color: rgba(158,158,158, 0.40); }\n .mdl-button--raised:focus:not(:active) {\n box-shadow: 0 0 8px rgba(0, 0, 0, 0.18), 0 8px 16px rgba(0, 0, 0, 0.36);\n background-color: rgba(158,158,158, 0.40); }\n .mdl-button--raised.mdl-button--colored {\n background: rgb(63,81,181);\n color: rgb(255,255,255); }\n .mdl-button--raised.mdl-button--colored:hover {\n background-color: rgb(63,81,181); }\n .mdl-button--raised.mdl-button--colored:active {\n background-color: rgb(63,81,181); }\n .mdl-button--raised.mdl-button--colored:focus:not(:active) {\n background-color: rgb(63,81,181); }\n .mdl-button--raised.mdl-button--colored .mdl-ripple {\n background: rgb(255,255,255); }\n\n.mdl-button--fab {\n border-radius: 50%;\n font-size: 24px;\n height: 56px;\n margin: auto;\n min-width: 56px;\n width: 56px;\n padding: 0;\n overflow: hidden;\n background: rgba(158,158,158, 0.20);\n box-shadow: 0 1px 1.5px 0 rgba(0, 0, 0, 0.12), 0 1px 1px 0 rgba(0, 0, 0, 0.24);\n position: relative;\n line-height: normal; }\n .mdl-button--fab .material-icons {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-12px, -12px);\n line-height: 24px;\n width: 24px; }\n .mdl-button--fab.mdl-button--mini-fab {\n height: 40px;\n min-width: 40px;\n width: 40px; }\n .mdl-button--fab .mdl-button__ripple-container {\n border-radius: 50%;\n -webkit-mask-image: -webkit-radial-gradient(circle, white, black); }\n .mdl-button--fab:active {\n box-shadow: 0 4px 5px 0 rgba(0, 0, 0, 0.14), 0 1px 10px 0 rgba(0, 0, 0, 0.12), 0 2px 4px -1px rgba(0, 0, 0, 0.2);\n background-color: rgba(158,158,158, 0.40); }\n .mdl-button--fab:focus:not(:active) {\n box-shadow: 0 0 8px rgba(0, 0, 0, 0.18), 0 8px 16px rgba(0, 0, 0, 0.36);\n background-color: rgba(158,158,158, 0.40); }\n .mdl-button--fab.mdl-button--colored {\n background: rgb(255,64,129);\n color: rgb(255,255,255); }\n .mdl-button--fab.mdl-button--colored:hover {\n background-color: rgb(255,64,129); }\n .mdl-button--fab.mdl-button--colored:focus:not(:active) {\n background-color: rgb(255,64,129); }\n .mdl-button--fab.mdl-button--colored:active {\n background-color: rgb(255,64,129); }\n .mdl-button--fab.mdl-button--colored .mdl-ripple {\n background: rgb(255,255,255); }\n\n.mdl-button--icon {\n border-radius: 50%;\n font-size: 24px;\n height: 32px;\n margin-left: 0;\n margin-right: 0;\n min-width: 32px;\n width: 32px;\n padding: 0;\n overflow: hidden;\n color: inherit;\n line-height: normal; }\n .mdl-button--icon .material-icons {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-12px, -12px);\n line-height: 24px;\n width: 24px; }\n .mdl-button--icon.mdl-button--mini-icon {\n height: 24px;\n min-width: 24px;\n width: 24px; }\n .mdl-button--icon.mdl-button--mini-icon .material-icons {\n top: 0px;\n left: 0px; }\n .mdl-button--icon .mdl-button__ripple-container {\n border-radius: 50%;\n -webkit-mask-image: -webkit-radial-gradient(circle, white, black); }\n\n.mdl-button__ripple-container {\n display: block;\n height: 100%;\n left: 0px;\n position: absolute;\n top: 0px;\n width: 100%;\n z-index: 0;\n overflow: hidden; }\n .mdl-button[disabled] .mdl-button__ripple-container .mdl-ripple,\n .mdl-button.mdl-button--disabled .mdl-button__ripple-container .mdl-ripple {\n background-color: transparent; }\n\n.mdl-button--primary.mdl-button--primary {\n color: rgb(63,81,181); }\n .mdl-button--primary.mdl-button--primary .mdl-ripple {\n background: rgb(255,255,255); }\n .mdl-button--primary.mdl-button--primary.mdl-button--raised, .mdl-button--primary.mdl-button--primary.mdl-button--fab {\n color: rgb(255,255,255);\n background-color: rgb(63,81,181); }\n\n.mdl-button--accent.mdl-button--accent {\n color: rgb(255,64,129); }\n .mdl-button--accent.mdl-button--accent .mdl-ripple {\n background: rgb(255,255,255); }\n .mdl-button--accent.mdl-button--accent.mdl-button--raised, .mdl-button--accent.mdl-button--accent.mdl-button--fab {\n color: rgb(255,255,255);\n background-color: rgb(255,64,129); }\n\n.mdl-button[disabled][disabled], .mdl-button.mdl-button--disabled.mdl-button--disabled {\n color: rgba(0,0,0, 0.26);\n cursor: default;\n background-color: transparent; }\n\n.mdl-button--fab[disabled][disabled], .mdl-button--fab.mdl-button--disabled.mdl-button--disabled {\n background-color: rgba(0,0,0, 0.12);\n color: rgba(0,0,0, 0.26);\n box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12); }\n\n.mdl-button--raised[disabled][disabled], .mdl-button--raised.mdl-button--disabled.mdl-button--disabled {\n background-color: rgba(0,0,0, 0.12);\n color: rgba(0,0,0, 0.26);\n box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12); }\n\n.mdl-button--colored[disabled][disabled], .mdl-button--colored.mdl-button--disabled.mdl-button--disabled {\n color: rgba(0,0,0, 0.26); }\n\n.mdl-button .material-icons {\n vertical-align: middle; }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n.mdl-card {\n display: flex;\n flex-direction: column;\n font-size: 16px;\n font-weight: 400;\n min-height: 200px;\n overflow: hidden;\n width: 330px;\n z-index: 1;\n position: relative;\n background: rgb(255,255,255);\n border-radius: 2px;\n box-sizing: border-box; }\n\n.mdl-card__media {\n background-color: rgb(255,64,129);\n background-repeat: repeat;\n background-position: 50% 50%;\n background-size: cover;\n background-origin: padding-box;\n background-attachment: scroll;\n box-sizing: border-box; }\n\n.mdl-card__title {\n align-items: center;\n color: rgb(0,0,0);\n display: block;\n display: flex;\n justify-content: stretch;\n line-height: normal;\n padding: 16px 16px;\n perspective-origin: 165px 56px;\n transform-origin: 165px 56px;\n box-sizing: border-box; }\n .mdl-card__title.mdl-card--border {\n border-bottom: 1px solid rgba(0, 0, 0, 0.1); }\n\n.mdl-card__title-text {\n align-self: flex-end;\n color: inherit;\n display: block;\n display: flex;\n font-size: 24px;\n font-weight: 300;\n line-height: normal;\n overflow: hidden;\n transform-origin: 149px 48px;\n margin: 0; }\n\n.mdl-card__subtitle-text {\n font-size: 14px;\n color: rgba(0,0,0, 0.54);\n margin: 0; }\n\n.mdl-card__supporting-text {\n color: rgba(0,0,0, 0.54);\n font-size: 13px;\n line-height: 18px;\n overflow: hidden;\n padding: 16px 16px;\n width: 90%; }\n\n.mdl-card__actions {\n font-size: 16px;\n line-height: normal;\n width: 100%;\n background-color: transparent;\n padding: 8px;\n box-sizing: border-box; }\n .mdl-card__actions.mdl-card--border {\n border-top: 1px solid rgba(0, 0, 0, 0.1); }\n\n.mdl-card--expand {\n flex-grow: 1; }\n\n.mdl-card__menu {\n position: absolute;\n right: 16px;\n top: 16px; }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* Typography */\n/* Shadows */\n/* Animations */\n.mdl-checkbox {\n position: relative;\n z-index: 1;\n vertical-align: middle;\n display: inline-block;\n box-sizing: border-box;\n width: 100%;\n height: 24px;\n margin: 0;\n padding: 0; }\n .mdl-checkbox.is-upgraded {\n padding-left: 24px; }\n\n.mdl-checkbox__input {\n line-height: 24px; }\n .mdl-checkbox.is-upgraded .mdl-checkbox__input {\n position: absolute;\n width: 0;\n height: 0;\n margin: 0;\n padding: 0;\n opacity: 0;\n -ms-appearance: none;\n -moz-appearance: none;\n -webkit-appearance: none;\n appearance: none;\n border: none; }\n\n.mdl-checkbox__box-outline {\n position: absolute;\n top: 3px;\n left: 0;\n display: inline-block;\n box-sizing: border-box;\n width: 16px;\n height: 16px;\n margin: 0;\n cursor: pointer;\n overflow: hidden;\n border: 2px solid rgba(0,0,0, 0.54);\n border-radius: 2px;\n z-index: 2; }\n .mdl-checkbox.is-checked .mdl-checkbox__box-outline {\n border: 2px solid rgb(63,81,181); }\n .mdl-checkbox.is-disabled .mdl-checkbox__box-outline {\n border: 2px solid rgba(0,0,0, 0.26);\n cursor: auto; }\n\n.mdl-checkbox__focus-helper {\n position: absolute;\n top: 3px;\n left: 0;\n display: inline-block;\n box-sizing: border-box;\n width: 16px;\n height: 16px;\n border-radius: 50%;\n background-color: transparent; }\n .mdl-checkbox.is-focused .mdl-checkbox__focus-helper {\n box-shadow: 0 0 0px 8px rgba(0, 0, 0, 0.1);\n background-color: rgba(0, 0, 0, 0.1); }\n .mdl-checkbox.is-focused.is-checked .mdl-checkbox__focus-helper {\n box-shadow: 0 0 0px 8px rgba(63,81,181, 0.26);\n background-color: rgba(63,81,181, 0.26); }\n\n.mdl-checkbox__tick-outline {\n position: absolute;\n top: 0;\n left: 0;\n height: 100%;\n width: 100%;\n mask: url(\"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcKICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICB4bWxuczpjYz0iaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbnMjIgogICB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiCiAgIHhtbG5zOnN2Zz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgdmVyc2lvbj0iMS4xIgogICB2aWV3Qm94PSIwIDAgMSAxIgogICBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWluWU1pbiBtZWV0Ij4KICA8ZGVmcz4KICAgIDxjbGlwUGF0aCBpZD0iY2xpcCI+CiAgICAgIDxwYXRoCiAgICAgICAgIGQ9Ik0gMCwwIDAsMSAxLDEgMSwwIDAsMCB6IE0gMC44NTM0Mzc1LDAuMTY3MTg3NSAwLjk1OTY4NzUsMC4yNzMxMjUgMC40MjkzNzUsMC44MDM0Mzc1IDAuMzIzMTI1LDAuOTA5Njg3NSAwLjIxNzE4NzUsMC44MDM0Mzc1IDAuMDQwMzEyNSwwLjYyNjg3NSAwLjE0NjU2MjUsMC41MjA2MjUgMC4zMjMxMjUsMC42OTc1IDAuODUzNDM3NSwwLjE2NzE4NzUgeiIKICAgICAgICAgc3R5bGU9ImZpbGw6I2ZmZmZmZjtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZSIgLz4KICAgIDwvY2xpcFBhdGg+CiAgICA8bWFzayBpZD0ibWFzayIgbWFza1VuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgbWFza0NvbnRlbnRVbml0cz0ib2JqZWN0Qm91bmRpbmdCb3giPgogICAgICA8cGF0aAogICAgICAgICBkPSJNIDAsMCAwLDEgMSwxIDEsMCAwLDAgeiBNIDAuODUzNDM3NSwwLjE2NzE4NzUgMC45NTk2ODc1LDAuMjczMTI1IDAuNDI5Mzc1LDAuODAzNDM3NSAwLjMyMzEyNSwwLjkwOTY4NzUgMC4yMTcxODc1LDAuODAzNDM3NSAwLjA0MDMxMjUsMC42MjY4NzUgMC4xNDY1NjI1LDAuNTIwNjI1IDAuMzIzMTI1LDAuNjk3NSAwLjg1MzQzNzUsMC4xNjcxODc1IHoiCiAgICAgICAgIHN0eWxlPSJmaWxsOiNmZmZmZmY7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmUiIC8+CiAgICA8L21hc2s+CiAgPC9kZWZzPgogIDxyZWN0CiAgICAgd2lkdGg9IjEiCiAgICAgaGVpZ2h0PSIxIgogICAgIHg9IjAiCiAgICAgeT0iMCIKICAgICBjbGlwLXBhdGg9InVybCgjY2xpcCkiCiAgICAgc3R5bGU9ImZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZSIgLz4KPC9zdmc+Cg==\");\n background: transparent;\n transition-duration: 0.28s;\n transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n transition-property: background; }\n .mdl-checkbox.is-checked .mdl-checkbox__tick-outline {\n background: rgb(63,81,181) url(\"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcKICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICB4bWxuczpjYz0iaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbnMjIgogICB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiCiAgIHhtbG5zOnN2Zz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgdmVyc2lvbj0iMS4xIgogICB2aWV3Qm94PSIwIDAgMSAxIgogICBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWluWU1pbiBtZWV0Ij4KICA8cGF0aAogICAgIGQ9Ik0gMC4wNDAzODA1OSwwLjYyNjc3NjcgMC4xNDY0NDY2MSwwLjUyMDcxMDY4IDAuNDI5Mjg5MzIsMC44MDM1NTMzOSAwLjMyMzIyMzMsMC45MDk2MTk0MSB6IE0gMC4yMTcxNTcyOSwwLjgwMzU1MzM5IDAuODUzNTUzMzksMC4xNjcxNTcyOSAwLjk1OTYxOTQxLDAuMjczMjIzMyAwLjMyMzIyMzMsMC45MDk2MTk0MSB6IgogICAgIGlkPSJyZWN0Mzc4MCIKICAgICBzdHlsZT0iZmlsbDojZmZmZmZmO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lIiAvPgo8L3N2Zz4K\"); }\n .mdl-checkbox.is-checked.is-disabled .mdl-checkbox__tick-outline {\n background: rgba(0,0,0, 0.26) url(\"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcKICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICB4bWxuczpjYz0iaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbnMjIgogICB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiCiAgIHhtbG5zOnN2Zz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgdmVyc2lvbj0iMS4xIgogICB2aWV3Qm94PSIwIDAgMSAxIgogICBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWluWU1pbiBtZWV0Ij4KICA8cGF0aAogICAgIGQ9Ik0gMC4wNDAzODA1OSwwLjYyNjc3NjcgMC4xNDY0NDY2MSwwLjUyMDcxMDY4IDAuNDI5Mjg5MzIsMC44MDM1NTMzOSAwLjMyMzIyMzMsMC45MDk2MTk0MSB6IE0gMC4yMTcxNTcyOSwwLjgwMzU1MzM5IDAuODUzNTUzMzksMC4xNjcxNTcyOSAwLjk1OTYxOTQxLDAuMjczMjIzMyAwLjMyMzIyMzMsMC45MDk2MTk0MSB6IgogICAgIGlkPSJyZWN0Mzc4MCIKICAgICBzdHlsZT0iZmlsbDojZmZmZmZmO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lIiAvPgo8L3N2Zz4K\"); }\n\n.mdl-checkbox__label {\n position: relative;\n cursor: pointer;\n font-size: 16px;\n line-height: 24px;\n margin: 0; }\n .mdl-checkbox.is-disabled .mdl-checkbox__label {\n color: rgba(0,0,0, 0.26);\n cursor: auto; }\n\n.mdl-checkbox__ripple-container {\n position: absolute;\n z-index: 2;\n top: -6px;\n left: -10px;\n box-sizing: border-box;\n width: 36px;\n height: 36px;\n border-radius: 50%;\n cursor: pointer;\n overflow: hidden;\n -webkit-mask-image: -webkit-radial-gradient(circle, white, black); }\n .mdl-checkbox__ripple-container .mdl-ripple {\n background: rgb(63,81,181); }\n .mdl-checkbox.is-disabled .mdl-checkbox__ripple-container {\n cursor: auto; }\n .mdl-checkbox.is-disabled .mdl-checkbox__ripple-container .mdl-ripple {\n background: transparent; }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* Typography */\n/* Shadows */\n/* Animations */\n.mdl-data-table {\n position: relative;\n border: 1px solid rgba(0, 0, 0, 0.12);\n border-collapse: collapse;\n white-space: nowrap;\n font-size: 13px;\n background-color: rgb(255,255,255); }\n .mdl-data-table thead {\n padding-bottom: 3px; }\n .mdl-data-table thead .mdl-data-table__select {\n margin-top: 0; }\n .mdl-data-table tbody tr {\n position: relative;\n height: 48px;\n transition-duration: 0.28s;\n transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n transition-property: background-color; }\n .mdl-data-table tbody tr.is-selected {\n background-color: #e0e0e0; }\n .mdl-data-table tbody tr:hover {\n background-color: #eeeeee; }\n .mdl-data-table td, .mdl-data-table th {\n padding: 0 18px 0 18px;\n text-align: right; }\n .mdl-data-table td:first-of-type, .mdl-data-table th:first-of-type {\n padding-left: 24px; }\n .mdl-data-table td:last-of-type, .mdl-data-table th:last-of-type {\n padding-right: 24px; }\n .mdl-data-table td {\n position: relative;\n vertical-align: top;\n height: 48px;\n border-top: 1px solid rgba(0, 0, 0, 0.12);\n border-bottom: 1px solid rgba(0, 0, 0, 0.12);\n padding-top: 12px;\n box-sizing: border-box; }\n .mdl-data-table td .mdl-data-table__select {\n vertical-align: top;\n position: absolute;\n left: 24px; }\n .mdl-data-table th {\n position: relative;\n vertical-align: bottom;\n text-overflow: ellipsis;\n font-size: 14px;\n font-weight: bold;\n line-height: 24px;\n letter-spacing: 0;\n height: 48px;\n font-size: 12px;\n color: rgba(0, 0, 0, 0.54);\n padding-bottom: 8px;\n box-sizing: border-box; }\n .mdl-data-table th .mdl-data-table__select {\n position: absolute;\n bottom: 8px;\n left: 24px; }\n\n.mdl-data-table__select {\n width: 16px; }\n\n.mdl-data-table__cell--non-numeric.mdl-data-table__cell--non-numeric {\n text-align: left; }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* Typography */\n/* Shadows */\n/* Animations */\n.mdl-mega-footer {\n padding: 16px 40px;\n color: rgb(158,158,158);\n background-color: rgb(66,66,66); }\n\n.mdl-mega-footer--top-section:after,\n.mdl-mega-footer--middle-section:after,\n.mdl-mega-footer--bottom-section:after,\n.mdl-mega-footer__top-section:after,\n.mdl-mega-footer__middle-section:after,\n.mdl-mega-footer__bottom-section:after {\n content: '';\n display: block;\n clear: both; }\n\n.mdl-mega-footer--left-section,\n.mdl-mega-footer__left-section {\n margin-bottom: 16px; }\n\n.mdl-mega-footer--right-section,\n.mdl-mega-footer__right-section {\n margin-bottom: 16px; }\n\n.mdl-mega-footer--right-section a,\n.mdl-mega-footer__right-section a {\n display: block;\n margin-bottom: 16px;\n color: inherit;\n text-decoration: none; }\n\n@media screen and (min-width: 760px) {\n .mdl-mega-footer--left-section,\n .mdl-mega-footer__left-section {\n float: left; }\n .mdl-mega-footer--right-section,\n .mdl-mega-footer__right-section {\n float: right; }\n .mdl-mega-footer--right-section a,\n .mdl-mega-footer__right-section a {\n display: inline-block;\n margin-left: 16px;\n line-height: 36px;\n vertical-align: middle; } }\n\n.mdl-mega-footer--social-btn,\n.mdl-mega-footer__social-btn {\n width: 36px;\n height: 36px;\n padding: 0;\n margin: 0;\n background-color: rgb(158,158,158);\n border: none; }\n\n.mdl-mega-footer--drop-down-section,\n.mdl-mega-footer__drop-down-section {\n display: block;\n position: relative; }\n\n@media screen and (min-width: 760px) {\n .mdl-mega-footer--drop-down-section,\n .mdl-mega-footer__drop-down-section {\n width: 33%; }\n .mdl-mega-footer--drop-down-section:nth-child(1),\n .mdl-mega-footer--drop-down-section:nth-child(2),\n .mdl-mega-footer__drop-down-section:nth-child(1),\n .mdl-mega-footer__drop-down-section:nth-child(2) {\n float: left; }\n .mdl-mega-footer--drop-down-section:nth-child(3),\n .mdl-mega-footer__drop-down-section:nth-child(3) {\n float: right; }\n .mdl-mega-footer--drop-down-section:nth-child(3):after,\n .mdl-mega-footer__drop-down-section:nth-child(3):after {\n clear: right; }\n .mdl-mega-footer--drop-down-section:nth-child(4),\n .mdl-mega-footer__drop-down-section:nth-child(4) {\n clear: right;\n float: right; }\n .mdl-mega-footer--middle-section:after,\n .mdl-mega-footer__middle-section:after {\n content: '';\n display: block;\n clear: both; }\n .mdl-mega-footer--bottom-section,\n .mdl-mega-footer__bottom-section {\n padding-top: 0; } }\n\n@media screen and (min-width: 1024px) {\n .mdl-mega-footer--drop-down-section,\n .mdl-mega-footer--drop-down-section:nth-child(3),\n .mdl-mega-footer--drop-down-section:nth-child(4),\n .mdl-mega-footer__drop-down-section,\n .mdl-mega-footer__drop-down-section:nth-child(3),\n .mdl-mega-footer__drop-down-section:nth-child(4) {\n width: 24%;\n float: left; } }\n\n.mdl-mega-footer--heading-checkbox,\n.mdl-mega-footer__heading-checkbox {\n position: absolute;\n width: 100%;\n height: 55.8px;\n padding: 32px;\n margin: 0;\n margin-top: -16px;\n cursor: pointer;\n z-index: 1;\n opacity: 0; }\n .mdl-mega-footer--heading-checkbox + .mdl-mega-footer--heading:after,\n .mdl-mega-footer--heading-checkbox + .mdl-mega-footer__heading:after,\n .mdl-mega-footer__heading-checkbox + .mdl-mega-footer--heading:after,\n .mdl-mega-footer__heading-checkbox + .mdl-mega-footer__heading:after {\n font-family: 'Material Icons';\n content: '\\E5CE'; }\n\n.mdl-mega-footer--heading-checkbox:checked ~ .mdl-mega-footer--link-list,\n.mdl-mega-footer--heading-checkbox:checked ~ .mdl-mega-footer__link-list,\n.mdl-mega-footer--heading-checkbox:checked + .mdl-mega-footer--heading + .mdl-mega-footer--link-list,\n.mdl-mega-footer--heading-checkbox:checked + .mdl-mega-footer__heading + .mdl-mega-footer__link-list,\n.mdl-mega-footer__heading-checkbox:checked ~ .mdl-mega-footer--link-list,\n.mdl-mega-footer__heading-checkbox:checked ~ .mdl-mega-footer__link-list,\n.mdl-mega-footer__heading-checkbox:checked + .mdl-mega-footer--heading + .mdl-mega-footer--link-list,\n.mdl-mega-footer__heading-checkbox:checked + .mdl-mega-footer__heading + .mdl-mega-footer__link-list {\n display: none; }\n\n.mdl-mega-footer--heading-checkbox:checked + .mdl-mega-footer--heading:after,\n.mdl-mega-footer--heading-checkbox:checked + .mdl-mega-footer__heading:after,\n.mdl-mega-footer__heading-checkbox:checked + .mdl-mega-footer--heading:after,\n.mdl-mega-footer__heading-checkbox:checked + .mdl-mega-footer__heading:after {\n font-family: 'Material Icons';\n content: '\\E5CF'; }\n\n.mdl-mega-footer--heading,\n.mdl-mega-footer__heading {\n position: relative;\n width: 100%;\n padding-right: 39.8px;\n margin-bottom: 16px;\n box-sizing: border-box;\n font-size: 14px;\n line-height: 23.8px;\n font-weight: 500;\n white-space: nowrap;\n text-overflow: ellipsis;\n overflow: hidden;\n color: rgb(224,224,224); }\n\n.mdl-mega-footer--heading:after,\n.mdl-mega-footer__heading:after {\n content: '';\n position: absolute;\n top: 0;\n right: 0;\n display: block;\n width: 23.8px;\n height: 23.8px;\n background-size: cover; }\n\n.mdl-mega-footer--link-list,\n.mdl-mega-footer__link-list {\n list-style: none;\n margin: 0;\n padding: 0;\n margin-bottom: 32px; }\n .mdl-mega-footer--link-list:after,\n .mdl-mega-footer__link-list:after {\n clear: both;\n display: block;\n content: ''; }\n\n.mdl-mega-footer--link-list li,\n.mdl-mega-footer__link-list li {\n font-size: 14px;\n font-weight: 400;\n line-height: 24px;\n letter-spacing: 0;\n line-height: 20px; }\n\n.mdl-mega-footer--link-list a,\n.mdl-mega-footer__link-list a {\n color: inherit;\n text-decoration: none;\n white-space: nowrap; }\n\n@media screen and (min-width: 760px) {\n .mdl-mega-footer--heading-checkbox,\n .mdl-mega-footer__heading-checkbox {\n display: none; }\n .mdl-mega-footer--heading-checkbox + .mdl-mega-footer--heading:after,\n .mdl-mega-footer--heading-checkbox + .mdl-mega-footer__heading:after,\n .mdl-mega-footer__heading-checkbox + .mdl-mega-footer--heading:after,\n .mdl-mega-footer__heading-checkbox + .mdl-mega-footer__heading:after {\n background-image: none; }\n .mdl-mega-footer--heading-checkbox:checked ~ .mdl-mega-footer--link-list,\n .mdl-mega-footer--heading-checkbox:checked ~ .mdl-mega-footer__link-list,\n .mdl-mega-footer--heading-checkbox:checked + .mdl-mega-footer__heading + .mdl-mega-footer__link-list,\n .mdl-mega-footer--heading-checkbox:checked + .mdl-mega-footer--heading + .mdl-mega-footer--link-list,\n .mdl-mega-footer__heading-checkbox:checked ~ .mdl-mega-footer--link-list,\n .mdl-mega-footer__heading-checkbox:checked ~ .mdl-mega-footer__link-list,\n .mdl-mega-footer__heading-checkbox:checked + .mdl-mega-footer__heading + .mdl-mega-footer__link-list,\n .mdl-mega-footer__heading-checkbox:checked + .mdl-mega-footer--heading + .mdl-mega-footer--link-list {\n display: block; }\n .mdl-mega-footer--heading-checkbox:checked + .mdl-mega-footer--heading:after,\n .mdl-mega-footer--heading-checkbox:checked + .mdl-mega-footer__heading:after,\n .mdl-mega-footer__heading-checkbox:checked + .mdl-mega-footer--heading:after,\n .mdl-mega-footer__heading-checkbox:checked + .mdl-mega-footer__heading:after {\n content: ''; } }\n\n.mdl-mega-footer--bottom-section,\n.mdl-mega-footer__bottom-section {\n padding-top: 16px;\n margin-bottom: 16px; }\n\n.mdl-logo {\n margin-bottom: 16px;\n color: white; }\n\n.mdl-mega-footer--bottom-section .mdl-mega-footer--link-list li,\n.mdl-mega-footer__bottom-section .mdl-mega-footer__link-list li {\n float: left;\n margin-bottom: 0;\n margin-right: 16px; }\n\n@media screen and (min-width: 760px) {\n .mdl-logo {\n float: left;\n margin-bottom: 0;\n margin-right: 16px; } }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n.mdl-mini-footer {\n display: flex;\n flex-flow: row wrap;\n justify-content: space-between;\n padding: 32px 16px;\n color: rgb(158,158,158);\n background-color: rgb(66,66,66); }\n .mdl-mini-footer:after {\n content: '';\n display: block; }\n .mdl-mini-footer .mdl-logo {\n line-height: 36px; }\n\n.mdl-mini-footer--link-list,\n.mdl-mini-footer__link-list {\n display: flex;\n flex-flow: row nowrap;\n list-style: none;\n margin: 0;\n padding: 0; }\n .mdl-mini-footer--link-list li,\n .mdl-mini-footer__link-list li {\n margin-bottom: 0;\n margin-right: 16px; }\n @media screen and (min-width: 760px) {\n .mdl-mini-footer--link-list li,\n .mdl-mini-footer__link-list li {\n line-height: 36px; } }\n .mdl-mini-footer--link-list a,\n .mdl-mini-footer__link-list a {\n color: inherit;\n text-decoration: none;\n white-space: nowrap; }\n\n.mdl-mini-footer--left-section,\n.mdl-mini-footer__left-section {\n display: inline-block;\n order: 0; }\n\n.mdl-mini-footer--right-section,\n.mdl-mini-footer__right-section {\n display: inline-block;\n order: 1; }\n\n.mdl-mini-footer--social-btn,\n.mdl-mini-footer__social-btn {\n width: 36px;\n height: 36px;\n padding: 0;\n margin: 0;\n background-color: rgb(158,158,158);\n border: none; }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n.mdl-icon-toggle {\n position: relative;\n z-index: 1;\n vertical-align: middle;\n display: inline-block;\n height: 32px;\n margin: 0;\n padding: 0; }\n\n.mdl-icon-toggle__input {\n line-height: 32px; }\n .mdl-icon-toggle.is-upgraded .mdl-icon-toggle__input {\n position: absolute;\n width: 0;\n height: 0;\n margin: 0;\n padding: 0;\n opacity: 0;\n -ms-appearance: none;\n -moz-appearance: none;\n -webkit-appearance: none;\n appearance: none;\n border: none; }\n\n.mdl-icon-toggle__label {\n display: inline-block;\n position: relative;\n cursor: pointer;\n height: 32px;\n width: 32px;\n min-width: 32px;\n color: rgb(97,97,97);\n border-radius: 50%;\n padding: 0;\n margin-left: 0;\n margin-right: 0;\n text-align: center;\n background-color: transparent;\n will-change: background-color;\n transition: background-color 0.2s cubic-bezier(0.4, 0, 0.2, 1), color 0.2s cubic-bezier(0.4, 0, 0.2, 1); }\n .mdl-icon-toggle__label.material-icons {\n line-height: 32px;\n font-size: 24px; }\n .mdl-icon-toggle.is-checked .mdl-icon-toggle__label {\n color: rgb(63,81,181); }\n .mdl-icon-toggle.is-disabled .mdl-icon-toggle__label {\n color: rgba(0,0,0, 0.26);\n cursor: auto;\n transition: none; }\n .mdl-icon-toggle.is-focused .mdl-icon-toggle__label {\n background-color: rgba(0,0,0, 0.12); }\n .mdl-icon-toggle.is-focused.is-checked .mdl-icon-toggle__label {\n background-color: rgba(63,81,181, 0.26); }\n\n.mdl-icon-toggle__ripple-container {\n position: absolute;\n z-index: 2;\n top: -2px;\n left: -2px;\n box-sizing: border-box;\n width: 36px;\n height: 36px;\n border-radius: 50%;\n cursor: pointer;\n overflow: hidden;\n -webkit-mask-image: -webkit-radial-gradient(circle, white, black); }\n .mdl-icon-toggle__ripple-container .mdl-ripple {\n background: rgb(97,97,97); }\n .mdl-icon-toggle.is-disabled .mdl-icon-toggle__ripple-container {\n cursor: auto; }\n .mdl-icon-toggle.is-disabled .mdl-icon-toggle__ripple-container .mdl-ripple {\n background: transparent; }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* Typography */\n/* Shadows */\n/* Animations */\n.mdl-menu__container {\n display: block;\n margin: 0;\n padding: 0;\n border: none;\n position: absolute;\n overflow: visible;\n height: 0;\n width: 0;\n visibility: hidden;\n z-index: -1; }\n .mdl-menu__container.is-visible, .mdl-menu__container.is-animating {\n z-index: 999;\n visibility: visible; }\n\n.mdl-menu__outline {\n display: block;\n background: rgb(255,255,255);\n margin: 0;\n padding: 0;\n border: none;\n border-radius: 2px;\n position: absolute;\n top: 0;\n left: 0;\n overflow: hidden;\n opacity: 0;\n transform: scale(0);\n transform-origin: 0 0;\n box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12);\n will-change: transform;\n transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n z-index: -1; }\n .mdl-menu__container.is-visible .mdl-menu__outline {\n opacity: 1;\n transform: scale(1);\n z-index: 999; }\n .mdl-menu__outline.mdl-menu--bottom-right {\n transform-origin: 100% 0; }\n .mdl-menu__outline.mdl-menu--top-left {\n transform-origin: 0 100%; }\n .mdl-menu__outline.mdl-menu--top-right {\n transform-origin: 100% 100%; }\n\n.mdl-menu {\n position: absolute;\n list-style: none;\n top: 0;\n left: 0;\n height: auto;\n width: auto;\n min-width: 124px;\n padding: 8px 0;\n margin: 0;\n opacity: 0;\n clip: rect(0 0 0 0);\n z-index: -1; }\n .mdl-menu__container.is-visible .mdl-menu {\n opacity: 1;\n z-index: 999; }\n .mdl-menu.is-animating {\n transition: opacity 0.2s cubic-bezier(0.4, 0, 0.2, 1), clip 0.3s cubic-bezier(0.4, 0, 0.2, 1); }\n .mdl-menu.mdl-menu--bottom-right {\n left: auto;\n right: 0; }\n .mdl-menu.mdl-menu--top-left {\n top: auto;\n bottom: 0; }\n .mdl-menu.mdl-menu--top-right {\n top: auto;\n left: auto;\n bottom: 0;\n right: 0; }\n .mdl-menu.mdl-menu--unaligned {\n top: auto;\n left: auto; }\n\n.mdl-menu__item {\n display: block;\n border: none;\n color: rgba(0,0,0, 0.87);\n background-color: transparent;\n text-align: left;\n margin: 0;\n padding: 0 16px;\n outline-color: rgb(189,189,189);\n position: relative;\n overflow: hidden;\n font-size: 14px;\n font-weight: 400;\n line-height: 24px;\n letter-spacing: 0;\n text-decoration: none;\n cursor: pointer;\n height: 48px;\n line-height: 48px;\n white-space: nowrap;\n opacity: 0;\n transition: opacity 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n user-select: none; }\n .mdl-menu__container.is-visible .mdl-menu__item {\n opacity: 1; }\n .mdl-menu__item::-moz-focus-inner {\n border: 0; }\n .mdl-menu__item[disabled] {\n color: rgb(189,189,189);\n background-color: transparent;\n cursor: auto; }\n .mdl-menu__item[disabled]:hover {\n background-color: transparent; }\n .mdl-menu__item[disabled]:focus {\n background-color: transparent; }\n .mdl-menu__item[disabled] .mdl-ripple {\n background: transparent; }\n .mdl-menu__item:hover {\n background-color: rgb(238,238,238); }\n .mdl-menu__item:focus {\n outline: none;\n background-color: rgb(238,238,238); }\n .mdl-menu__item:active {\n background-color: rgb(224,224,224); }\n\n.mdl-menu__item--ripple-container {\n display: block;\n height: 100%;\n left: 0px;\n position: absolute;\n top: 0px;\n width: 100%;\n z-index: 0;\n overflow: hidden; }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n.mdl-progress {\n display: block;\n position: relative;\n height: 4px;\n width: 500px; }\n\n.mdl-progress > .bar {\n display: block;\n position: absolute;\n top: 0;\n bottom: 0;\n width: 0%;\n transition: width 0.2s cubic-bezier(0.4, 0, 0.2, 1); }\n\n.mdl-progress > .progressbar {\n background-color: rgb(63,81,181);\n z-index: 1;\n left: 0; }\n\n.mdl-progress > .bufferbar {\n background-image: linear-gradient(to right, rgba(255,255,255, 0.7), rgba(255,255,255, 0.7)), linear-gradient(to right, rgb(63,81,181), rgb(63,81,181));\n z-index: 0;\n left: 0; }\n\n.mdl-progress > .auxbar {\n right: 0; }\n\n@supports (-webkit-appearance: none) {\n .mdl-progress:not(.mdl-progress__indeterminate):not(.mdl-progress__indeterminate) > .auxbar {\n background-image: linear-gradient(to right, rgba(255,255,255, 0.7), rgba(255,255,255, 0.7)), linear-gradient(to right, rgb(63,81,181), rgb(63,81,181));\n mask: url(\"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIj8+Cjxzdmcgd2lkdGg9IjEyIiBoZWlnaHQ9IjQiIHZpZXdQb3J0PSIwIDAgMTIgNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgogIDxlbGxpcHNlIGN4PSIyIiBjeT0iMiIgcng9IjIiIHJ5PSIyIj4KICAgIDxhbmltYXRlIGF0dHJpYnV0ZU5hbWU9ImN4IiBmcm9tPSIyIiB0bz0iLTEwIiBkdXI9IjAuNnMiIHJlcGVhdENvdW50PSJpbmRlZmluaXRlIiAvPgogIDwvZWxsaXBzZT4KICA8ZWxsaXBzZSBjeD0iMTQiIGN5PSIyIiByeD0iMiIgcnk9IjIiIGNsYXNzPSJsb2FkZXIiPgogICAgPGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iY3giIGZyb209IjE0IiB0bz0iMiIgZHVyPSIwLjZzIiByZXBlYXRDb3VudD0iaW5kZWZpbml0ZSIgLz4KICA8L2VsbGlwc2U+Cjwvc3ZnPgo=\"); } }\n\n.mdl-progress:not(.mdl-progress__indeterminate) > .auxbar {\n background-image: linear-gradient(to right, rgba(255,255,255, 0.9), rgba(255,255,255, 0.9)), linear-gradient(to right, rgb(63,81,181), rgb(63,81,181)); }\n\n.mdl-progress.mdl-progress__indeterminate > .bar1 {\n background-color: rgb(63,81,181);\n animation-name: indeterminate1;\n animation-duration: 2s;\n animation-iteration-count: infinite;\n animation-timing-function: linear; }\n\n.mdl-progress.mdl-progress__indeterminate > .bar3 {\n background-image: none;\n background-color: rgb(63,81,181);\n animation-name: indeterminate2;\n animation-duration: 2s;\n animation-iteration-count: infinite;\n animation-timing-function: linear; }\n\n@keyframes indeterminate1 {\n 0% {\n left: 0%;\n width: 0%; }\n 50% {\n left: 25%;\n width: 75%; }\n 75% {\n left: 100%;\n width: 0%; } }\n\n@keyframes indeterminate2 {\n 0% {\n left: 0%;\n width: 0%; }\n 50% {\n left: 0%;\n width: 0%; }\n 75% {\n left: 0%;\n width: 25%; }\n 100% {\n left: 100%;\n width: 0%; } }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* Typography */\n/* Shadows */\n/* Animations */\n.mdl-navigation {\n display: flex;\n flex-wrap: nowrap;\n box-sizing: border-box; }\n\n.mdl-navigation__link {\n color: rgb(66,66,66);\n text-decoration: none;\n font-weight: 500;\n font-size: 13px;\n margin: 0; }\n\n.mdl-layout {\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: column;\n overflow-y: auto;\n overflow-x: hidden;\n position: relative;\n -webkit-overflow-scrolling: touch; }\n\n.mdl-layout.is-small-screen .mdl-layout--large-screen-only {\n display: none; }\n\n.mdl-layout:not(.is-small-screen) .mdl-layout--small-screen-only {\n display: none; }\n\n.mdl-layout__container {\n position: absolute;\n width: 100%;\n height: 100%; }\n\n.mdl-layout__title,\n.mdl-layout-title {\n display: block;\n position: relative;\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 20px;\n font-weight: 500;\n line-height: 1;\n letter-spacing: 0.02em;\n font-weight: 400;\n box-sizing: border-box; }\n\n.mdl-layout-spacer {\n flex-grow: 1; }\n\n.mdl-layout__drawer {\n display: flex;\n flex-direction: column;\n flex-wrap: nowrap;\n width: 240px;\n height: 100%;\n max-height: 100%;\n position: absolute;\n top: 0;\n left: 0;\n box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12);\n box-sizing: border-box;\n border-right: 1px solid rgb(224,224,224);\n background: rgb(250,250,250);\n transform: translateX(-250px);\n transform-style: preserve-3d;\n will-change: transform;\n transition-duration: 0.2s;\n transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n transition-property: transform;\n color: rgb(66,66,66);\n overflow: visible;\n overflow-y: auto;\n z-index: 5; }\n .mdl-layout__drawer.is-visible {\n transform: translateX(0); }\n .mdl-layout__drawer.is-visible ~ .mdl-layout__content.mdl-layout__content {\n overflow: hidden; }\n .mdl-layout__drawer > * {\n flex-shrink: 0; }\n .mdl-layout__drawer > .mdl-layout__title,\n .mdl-layout__drawer > .mdl-layout-title {\n line-height: 64px;\n padding-left: 40px; }\n @media screen and (max-width: 1024px) {\n .mdl-layout__drawer > .mdl-layout__title,\n .mdl-layout__drawer > .mdl-layout-title {\n line-height: 56px;\n padding-left: 16px; } }\n .mdl-layout__drawer .mdl-navigation {\n flex-direction: column;\n align-items: stretch;\n padding-top: 16px; }\n .mdl-layout__drawer .mdl-navigation .mdl-navigation__link {\n display: block;\n flex-shrink: 0;\n padding: 16px 40px;\n margin: 0;\n color: #757575; }\n @media screen and (max-width: 1024px) {\n .mdl-layout__drawer .mdl-navigation .mdl-navigation__link {\n padding: 16px 16px; } }\n .mdl-layout__drawer .mdl-navigation .mdl-navigation__link:hover {\n background-color: rgb(224,224,224); }\n .mdl-layout__drawer .mdl-navigation .mdl-navigation__link--current {\n background-color: rgb(0,0,0);\n color: rgb(224,224,224); }\n @media screen and (min-width: 1025px) {\n .mdl-layout--fixed-drawer > .mdl-layout__drawer {\n transform: translateX(0); } }\n\n.mdl-layout__drawer-button {\n display: block;\n position: absolute;\n height: 48px;\n width: 48px;\n border: 0;\n flex-shrink: 0;\n overflow: hidden;\n text-align: center;\n cursor: pointer;\n font-size: 26px;\n line-height: 50px;\n font-family: Helvetica, Arial, sans-serif;\n margin: 10px 12px;\n top: 0;\n left: 0;\n color: rgb(255,255,255);\n z-index: 4; }\n .mdl-layout__header .mdl-layout__drawer-button {\n position: absolute;\n color: rgb(255,255,255);\n background-color: inherit; }\n @media screen and (max-width: 1024px) {\n .mdl-layout__header .mdl-layout__drawer-button {\n margin: 4px; } }\n @media screen and (max-width: 1024px) {\n .mdl-layout__drawer-button {\n margin: 4px;\n color: rgba(0, 0, 0, 0.5); } }\n @media screen and (min-width: 1025px) {\n .mdl-layout--fixed-drawer > .mdl-layout__drawer-button {\n display: none; } }\n\n.mdl-layout__header {\n display: flex;\n flex-direction: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n box-sizing: border-box;\n flex-shrink: 0;\n width: 100%;\n margin: 0;\n padding: 0;\n border: none;\n min-height: 64px;\n max-height: 1000px;\n z-index: 3;\n background-color: rgb(63,81,181);\n color: rgb(255,255,255);\n box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12);\n transition-duration: 0.2s;\n transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n transition-property: max-height, box-shadow; }\n @media screen and (max-width: 1024px) {\n .mdl-layout__header {\n min-height: 56px; } }\n .mdl-layout--fixed-drawer.is-upgraded:not(.is-small-screen) > .mdl-layout__header {\n margin-left: 240px;\n width: calc(100% - 240px); }\n @media screen and (min-width: 1025px) {\n .mdl-layout--fixed-drawer > .mdl-layout__header .mdl-layout__header-row {\n padding-left: 40px; } }\n .mdl-layout__header > .mdl-layout-icon {\n position: absolute;\n left: 40px;\n top: 16px;\n height: 32px;\n width: 32px;\n overflow: hidden;\n z-index: 3;\n display: block; }\n @media screen and (max-width: 1024px) {\n .mdl-layout__header > .mdl-layout-icon {\n left: 16px;\n top: 12px; } }\n .mdl-layout.has-drawer .mdl-layout__header > .mdl-layout-icon {\n display: none; }\n .mdl-layout__header.is-compact {\n max-height: 64px; }\n @media screen and (max-width: 1024px) {\n .mdl-layout__header.is-compact {\n max-height: 56px; } }\n .mdl-layout__header.is-compact.has-tabs {\n height: 112px; }\n @media screen and (max-width: 1024px) {\n .mdl-layout__header.is-compact.has-tabs {\n min-height: 104px; } }\n @media screen and (max-width: 1024px) {\n .mdl-layout__header {\n display: none; }\n .mdl-layout--fixed-header > .mdl-layout__header {\n display: flex; } }\n\n.mdl-layout__header--transparent.mdl-layout__header--transparent {\n background-color: transparent;\n box-shadow: none; }\n\n.mdl-layout__header--seamed {\n box-shadow: none; }\n\n.mdl-layout__header--scroll {\n box-shadow: none; }\n\n.mdl-layout__header--waterfall {\n box-shadow: none;\n overflow: hidden; }\n .mdl-layout__header--waterfall.is-casting-shadow {\n box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12); }\n\n.mdl-layout__header-row {\n display: flex;\n flex-direction: row;\n flex-wrap: nowrap;\n flex-shrink: 0;\n box-sizing: border-box;\n align-self: stretch;\n align-items: center;\n height: 64px;\n margin: 0;\n padding: 0 40px 0 80px; }\n @media screen and (max-width: 1024px) {\n .mdl-layout__header-row {\n height: 56px;\n padding: 0 16px 0 72px; } }\n .mdl-layout__header-row > * {\n flex-shrink: 0; }\n .mdl-layout__header--scroll .mdl-layout__header-row {\n width: 100%; }\n .mdl-layout__header-row .mdl-navigation {\n margin: 0;\n padding: 0;\n height: 64px;\n flex-direction: row;\n align-items: center; }\n @media screen and (max-width: 1024px) {\n .mdl-layout__header-row .mdl-navigation {\n height: 56px; } }\n .mdl-layout__header-row .mdl-navigation__link {\n display: block;\n color: rgb(255,255,255);\n line-height: 64px;\n padding: 0 24px; }\n @media screen and (max-width: 1024px) {\n .mdl-layout__header-row .mdl-navigation__link {\n line-height: 56px;\n padding: 0 16px; } }\n\n.mdl-layout__obfuscator {\n background-color: transparent;\n position: absolute;\n top: 0;\n left: 0;\n height: 100%;\n width: 100%;\n z-index: 4;\n visibility: hidden;\n transition-property: background-color;\n transition-duration: 0.2s;\n transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); }\n .mdl-layout__obfuscator.is-visible {\n background-color: rgba(0, 0, 0, 0.5);\n visibility: visible; }\n\n.mdl-layout__content {\n -ms-flex: 0 1 auto;\n display: inline-block;\n overflow-y: auto;\n overflow-x: hidden;\n flex-grow: 1;\n z-index: 1;\n -webkit-overflow-scrolling: touch; }\n .mdl-layout--fixed-drawer > .mdl-layout__content {\n margin-left: 240px; }\n .mdl-layout__container.has-scrolling-header .mdl-layout__content {\n overflow: visible; }\n @media screen and (max-width: 1024px) {\n .mdl-layout--fixed-drawer > .mdl-layout__content {\n margin-left: 0; }\n .mdl-layout__container.has-scrolling-header .mdl-layout__content {\n overflow-y: auto;\n overflow-x: hidden; } }\n\n.mdl-layout__tab-bar {\n height: 96px;\n margin: 0;\n width: calc(100% - 112px);\n padding: 0 0 0 56px;\n display: flex;\n background-color: rgb(63,81,181);\n overflow-y: hidden;\n overflow-x: scroll; }\n .mdl-layout__tab-bar::-webkit-scrollbar {\n display: none; }\n @media screen and (max-width: 1024px) {\n .mdl-layout__tab-bar {\n width: calc(100% - 60px);\n padding: 0 0 0 60px; } }\n .mdl-layout--fixed-tabs .mdl-layout__tab-bar {\n padding: 0;\n overflow: hidden;\n width: 100%; }\n\n.mdl-layout__tab-bar-container {\n position: relative;\n height: 48px;\n width: 100%;\n border: none;\n margin: 0;\n z-index: 2;\n flex-grow: 0;\n flex-shrink: 0;\n overflow: hidden; }\n .mdl-layout__container > .mdl-layout__tab-bar-container {\n position: absolute;\n top: 0;\n left: 0; }\n\n.mdl-layout__tab-bar-button {\n display: inline-block;\n position: absolute;\n top: 0;\n height: 48px;\n width: 56px;\n z-index: 4;\n text-align: center;\n background-color: rgb(63,81,181);\n color: transparent;\n cursor: pointer;\n user-select: none; }\n @media screen and (max-width: 1024px) {\n .mdl-layout__tab-bar-button {\n display: none;\n width: 60px; } }\n .mdl-layout--fixed-tabs .mdl-layout__tab-bar-button {\n display: none; }\n .mdl-layout__tab-bar-button .material-icons {\n line-height: 48px; }\n .mdl-layout__tab-bar-button.is-active {\n color: rgb(255,255,255); }\n\n.mdl-layout__tab-bar-left-button {\n left: 0; }\n\n.mdl-layout__tab-bar-right-button {\n right: 0; }\n\n.mdl-layout__tab {\n margin: 0;\n border: none;\n padding: 0 24px 0 24px;\n float: left;\n position: relative;\n display: block;\n flex-grow: 0;\n flex-shrink: 0;\n text-decoration: none;\n height: 48px;\n line-height: 48px;\n text-align: center;\n font-weight: 500;\n font-size: 14px;\n text-transform: uppercase;\n color: rgba(255,255,255, 0.6);\n overflow: hidden; }\n @media screen and (max-width: 1024px) {\n .mdl-layout__tab {\n padding: 0 12px 0 12px; } }\n .mdl-layout--fixed-tabs .mdl-layout__tab {\n float: none;\n flex-grow: 1;\n padding: 0; }\n .mdl-layout.is-upgraded .mdl-layout__tab.is-active {\n color: rgb(255,255,255); }\n .mdl-layout.is-upgraded .mdl-layout__tab.is-active::after {\n height: 2px;\n width: 100%;\n display: block;\n content: \" \";\n bottom: 0;\n left: 0;\n position: absolute;\n background: rgb(255,64,129);\n animation: border-expand 0.2s cubic-bezier(0.4, 0, 0.4, 1) 0.01s alternate forwards;\n transition: all 1s cubic-bezier(0.4, 0, 1, 1); }\n .mdl-layout__tab .mdl-layout__tab-ripple-container {\n display: block;\n position: absolute;\n height: 100%;\n width: 100%;\n left: 0;\n top: 0;\n z-index: 1;\n overflow: hidden; }\n .mdl-layout__tab .mdl-layout__tab-ripple-container .mdl-ripple {\n background-color: rgb(255,255,255); }\n\n.mdl-layout__tab-panel {\n display: block; }\n .mdl-layout.is-upgraded .mdl-layout__tab-panel {\n display: none; }\n .mdl-layout.is-upgraded .mdl-layout__tab-panel.is-active {\n display: block; }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* Typography */\n/* Shadows */\n/* Animations */\n.mdl-radio {\n position: relative;\n font-size: 16px;\n line-height: 24px;\n display: inline-block;\n box-sizing: border-box;\n margin: 0;\n padding-left: 0; }\n .mdl-radio.is-upgraded {\n padding-left: 24px; }\n\n.mdl-radio__button {\n line-height: 24px; }\n .mdl-radio.is-upgraded .mdl-radio__button {\n position: absolute;\n width: 0;\n height: 0;\n margin: 0;\n padding: 0;\n opacity: 0;\n -ms-appearance: none;\n -moz-appearance: none;\n -webkit-appearance: none;\n appearance: none;\n border: none; }\n\n.mdl-radio__outer-circle {\n position: absolute;\n top: 4px;\n left: 0;\n display: inline-block;\n box-sizing: border-box;\n width: 16px;\n height: 16px;\n margin: 0;\n cursor: pointer;\n border: 2px solid rgba(0,0,0, 0.54);\n border-radius: 50%;\n z-index: 2; }\n .mdl-radio.is-checked .mdl-radio__outer-circle {\n border: 2px solid rgb(63,81,181); }\n .mdl-radio.is-disabled .mdl-radio__outer-circle {\n border: 2px solid rgba(0,0,0, 0.26);\n cursor: auto; }\n\n.mdl-radio__inner-circle {\n position: absolute;\n z-index: 1;\n margin: 0;\n top: 8px;\n left: 4px;\n box-sizing: border-box;\n width: 8px;\n height: 8px;\n cursor: pointer;\n transition-duration: 0.28s;\n transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n transition-property: transform;\n transform: scale3d(0, 0, 0);\n border-radius: 50%;\n background: rgb(63,81,181); }\n .mdl-radio.is-checked .mdl-radio__inner-circle {\n transform: scale3d(1, 1, 1); }\n .mdl-radio.is-disabled .mdl-radio__inner-circle {\n background: rgba(0,0,0, 0.26);\n cursor: auto; }\n .mdl-radio.is-focused .mdl-radio__inner-circle {\n box-shadow: 0 0 0px 10px rgba(0, 0, 0, 0.1); }\n\n.mdl-radio__label {\n cursor: pointer; }\n .mdl-radio.is-disabled .mdl-radio__label {\n color: rgba(0,0,0, 0.26);\n cursor: auto; }\n\n.mdl-radio__ripple-container {\n position: absolute;\n z-index: 2;\n top: -9px;\n left: -13px;\n box-sizing: border-box;\n width: 42px;\n height: 42px;\n border-radius: 50%;\n cursor: pointer;\n overflow: hidden;\n -webkit-mask-image: -webkit-radial-gradient(circle, white, black); }\n .mdl-radio__ripple-container .mdl-ripple {\n background: rgb(63,81,181); }\n .mdl-radio.is-disabled .mdl-radio__ripple-container {\n cursor: auto; }\n .mdl-radio.is-disabled .mdl-radio__ripple-container .mdl-ripple {\n background: transparent; }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n_:-ms-input-placeholder, :root .mdl-slider.mdl-slider.is-upgraded {\n -ms-appearance: none;\n height: 32px;\n margin: 0; }\n\n.mdl-slider {\n width: calc(100% - 40px);\n margin: 0 20px; }\n .mdl-slider.is-upgraded {\n -webkit-appearance: none;\n -moz-appearance: none;\n appearance: none;\n height: 2px;\n background: transparent;\n -webkit-user-select: none;\n -moz-user-select: none;\n user-select: none;\n outline: 0;\n padding: 0;\n color: rgb(63,81,181);\n align-self: center;\n z-index: 1;\n cursor: pointer;\n /**************************** Tracks ****************************/\n /**************************** Thumbs ****************************/\n /**************************** 0-value ****************************/\n /**************************** Disabled ****************************/ }\n .mdl-slider.is-upgraded::-moz-focus-outer {\n border: 0; }\n .mdl-slider.is-upgraded::-ms-tooltip {\n display: none; }\n .mdl-slider.is-upgraded::-webkit-slider-runnable-track {\n background: transparent; }\n .mdl-slider.is-upgraded::-moz-range-track {\n background: transparent;\n border: none; }\n .mdl-slider.is-upgraded::-ms-track {\n background: none;\n color: transparent;\n height: 2px;\n width: 100%;\n border: none; }\n .mdl-slider.is-upgraded::-ms-fill-lower {\n padding: 0;\n background: linear-gradient(to right, transparent, transparent 16px, rgb(63,81,181) 16px, rgb(63,81,181) 0); }\n .mdl-slider.is-upgraded::-ms-fill-upper {\n padding: 0;\n background: linear-gradient(to left, transparent, transparent 16px, rgba(0,0,0, 0.26) 16px, rgba(0,0,0, 0.26) 0); }\n .mdl-slider.is-upgraded::-webkit-slider-thumb {\n -webkit-appearance: none;\n width: 12px;\n height: 12px;\n box-sizing: border-box;\n border-radius: 50%;\n background: rgb(63,81,181);\n border: none;\n transition: transform 0.18s cubic-bezier(0.4, 0, 0.2, 1), border 0.18s cubic-bezier(0.4, 0, 0.2, 1), box-shadow 0.18s cubic-bezier(0.4, 0, 0.2, 1), background 0.28s cubic-bezier(0.4, 0, 0.2, 1); }\n .mdl-slider.is-upgraded::-moz-range-thumb {\n -moz-appearance: none;\n width: 12px;\n height: 12px;\n box-sizing: border-box;\n border-radius: 50%;\n background-image: none;\n background: rgb(63,81,181);\n border: none; }\n .mdl-slider.is-upgraded:focus:not(:active)::-webkit-slider-thumb {\n box-shadow: 0 0 0 10px rgba(63,81,181, 0.26); }\n .mdl-slider.is-upgraded:focus:not(:active)::-moz-range-thumb {\n box-shadow: 0 0 0 10px rgba(63,81,181, 0.26); }\n .mdl-slider.is-upgraded:active::-webkit-slider-thumb {\n background-image: none;\n background: rgb(63,81,181);\n transform: scale(1.5); }\n .mdl-slider.is-upgraded:active::-moz-range-thumb {\n background-image: none;\n background: rgb(63,81,181);\n transform: scale(1.5); }\n .mdl-slider.is-upgraded::-ms-thumb {\n width: 32px;\n height: 32px;\n border: none;\n border-radius: 50%;\n background: rgb(63,81,181);\n transform: scale(0.375);\n transition: transform 0.18s cubic-bezier(0.4, 0, 0.2, 1), background 0.28s cubic-bezier(0.4, 0, 0.2, 1); }\n .mdl-slider.is-upgraded:focus:not(:active)::-ms-thumb {\n background: radial-gradient(circle closest-side, rgb(63,81,181) 0%, rgb(63,81,181) 37.5%, rgba(63,81,181, 0.26) 37.5%, rgba(63,81,181, 0.26) 100%);\n transform: scale(1); }\n .mdl-slider.is-upgraded:active::-ms-thumb {\n background: rgb(63,81,181);\n transform: scale(0.5625); }\n .mdl-slider.is-upgraded.is-lowest-value::-webkit-slider-thumb {\n border: 2px solid rgba(0,0,0, 0.26);\n background: transparent; }\n .mdl-slider.is-upgraded.is-lowest-value::-moz-range-thumb {\n border: 2px solid rgba(0,0,0, 0.26);\n background: transparent; }\n .mdl-slider.is-upgraded.is-lowest-value +\n.mdl-slider__background-flex > .mdl-slider__background-upper {\n left: 6px; }\n .mdl-slider.is-upgraded.is-lowest-value:focus:not(:active)::-webkit-slider-thumb {\n box-shadow: 0 0 0 10px rgba(0,0,0, 0.12);\n background: rgba(0,0,0, 0.12); }\n .mdl-slider.is-upgraded.is-lowest-value:focus:not(:active)::-moz-range-thumb {\n box-shadow: 0 0 0 10px rgba(0,0,0, 0.12);\n background: rgba(0,0,0, 0.12); }\n .mdl-slider.is-upgraded.is-lowest-value:active::-webkit-slider-thumb {\n border: 1.6px solid rgba(0,0,0, 0.26);\n transform: scale(1.5); }\n .mdl-slider.is-upgraded.is-lowest-value:active +\n.mdl-slider__background-flex > .mdl-slider__background-upper {\n left: 9px; }\n .mdl-slider.is-upgraded.is-lowest-value:active::-moz-range-thumb {\n border: 1.5px solid rgba(0,0,0, 0.26);\n transform: scale(1.5); }\n .mdl-slider.is-upgraded.is-lowest-value::-ms-thumb {\n background: radial-gradient(circle closest-side, transparent 0%, transparent 66.67%, rgba(0,0,0, 0.26) 66.67%, rgba(0,0,0, 0.26) 100%); }\n .mdl-slider.is-upgraded.is-lowest-value:focus:not(:active)::-ms-thumb {\n background: radial-gradient(circle closest-side, rgba(0,0,0, 0.12) 0%, rgba(0,0,0, 0.12) 25%, rgba(0,0,0, 0.26) 25%, rgba(0,0,0, 0.26) 37.5%, rgba(0,0,0, 0.12) 37.5%, rgba(0,0,0, 0.12) 100%);\n transform: scale(1); }\n .mdl-slider.is-upgraded.is-lowest-value:active::-ms-thumb {\n transform: scale(0.5625);\n background: radial-gradient(circle closest-side, transparent 0%, transparent 77.78%, rgba(0,0,0, 0.26) 77.78%, rgba(0,0,0, 0.26) 100%); }\n .mdl-slider.is-upgraded.is-lowest-value::-ms-fill-lower {\n background: transparent; }\n .mdl-slider.is-upgraded.is-lowest-value::-ms-fill-upper {\n margin-left: 6px; }\n .mdl-slider.is-upgraded.is-lowest-value:active::-ms-fill-upper {\n margin-left: 9px; }\n .mdl-slider.is-upgraded:disabled:focus::-webkit-slider-thumb, .mdl-slider.is-upgraded:disabled:active::-webkit-slider-thumb, .mdl-slider.is-upgraded:disabled::-webkit-slider-thumb {\n transform: scale(0.667);\n background: rgba(0,0,0, 0.26); }\n .mdl-slider.is-upgraded:disabled:focus::-moz-range-thumb, .mdl-slider.is-upgraded:disabled:active::-moz-range-thumb, .mdl-slider.is-upgraded:disabled::-moz-range-thumb {\n transform: scale(0.667);\n background: rgba(0,0,0, 0.26); }\n .mdl-slider.is-upgraded:disabled +\n.mdl-slider__background-flex > .mdl-slider__background-lower {\n background-color: rgba(0,0,0, 0.26);\n left: -6px; }\n .mdl-slider.is-upgraded:disabled +\n.mdl-slider__background-flex > .mdl-slider__background-upper {\n left: 6px; }\n .mdl-slider.is-upgraded.is-lowest-value:disabled:focus::-webkit-slider-thumb, .mdl-slider.is-upgraded.is-lowest-value:disabled:active::-webkit-slider-thumb, .mdl-slider.is-upgraded.is-lowest-value:disabled::-webkit-slider-thumb {\n border: 3px solid rgba(0,0,0, 0.26);\n background: transparent;\n transform: scale(0.667); }\n .mdl-slider.is-upgraded.is-lowest-value:disabled:focus::-moz-range-thumb, .mdl-slider.is-upgraded.is-lowest-value:disabled:active::-moz-range-thumb, .mdl-slider.is-upgraded.is-lowest-value:disabled::-moz-range-thumb {\n border: 3px solid rgba(0,0,0, 0.26);\n background: transparent;\n transform: scale(0.667); }\n .mdl-slider.is-upgraded.is-lowest-value:disabled:active +\n.mdl-slider__background-flex > .mdl-slider__background-upper {\n left: 6px; }\n .mdl-slider.is-upgraded:disabled:focus::-ms-thumb, .mdl-slider.is-upgraded:disabled:active::-ms-thumb, .mdl-slider.is-upgraded:disabled::-ms-thumb {\n transform: scale(0.25);\n background: rgba(0,0,0, 0.26); }\n .mdl-slider.is-upgraded.is-lowest-value:disabled:focus::-ms-thumb, .mdl-slider.is-upgraded.is-lowest-value:disabled:active::-ms-thumb, .mdl-slider.is-upgraded.is-lowest-value:disabled::-ms-thumb {\n transform: scale(0.25);\n background: radial-gradient(circle closest-side, transparent 0%, transparent 50%, rgba(0,0,0, 0.26) 50%, rgba(0,0,0, 0.26) 100%); }\n .mdl-slider.is-upgraded:disabled::-ms-fill-lower {\n margin-right: 6px;\n background: linear-gradient(to right, transparent, transparent 25px, rgba(0,0,0, 0.26) 25px, rgba(0,0,0, 0.26) 0); }\n .mdl-slider.is-upgraded:disabled::-ms-fill-upper {\n margin-left: 6px; }\n .mdl-slider.is-upgraded.is-lowest-value:disabled:active::-ms-fill-upper {\n margin-left: 6px; }\n\n.mdl-slider__ie-container {\n height: 18px;\n overflow: visible;\n border: none;\n margin: none;\n padding: none; }\n\n.mdl-slider__container {\n height: 18px;\n position: relative;\n background: none;\n display: flex;\n flex-direction: row; }\n\n.mdl-slider__background-flex {\n background: transparent;\n position: absolute;\n height: 2px;\n width: calc(100% - 52px);\n top: 50%;\n left: 0;\n margin: 0 26px;\n display: flex;\n overflow: hidden;\n border: 0;\n padding: 0;\n transform: translate(0, -1px); }\n\n.mdl-slider__background-lower {\n background: rgb(63,81,181);\n flex: 0;\n position: relative;\n border: 0;\n padding: 0; }\n\n.mdl-slider__background-upper {\n background: rgba(0,0,0, 0.26);\n flex: 0;\n position: relative;\n border: 0;\n padding: 0;\n transition: left 0.18s cubic-bezier(0.4, 0, 0.2, 1); }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n.mdl-spinner {\n display: inline-block;\n position: relative;\n width: 28px;\n height: 28px; }\n .mdl-spinner:not(.is-upgraded).is-active:after {\n content: \"Loading...\"; }\n .mdl-spinner.is-upgraded.is-active {\n animation: mdl-spinner__container-rotate 1568.23529412ms linear infinite; }\n\n@keyframes mdl-spinner__container-rotate {\n to {\n transform: rotate(360deg); } }\n\n.mdl-spinner__layer {\n position: absolute;\n width: 100%;\n height: 100%;\n opacity: 0; }\n\n.mdl-spinner__layer-1 {\n border-color: rgb(66,165,245); }\n .mdl-spinner--single-color .mdl-spinner__layer-1 {\n border-color: rgb(63,81,181); }\n .mdl-spinner.is-active .mdl-spinner__layer-1 {\n animation: mdl-spinner__fill-unfill-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both, mdl-spinner__layer-1-fade-in-out 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both; }\n\n.mdl-spinner__layer-2 {\n border-color: rgb(244,67,54); }\n .mdl-spinner--single-color .mdl-spinner__layer-2 {\n border-color: rgb(63,81,181); }\n .mdl-spinner.is-active .mdl-spinner__layer-2 {\n animation: mdl-spinner__fill-unfill-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both, mdl-spinner__layer-2-fade-in-out 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both; }\n\n.mdl-spinner__layer-3 {\n border-color: rgb(253,216,53); }\n .mdl-spinner--single-color .mdl-spinner__layer-3 {\n border-color: rgb(63,81,181); }\n .mdl-spinner.is-active .mdl-spinner__layer-3 {\n animation: mdl-spinner__fill-unfill-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both, mdl-spinner__layer-3-fade-in-out 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both; }\n\n.mdl-spinner__layer-4 {\n border-color: rgb(76,175,80); }\n .mdl-spinner--single-color .mdl-spinner__layer-4 {\n border-color: rgb(63,81,181); }\n .mdl-spinner.is-active .mdl-spinner__layer-4 {\n animation: mdl-spinner__fill-unfill-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both, mdl-spinner__layer-4-fade-in-out 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both; }\n\n@keyframes mdl-spinner__fill-unfill-rotate {\n 12.5% {\n transform: rotate(135deg); }\n 25% {\n transform: rotate(270deg); }\n 37.5% {\n transform: rotate(405deg); }\n 50% {\n transform: rotate(540deg); }\n 62.5% {\n transform: rotate(675deg); }\n 75% {\n transform: rotate(810deg); }\n 87.5% {\n transform: rotate(945deg); }\n to {\n transform: rotate(1080deg); } }\n\n/**\n* HACK: Even though the intention is to have the current .mdl-spinner__layer-N\n* at `opacity: 1`, we set it to `opacity: 0.99` instead since this forces Chrome\n* to do proper subpixel rendering for the elements being animated. This is\n* especially visible in Chrome 39 on Ubuntu 14.04. See:\n*\n* - https://github.com/Polymer/paper-spinner/issues/9\n* - https://code.google.com/p/chromium/issues/detail?id=436255\n*/\n@keyframes mdl-spinner__layer-1-fade-in-out {\n from {\n opacity: 0.99; }\n 25% {\n opacity: 0.99; }\n 26% {\n opacity: 0; }\n 89% {\n opacity: 0; }\n 90% {\n opacity: 0.99; }\n 100% {\n opacity: 0.99; } }\n\n@keyframes mdl-spinner__layer-2-fade-in-out {\n from {\n opacity: 0; }\n 15% {\n opacity: 0; }\n 25% {\n opacity: 0.99; }\n 50% {\n opacity: 0.99; }\n 51% {\n opacity: 0; } }\n\n@keyframes mdl-spinner__layer-3-fade-in-out {\n from {\n opacity: 0; }\n 40% {\n opacity: 0; }\n 50% {\n opacity: 0.99; }\n 75% {\n opacity: 0.99; }\n 76% {\n opacity: 0; } }\n\n@keyframes mdl-spinner__layer-4-fade-in-out {\n from {\n opacity: 0; }\n 65% {\n opacity: 0; }\n 75% {\n opacity: 0.99; }\n 90% {\n opacity: 0.99; }\n 100% {\n opacity: 0; } }\n\n/**\n* Patch the gap that appear between the two adjacent\n* div.mdl-spinner__circle-clipper while the spinner is rotating\n* (appears on Chrome 38, Safari 7.1, and IE 11).\n*\n* Update: the gap no longer appears on Chrome when .mdl-spinner__layer-N's\n* opacity is 0.99, but still does on Safari and IE.\n*/\n.mdl-spinner__gap-patch {\n position: absolute;\n box-sizing: border-box;\n top: 0;\n left: 45%;\n width: 10%;\n height: 100%;\n overflow: hidden;\n border-color: inherit; }\n .mdl-spinner__gap-patch .mdl-spinner__circle {\n width: 1000%;\n left: -450%; }\n\n.mdl-spinner__circle-clipper {\n display: inline-block;\n position: relative;\n width: 50%;\n height: 100%;\n overflow: hidden;\n border-color: inherit; }\n .mdl-spinner__circle-clipper .mdl-spinner__circle {\n width: 200%; }\n\n.mdl-spinner__circle {\n box-sizing: border-box;\n height: 100%;\n border-width: 3px;\n border-style: solid;\n border-color: inherit;\n border-bottom-color: transparent !important;\n border-radius: 50%;\n animation: none;\n position: absolute;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0; }\n .mdl-spinner__left .mdl-spinner__circle {\n border-right-color: transparent !important;\n transform: rotate(129deg); }\n .mdl-spinner.is-active .mdl-spinner__left .mdl-spinner__circle {\n animation: mdl-spinner__left-spin 1333ms cubic-bezier(0.4, 0, 0.2, 1) infinite both; }\n .mdl-spinner__right .mdl-spinner__circle {\n left: -100%;\n border-left-color: transparent !important;\n transform: rotate(-129deg); }\n .mdl-spinner.is-active .mdl-spinner__right .mdl-spinner__circle {\n animation: mdl-spinner__right-spin 1333ms cubic-bezier(0.4, 0, 0.2, 1) infinite both; }\n\n@keyframes mdl-spinner__left-spin {\n from {\n transform: rotate(130deg); }\n 50% {\n transform: rotate(-5deg); }\n to {\n transform: rotate(130deg); } }\n\n@keyframes mdl-spinner__right-spin {\n from {\n transform: rotate(-130deg); }\n 50% {\n transform: rotate(5deg); }\n to {\n transform: rotate(-130deg); } }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* Typography */\n/* Shadows */\n/* Animations */\n.mdl-switch {\n position: relative;\n z-index: 1;\n vertical-align: middle;\n display: inline-block;\n box-sizing: border-box;\n width: 100%;\n height: 24px;\n margin: 0;\n padding: 0;\n overflow: visible;\n -webkit-touch-callout: none;\n -webkit-user-select: none;\n -moz-user-select: none;\n -ms-user-select: none;\n user-select: none; }\n .mdl-switch.is-upgraded {\n padding-left: 28px; }\n\n.mdl-switch__input {\n line-height: 24px; }\n .mdl-switch.is-upgraded .mdl-switch__input {\n position: absolute;\n width: 0;\n height: 0;\n margin: 0;\n padding: 0;\n opacity: 0;\n -ms-appearance: none;\n -moz-appearance: none;\n -webkit-appearance: none;\n appearance: none;\n border: none; }\n\n.mdl-switch__track {\n background: rgba(0,0,0, 0.26);\n position: absolute;\n left: 0;\n top: 5px;\n height: 14px;\n width: 36px;\n border-radius: 14px;\n cursor: pointer; }\n .mdl-switch.is-checked .mdl-switch__track {\n background: rgba(63,81,181, 0.5); }\n .mdl-switch.is-disabled .mdl-switch__track {\n background: rgba(0,0,0, 0.12);\n cursor: auto; }\n\n.mdl-switch__thumb {\n background: rgb(250,250,250);\n position: absolute;\n left: 0;\n top: 2px;\n height: 20px;\n width: 20px;\n border-radius: 50%;\n cursor: pointer;\n box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12);\n transition-duration: 0.28s;\n transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n transition-property: left; }\n .mdl-switch.is-checked .mdl-switch__thumb {\n background: rgb(63,81,181);\n left: 16px;\n box-shadow: 0 3px 4px 0 rgba(0, 0, 0, 0.14), 0 3px 3px -2px rgba(0, 0, 0, 0.2), 0 1px 8px 0 rgba(0, 0, 0, 0.12); }\n .mdl-switch.is-disabled .mdl-switch__thumb {\n background: rgb(189,189,189);\n cursor: auto; }\n\n.mdl-switch__focus-helper {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-4px, -4px);\n display: inline-block;\n box-sizing: border-box;\n width: 8px;\n height: 8px;\n border-radius: 50%;\n background-color: transparent; }\n .mdl-switch.is-focused .mdl-switch__focus-helper {\n box-shadow: 0 0 0px 20px rgba(0, 0, 0, 0.1);\n background-color: rgba(0, 0, 0, 0.1); }\n .mdl-switch.is-focused.is-checked .mdl-switch__focus-helper {\n box-shadow: 0 0 0px 20px rgba(63,81,181, 0.26);\n background-color: rgba(63,81,181, 0.26); }\n\n.mdl-switch__label {\n position: relative;\n cursor: pointer;\n font-size: 16px;\n line-height: 24px;\n margin: 0;\n left: 24px; }\n .mdl-switch.is-disabled .mdl-switch__label {\n color: rgb(189,189,189);\n cursor: auto; }\n\n.mdl-switch__ripple-container {\n position: absolute;\n z-index: 2;\n top: -12px;\n left: -14px;\n box-sizing: border-box;\n width: 48px;\n height: 48px;\n border-radius: 50%;\n cursor: pointer;\n overflow: hidden;\n -webkit-mask-image: -webkit-radial-gradient(circle, white, black);\n transition-duration: 0.40s;\n transition-timing-function: step-end;\n transition-property: left; }\n .mdl-switch__ripple-container .mdl-ripple {\n background: rgb(63,81,181); }\n .mdl-switch.is-disabled .mdl-switch__ripple-container {\n cursor: auto; }\n .mdl-switch.is-disabled .mdl-switch__ripple-container .mdl-ripple {\n background: transparent; }\n .mdl-switch.is-checked .mdl-switch__ripple-container {\n cursor: auto;\n left: 2px; }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n.mdl-tabs {\n display: block;\n width: 100%; }\n\n.mdl-tabs__tab-bar {\n display: flex;\n flex-direction: row;\n justify-content: center;\n align-content: space-between;\n align-items: flex-start;\n height: 48px;\n padding: 0 0 0 0;\n margin: 0;\n border-bottom: 1px solid rgb(224,224,224); }\n\n.mdl-tabs__tab {\n margin: 0;\n border: none;\n padding: 0 24px 0 24px;\n float: left;\n position: relative;\n display: block;\n color: red;\n text-decoration: none;\n height: 48px;\n line-height: 48px;\n text-align: center;\n font-weight: 500;\n font-size: 14px;\n text-transform: uppercase;\n color: rgba(0,0,0, 0.54);\n overflow: hidden; }\n .mdl-tabs.is-upgraded .mdl-tabs__tab.is-active {\n color: rgba(0,0,0, 0.87); }\n .mdl-tabs.is-upgraded .mdl-tabs__tab.is-active:after {\n height: 2px;\n width: 100%;\n display: block;\n content: \" \";\n bottom: 0px;\n left: 0px;\n position: absolute;\n background: rgb(63,81,181);\n animation: border-expand 0.2s cubic-bezier(0.4, 0, 0.4, 1) 0.01s alternate forwards;\n transition: all 1s cubic-bezier(0.4, 0, 1, 1); }\n .mdl-tabs__tab .mdl-tabs__ripple-container {\n display: block;\n position: absolute;\n height: 100%;\n width: 100%;\n left: 0px;\n top: 0px;\n z-index: 1;\n overflow: hidden; }\n .mdl-tabs__tab .mdl-tabs__ripple-container .mdl-ripple {\n background: rgb(63,81,181); }\n\n.mdl-tabs__panel {\n display: block; }\n .mdl-tabs.is-upgraded .mdl-tabs__panel {\n display: none; }\n .mdl-tabs.is-upgraded .mdl-tabs__panel.is-active {\n display: block; }\n\n@keyframes border-expand {\n 0% {\n opacity: 0;\n width: 0; }\n 100% {\n opacity: 1;\n width: 100%; } }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* Typography */\n/* Shadows */\n/* Animations */\n.mdl-textfield {\n position: relative;\n font-size: 16px;\n display: inline-block;\n box-sizing: border-box;\n width: 300px;\n max-width: 100%;\n margin: 0;\n padding: 20px 0; }\n .mdl-textfield .mdl-button {\n position: absolute;\n bottom: 20px; }\n\n.mdl-textfield--align-right {\n text-align: right; }\n\n.mdl-textfield--full-width {\n width: 100%; }\n\n.mdl-textfield--expandable {\n min-width: 32px;\n width: auto;\n min-height: 32px; }\n\n.mdl-textfield__input {\n border: none;\n border-bottom: 1px solid rgba(0,0,0, 0.12);\n display: block;\n font-size: 16px;\n margin: 0;\n padding: 4px 0;\n width: 100%;\n background: none;\n text-align: left;\n color: inherit; }\n .mdl-textfield.is-focused .mdl-textfield__input {\n outline: none; }\n .mdl-textfield.is-invalid .mdl-textfield__input {\n border-color: rgb(222, 50, 38);\n box-shadow: none; }\n .mdl-textfield.is-disabled .mdl-textfield__input {\n background-color: transparent;\n border-bottom: 1px dotted rgba(0,0,0, 0.12);\n color: rgba(0,0,0, 0.26); }\n\n.mdl-textfield textarea.mdl-textfield__input {\n display: block; }\n\n.mdl-textfield__label {\n bottom: 0;\n color: rgba(0,0,0, 0.26);\n font-size: 16px;\n left: 0;\n right: 0;\n pointer-events: none;\n position: absolute;\n display: block;\n top: 24px;\n width: 100%;\n overflow: hidden;\n white-space: nowrap;\n text-align: left; }\n .mdl-textfield.is-dirty .mdl-textfield__label {\n visibility: hidden; }\n .mdl-textfield--floating-label .mdl-textfield__label {\n transition-duration: 0.2s;\n transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); }\n .mdl-textfield.is-disabled.is-disabled .mdl-textfield__label {\n color: rgba(0,0,0, 0.26); }\n .mdl-textfield--floating-label.is-focused .mdl-textfield__label,\n .mdl-textfield--floating-label.is-dirty .mdl-textfield__label {\n color: rgb(63,81,181);\n font-size: 12px;\n top: 4px;\n visibility: visible; }\n .mdl-textfield--floating-label.is-focused .mdl-textfield__expandable-holder .mdl-textfield__label,\n .mdl-textfield--floating-label.is-dirty .mdl-textfield__expandable-holder .mdl-textfield__label {\n top: -16px; }\n .mdl-textfield--floating-label.is-invalid .mdl-textfield__label {\n color: rgb(222, 50, 38);\n font-size: 12px; }\n .mdl-textfield__label:after {\n background-color: rgb(63,81,181);\n bottom: 20px;\n content: '';\n height: 2px;\n left: 45%;\n position: absolute;\n transition-duration: 0.2s;\n transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n visibility: hidden;\n width: 10px; }\n .mdl-textfield.is-focused .mdl-textfield__label:after {\n left: 0;\n visibility: visible;\n width: 100%; }\n .mdl-textfield.is-invalid .mdl-textfield__label:after {\n background-color: rgb(222, 50, 38); }\n\n.mdl-textfield__error {\n color: rgb(222, 50, 38);\n position: absolute;\n font-size: 12px;\n margin-top: 3px;\n visibility: hidden;\n display: block; }\n .mdl-textfield.is-invalid .mdl-textfield__error {\n visibility: visible; }\n\n.mdl-textfield__expandable-holder {\n display: inline-block;\n position: relative;\n margin-left: 32px;\n transition-duration: 0.2s;\n transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n display: inline-block;\n max-width: 0.1px; }\n .mdl-textfield.is-focused .mdl-textfield__expandable-holder, .mdl-textfield.is-dirty .mdl-textfield__expandable-holder {\n max-width: 600px; }\n .mdl-textfield__expandable-holder .mdl-textfield__label:after {\n bottom: 0; }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n.mdl-tooltip {\n transform: scale(0);\n transform-origin: top center;\n will-change: transform;\n z-index: 999;\n background: rgba(97,97,97, 0.9);\n border-radius: 2px;\n color: rgb(255,255,255);\n display: inline-block;\n font-size: 10px;\n font-weight: 500;\n line-height: 14px;\n max-width: 170px;\n position: fixed;\n top: -500px;\n left: -500px;\n padding: 8px;\n text-align: center; }\n\n.mdl-tooltip.is-active {\n animation: pulse 200ms cubic-bezier(0, 0, 0.2, 1) forwards; }\n\n.mdl-tooltip--large {\n line-height: 14px;\n font-size: 14px;\n padding: 16px; }\n\n@keyframes pulse {\n 0% {\n transform: scale(0);\n opacity: 0; }\n 50% {\n transform: scale(0.99); }\n 100% {\n transform: scale(1);\n opacity: 1;\n visibility: visible; } }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* Typography */\n/* Shadows */\n/* Animations */\n.mdl-shadow--2dp {\n box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12); }\n\n.mdl-shadow--3dp {\n box-shadow: 0 3px 4px 0 rgba(0, 0, 0, 0.14), 0 3px 3px -2px rgba(0, 0, 0, 0.2), 0 1px 8px 0 rgba(0, 0, 0, 0.12); }\n\n.mdl-shadow--4dp {\n box-shadow: 0 4px 5px 0 rgba(0, 0, 0, 0.14), 0 1px 10px 0 rgba(0, 0, 0, 0.12), 0 2px 4px -1px rgba(0, 0, 0, 0.2); }\n\n.mdl-shadow--6dp {\n box-shadow: 0 6px 10px 0 rgba(0, 0, 0, 0.14), 0 1px 18px 0 rgba(0, 0, 0, 0.12), 0 3px 5px -1px rgba(0, 0, 0, 0.2); }\n\n.mdl-shadow--8dp {\n box-shadow: 0 8px 10px 1px rgba(0, 0, 0, 0.14), 0 3px 14px 2px rgba(0, 0, 0, 0.12), 0 5px 5px -3px rgba(0, 0, 0, 0.2); }\n\n.mdl-shadow--16dp {\n box-shadow: 0 16px 24px 2px rgba(0, 0, 0, 0.14), 0 6px 30px 5px rgba(0, 0, 0, 0.12), 0 8px 10px -5px rgba(0, 0, 0, 0.2); }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*\n* NOTE: Some rules here are applied using duplicate selectors.\n* This is on purpose to increase their specificity when applied.\n* For example: `.mdl-cell--1-col-phone.mdl-cell--1-col-phone`\n*/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n.mdl-grid {\n display: flex;\n flex-flow: row wrap;\n margin: 0 auto 0 auto;\n align-items: stretch; }\n .mdl-grid.mdl-grid--no-spacing {\n padding: 0; }\n\n.mdl-cell {\n box-sizing: border-box; }\n\n.mdl-cell--top {\n align-self: flex-start; }\n\n.mdl-cell--middle {\n align-self: center; }\n\n.mdl-cell--bottom {\n align-self: flex-end; }\n\n.mdl-cell--stretch {\n align-self: stretch; }\n\n.mdl-grid.mdl-grid--no-spacing > .mdl-cell {\n margin: 0; }\n\n@media (max-width: 479px) {\n .mdl-grid {\n padding: 8px; }\n .mdl-cell {\n margin: 8px;\n width: calc(100% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell {\n width: 100%; }\n .mdl-cell--hide-phone {\n display: none !important; }\n .mdl-cell--1-col,\n .mdl-cell--1-col-phone.mdl-cell--1-col-phone {\n width: calc(25% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--1-col, .mdl-grid--no-spacing >\n .mdl-cell--1-col-phone.mdl-cell--1-col-phone {\n width: 25%; }\n .mdl-cell--2-col,\n .mdl-cell--2-col-phone.mdl-cell--2-col-phone {\n width: calc(50% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--2-col, .mdl-grid--no-spacing >\n .mdl-cell--2-col-phone.mdl-cell--2-col-phone {\n width: 50%; }\n .mdl-cell--3-col,\n .mdl-cell--3-col-phone.mdl-cell--3-col-phone {\n width: calc(75% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--3-col, .mdl-grid--no-spacing >\n .mdl-cell--3-col-phone.mdl-cell--3-col-phone {\n width: 75%; }\n .mdl-cell--4-col,\n .mdl-cell--4-col-phone.mdl-cell--4-col-phone {\n width: calc(100% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--4-col, .mdl-grid--no-spacing >\n .mdl-cell--4-col-phone.mdl-cell--4-col-phone {\n width: 100%; }\n .mdl-cell--5-col,\n .mdl-cell--5-col-phone.mdl-cell--5-col-phone {\n width: calc(100% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--5-col, .mdl-grid--no-spacing >\n .mdl-cell--5-col-phone.mdl-cell--5-col-phone {\n width: 100%; }\n .mdl-cell--6-col,\n .mdl-cell--6-col-phone.mdl-cell--6-col-phone {\n width: calc(100% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--6-col, .mdl-grid--no-spacing >\n .mdl-cell--6-col-phone.mdl-cell--6-col-phone {\n width: 100%; }\n .mdl-cell--7-col,\n .mdl-cell--7-col-phone.mdl-cell--7-col-phone {\n width: calc(100% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--7-col, .mdl-grid--no-spacing >\n .mdl-cell--7-col-phone.mdl-cell--7-col-phone {\n width: 100%; }\n .mdl-cell--8-col,\n .mdl-cell--8-col-phone.mdl-cell--8-col-phone {\n width: calc(100% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--8-col, .mdl-grid--no-spacing >\n .mdl-cell--8-col-phone.mdl-cell--8-col-phone {\n width: 100%; }\n .mdl-cell--9-col,\n .mdl-cell--9-col-phone.mdl-cell--9-col-phone {\n width: calc(100% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--9-col, .mdl-grid--no-spacing >\n .mdl-cell--9-col-phone.mdl-cell--9-col-phone {\n width: 100%; }\n .mdl-cell--10-col,\n .mdl-cell--10-col-phone.mdl-cell--10-col-phone {\n width: calc(100% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--10-col, .mdl-grid--no-spacing >\n .mdl-cell--10-col-phone.mdl-cell--10-col-phone {\n width: 100%; }\n .mdl-cell--11-col,\n .mdl-cell--11-col-phone.mdl-cell--11-col-phone {\n width: calc(100% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--11-col, .mdl-grid--no-spacing >\n .mdl-cell--11-col-phone.mdl-cell--11-col-phone {\n width: 100%; }\n .mdl-cell--12-col,\n .mdl-cell--12-col-phone.mdl-cell--12-col-phone {\n width: calc(100% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--12-col, .mdl-grid--no-spacing >\n .mdl-cell--12-col-phone.mdl-cell--12-col-phone {\n width: 100%; } }\n\n@media (min-width: 480px) and (max-width: 839px) {\n .mdl-grid {\n padding: 8px; }\n .mdl-cell {\n margin: 8px;\n width: calc(50% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell {\n width: 50%; }\n .mdl-cell--hide-tablet {\n display: none !important; }\n .mdl-cell--1-col,\n .mdl-cell--1-col-tablet.mdl-cell--1-col-tablet {\n width: calc(12.5% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--1-col, .mdl-grid--no-spacing >\n .mdl-cell--1-col-tablet.mdl-cell--1-col-tablet {\n width: 12.5%; }\n .mdl-cell--2-col,\n .mdl-cell--2-col-tablet.mdl-cell--2-col-tablet {\n width: calc(25% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--2-col, .mdl-grid--no-spacing >\n .mdl-cell--2-col-tablet.mdl-cell--2-col-tablet {\n width: 25%; }\n .mdl-cell--3-col,\n .mdl-cell--3-col-tablet.mdl-cell--3-col-tablet {\n width: calc(37.5% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--3-col, .mdl-grid--no-spacing >\n .mdl-cell--3-col-tablet.mdl-cell--3-col-tablet {\n width: 37.5%; }\n .mdl-cell--4-col,\n .mdl-cell--4-col-tablet.mdl-cell--4-col-tablet {\n width: calc(50% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--4-col, .mdl-grid--no-spacing >\n .mdl-cell--4-col-tablet.mdl-cell--4-col-tablet {\n width: 50%; }\n .mdl-cell--5-col,\n .mdl-cell--5-col-tablet.mdl-cell--5-col-tablet {\n width: calc(62.5% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--5-col, .mdl-grid--no-spacing >\n .mdl-cell--5-col-tablet.mdl-cell--5-col-tablet {\n width: 62.5%; }\n .mdl-cell--6-col,\n .mdl-cell--6-col-tablet.mdl-cell--6-col-tablet {\n width: calc(75% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--6-col, .mdl-grid--no-spacing >\n .mdl-cell--6-col-tablet.mdl-cell--6-col-tablet {\n width: 75%; }\n .mdl-cell--7-col,\n .mdl-cell--7-col-tablet.mdl-cell--7-col-tablet {\n width: calc(87.5% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--7-col, .mdl-grid--no-spacing >\n .mdl-cell--7-col-tablet.mdl-cell--7-col-tablet {\n width: 87.5%; }\n .mdl-cell--8-col,\n .mdl-cell--8-col-tablet.mdl-cell--8-col-tablet {\n width: calc(100% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--8-col, .mdl-grid--no-spacing >\n .mdl-cell--8-col-tablet.mdl-cell--8-col-tablet {\n width: 100%; }\n .mdl-cell--9-col,\n .mdl-cell--9-col-tablet.mdl-cell--9-col-tablet {\n width: calc(100% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--9-col, .mdl-grid--no-spacing >\n .mdl-cell--9-col-tablet.mdl-cell--9-col-tablet {\n width: 100%; }\n .mdl-cell--10-col,\n .mdl-cell--10-col-tablet.mdl-cell--10-col-tablet {\n width: calc(100% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--10-col, .mdl-grid--no-spacing >\n .mdl-cell--10-col-tablet.mdl-cell--10-col-tablet {\n width: 100%; }\n .mdl-cell--11-col,\n .mdl-cell--11-col-tablet.mdl-cell--11-col-tablet {\n width: calc(100% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--11-col, .mdl-grid--no-spacing >\n .mdl-cell--11-col-tablet.mdl-cell--11-col-tablet {\n width: 100%; }\n .mdl-cell--12-col,\n .mdl-cell--12-col-tablet.mdl-cell--12-col-tablet {\n width: calc(100% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--12-col, .mdl-grid--no-spacing >\n .mdl-cell--12-col-tablet.mdl-cell--12-col-tablet {\n width: 100%; } }\n\n@media (min-width: 840px) {\n .mdl-grid {\n padding: 8px; }\n .mdl-cell {\n margin: 8px;\n width: calc(33.3333333333% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell {\n width: 33.3333333333%; }\n .mdl-cell--hide-desktop {\n display: none !important; }\n .mdl-cell--1-col,\n .mdl-cell--1-col-desktop.mdl-cell--1-col-desktop {\n width: calc(8.3333333333% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--1-col, .mdl-grid--no-spacing >\n .mdl-cell--1-col-desktop.mdl-cell--1-col-desktop {\n width: 8.3333333333%; }\n .mdl-cell--2-col,\n .mdl-cell--2-col-desktop.mdl-cell--2-col-desktop {\n width: calc(16.6666666667% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--2-col, .mdl-grid--no-spacing >\n .mdl-cell--2-col-desktop.mdl-cell--2-col-desktop {\n width: 16.6666666667%; }\n .mdl-cell--3-col,\n .mdl-cell--3-col-desktop.mdl-cell--3-col-desktop {\n width: calc(25% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--3-col, .mdl-grid--no-spacing >\n .mdl-cell--3-col-desktop.mdl-cell--3-col-desktop {\n width: 25%; }\n .mdl-cell--4-col,\n .mdl-cell--4-col-desktop.mdl-cell--4-col-desktop {\n width: calc(33.3333333333% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--4-col, .mdl-grid--no-spacing >\n .mdl-cell--4-col-desktop.mdl-cell--4-col-desktop {\n width: 33.3333333333%; }\n .mdl-cell--5-col,\n .mdl-cell--5-col-desktop.mdl-cell--5-col-desktop {\n width: calc(41.6666666667% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--5-col, .mdl-grid--no-spacing >\n .mdl-cell--5-col-desktop.mdl-cell--5-col-desktop {\n width: 41.6666666667%; }\n .mdl-cell--6-col,\n .mdl-cell--6-col-desktop.mdl-cell--6-col-desktop {\n width: calc(50% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--6-col, .mdl-grid--no-spacing >\n .mdl-cell--6-col-desktop.mdl-cell--6-col-desktop {\n width: 50%; }\n .mdl-cell--7-col,\n .mdl-cell--7-col-desktop.mdl-cell--7-col-desktop {\n width: calc(58.3333333333% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--7-col, .mdl-grid--no-spacing >\n .mdl-cell--7-col-desktop.mdl-cell--7-col-desktop {\n width: 58.3333333333%; }\n .mdl-cell--8-col,\n .mdl-cell--8-col-desktop.mdl-cell--8-col-desktop {\n width: calc(66.6666666667% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--8-col, .mdl-grid--no-spacing >\n .mdl-cell--8-col-desktop.mdl-cell--8-col-desktop {\n width: 66.6666666667%; }\n .mdl-cell--9-col,\n .mdl-cell--9-col-desktop.mdl-cell--9-col-desktop {\n width: calc(75% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--9-col, .mdl-grid--no-spacing >\n .mdl-cell--9-col-desktop.mdl-cell--9-col-desktop {\n width: 75%; }\n .mdl-cell--10-col,\n .mdl-cell--10-col-desktop.mdl-cell--10-col-desktop {\n width: calc(83.3333333333% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--10-col, .mdl-grid--no-spacing >\n .mdl-cell--10-col-desktop.mdl-cell--10-col-desktop {\n width: 83.3333333333%; }\n .mdl-cell--11-col,\n .mdl-cell--11-col-desktop.mdl-cell--11-col-desktop {\n width: calc(91.6666666667% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--11-col, .mdl-grid--no-spacing >\n .mdl-cell--11-col-desktop.mdl-cell--11-col-desktop {\n width: 91.6666666667%; }\n .mdl-cell--12-col,\n .mdl-cell--12-col-desktop.mdl-cell--12-col-desktop {\n width: calc(100% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--12-col, .mdl-grid--no-spacing >\n .mdl-cell--12-col-desktop.mdl-cell--12-col-desktop {\n width: 100%; } }\n","/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/* Material Design Lite */\n\n// Variables and mixins\n@import \"variables\";\n@import \"mixins\";\n\n// Resets and dependencies\n@import \"resets/resets\";\n@import \"typography/typography\";\n\n// Components\n@import \"palette/palette\";\n@import \"ripple/ripple\";\n@import \"animation/animation\";\n@import \"badge/badge\";\n@import \"button/button\";\n@import \"card/card\";\n@import \"checkbox/checkbox\";\n@import \"data-table/data-table\";\n@import \"footer/mega_footer\";\n@import \"footer/mini_footer\";\n@import \"icon-toggle/icon-toggle\";\n@import \"menu/menu\";\n@import \"progress/progress\";\n@import \"layout/layout\";\n@import \"radio/radio\";\n@import \"slider/slider\";\n@import \"spinner/spinner\";\n@import \"switch/switch\";\n@import \"tabs/tabs\";\n@import \"textfield/textfield\";\n@import \"tooltip/tooltip\";\n@import \"shadow/shadow\";\n@import \"grid/grid\";\n",null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null],"sourceRoot":"/source/"} \ No newline at end of file diff --git a/examples/scalajs-play-core-react/server/src/main/assets/material/material.min.js b/examples/scalajs-play-core-react/server/src/main/assets/material/material.min.js new file mode 100644 index 0000000..cb9bbca --- /dev/null +++ b/examples/scalajs-play-core-react/server/src/main/assets/material/material.min.js @@ -0,0 +1,10 @@ +/** + * material-design-lite - Material Design Components in CSS, JS and HTML + * @version v1.0.6 + * @license Apache-2.0 + * @copyright 2015 Google, Inc. + * @link https://github.com/google/material-design-lite + */ +!function(){"use strict";function e(e,t){if(e){if(t.element_.classList.contains(t.CssClasses_.MDL_JS_RIPPLE_EFFECT)){var s=document.createElement("span");s.classList.add(t.CssClasses_.MDL_RIPPLE_CONTAINER),s.classList.add(t.CssClasses_.MDL_JS_RIPPLE_EFFECT);var i=document.createElement("span");i.classList.add(t.CssClasses_.MDL_RIPPLE),s.appendChild(i),e.appendChild(s)}e.addEventListener("click",function(s){s.preventDefault();var i=e.href.split("#")[1],n=t.element_.querySelector("#"+i);t.resetTabState_(),t.resetPanelState_(),e.classList.add(t.CssClasses_.ACTIVE_CLASS),n.classList.add(t.CssClasses_.ACTIVE_CLASS)})}}function t(e,t,s,i){if(i.tabBar_.classList.contains(i.CssClasses_.JS_RIPPLE_EFFECT)){var n=document.createElement("span");n.classList.add(i.CssClasses_.RIPPLE_CONTAINER),n.classList.add(i.CssClasses_.JS_RIPPLE_EFFECT);var a=document.createElement("span");a.classList.add(i.CssClasses_.RIPPLE),n.appendChild(a),e.appendChild(n)}e.addEventListener("click",function(n){n.preventDefault();var a=e.href.split("#")[1],l=i.content_.querySelector("#"+a);i.resetTabState_(t),i.resetPanelState_(s),e.classList.add(i.CssClasses_.IS_ACTIVE),l.classList.add(i.CssClasses_.IS_ACTIVE)})}var s={upgradeDom:function(e,t){},upgradeElement:function(e,t){},upgradeElements:function(e){},upgradeAllRegistered:function(){},registerUpgradedCallback:function(e,t){},register:function(e){},downgradeElements:function(e){}};s=function(){function e(e,t){for(var s=0;sd;d++){if(r=l[d],!r)throw new Error("Unable to find a registered component for the given class.");a.push(r.className),i.setAttribute("data-upgraded",a.join(","));var h=new r.classConstructor(i);h[C]=r,c.push(h);for(var u=0,m=r.callbacks.length;m>u;u++)r.callbacks[u](i);r.widget&&(i[r.className]=h);var E=document.createEvent("Events");E.initEvent("mdl-componentupgraded",!0,!0),i.dispatchEvent(E)}}function a(e){Array.isArray(e)||(e="function"==typeof e.item?Array.prototype.slice.call(e):[e]);for(var t,s=0,i=e.length;i>s;s++)t=e[s],t instanceof HTMLElement&&(n(t),t.children.length>0&&a(t.children))}function l(t){var s="undefined"==typeof t.widget&&"undefined"==typeof t.widget,i=!0;s||(i=t.widget||t.widget);var n={classConstructor:t.constructor||t.constructor,className:t.classAsString||t.classAsString,cssClass:t.cssClass||t.cssClass,widget:i,callbacks:[]};if(p.forEach(function(e){if(e.cssClass===n.cssClass)throw new Error("The provided cssClass has already been registered: "+e.cssClass);if(e.className===n.className)throw new Error("The provided className has already been registered")}),t.constructor.prototype.hasOwnProperty(C))throw new Error("MDL component classes must not have "+C+" defined as a property.");var a=e(t.classAsString,n);a||p.push(n)}function o(t,s){var i=e(t);i&&i.callbacks.push(s)}function r(){for(var e=0;e0&&this.container_.classList.contains(this.CssClasses_.IS_VISIBLE)&&(e.keyCode===this.Keycodes_.UP_ARROW?(e.preventDefault(),t[t.length-1].focus()):e.keyCode===this.Keycodes_.DOWN_ARROW&&(e.preventDefault(),t[0].focus()))}},_.prototype.handleItemKeyboardEvent_=function(e){if(this.element_&&this.container_){var t=this.element_.querySelectorAll("."+this.CssClasses_.ITEM+":not([disabled])");if(t&&t.length>0&&this.container_.classList.contains(this.CssClasses_.IS_VISIBLE)){var s=Array.prototype.slice.call(t).indexOf(e.target);if(e.keyCode===this.Keycodes_.UP_ARROW)e.preventDefault(),s>0?t[s-1].focus():t[t.length-1].focus();else if(e.keyCode===this.Keycodes_.DOWN_ARROW)e.preventDefault(),t.length>s+1?t[s+1].focus():t[0].focus();else if(e.keyCode===this.Keycodes_.SPACE||e.keyCode===this.Keycodes_.ENTER){e.preventDefault();var i=new MouseEvent("mousedown");e.target.dispatchEvent(i),i=new MouseEvent("mouseup"),e.target.dispatchEvent(i),e.target.click()}else e.keyCode===this.Keycodes_.ESCAPE&&(e.preventDefault(),this.hide())}}},_.prototype.handleItemClick_=function(e){e.target.hasAttribute("disabled")?e.stopPropagation():(this.closing_=!0,window.setTimeout(function(e){this.hide(),this.closing_=!1}.bind(this),this.Constant_.CLOSE_TIMEOUT))},_.prototype.applyClip_=function(e,t){this.element_.classList.contains(this.CssClasses_.UNALIGNED)?this.element_.style.clip="":this.element_.classList.contains(this.CssClasses_.BOTTOM_RIGHT)?this.element_.style.clip="rect(0 "+t+"px 0 "+t+"px)":this.element_.classList.contains(this.CssClasses_.TOP_LEFT)?this.element_.style.clip="rect("+e+"px 0 "+e+"px 0)":this.element_.classList.contains(this.CssClasses_.TOP_RIGHT)?this.element_.style.clip="rect("+e+"px "+t+"px "+e+"px "+t+"px)":this.element_.style.clip=""},_.prototype.addAnimationEndListener_=function(){var e=function(){this.element_.removeEventListener("transitionend",e),this.element_.removeEventListener("webkitTransitionEnd",e),this.element_.classList.remove(this.CssClasses_.IS_ANIMATING)}.bind(this);this.element_.addEventListener("transitionend",e),this.element_.addEventListener("webkitTransitionEnd",e)},_.prototype.show=function(e){if(this.element_&&this.container_&&this.outline_){var t=this.element_.getBoundingClientRect().height,s=this.element_.getBoundingClientRect().width;this.container_.style.width=s+"px",this.container_.style.height=t+"px",this.outline_.style.width=s+"px",this.outline_.style.height=t+"px";for(var i=this.Constant_.TRANSITION_DURATION_SECONDS*this.Constant_.TRANSITION_DURATION_FRACTION,n=this.element_.querySelectorAll("."+this.CssClasses_.ITEM),a=0;a=this.maxRows&&e.preventDefault()},E.prototype.onFocus_=function(e){this.element_.classList.add(this.CssClasses_.IS_FOCUSED)},E.prototype.onBlur_=function(e){this.element_.classList.remove(this.CssClasses_.IS_FOCUSED)},E.prototype.updateClasses_=function(){this.checkDisabled(),this.checkValidity(),this.checkDirty()},E.prototype.checkDisabled=function(){this.input_.disabled?this.element_.classList.add(this.CssClasses_.IS_DISABLED):this.element_.classList.remove(this.CssClasses_.IS_DISABLED)},E.prototype.checkDisabled=E.prototype.checkDisabled,E.prototype.checkValidity=function(){this.input_.validity&&(this.input_.validity.valid?this.element_.classList.remove(this.CssClasses_.IS_INVALID):this.element_.classList.add(this.CssClasses_.IS_INVALID))},E.prototype.checkValidity=E.prototype.checkValidity,E.prototype.checkDirty=function(){this.input_.value&&this.input_.value.length>0?this.element_.classList.add(this.CssClasses_.IS_DIRTY):this.element_.classList.remove(this.CssClasses_.IS_DIRTY)},E.prototype.checkDirty=E.prototype.checkDirty,E.prototype.disable=function(){this.input_.disabled=!0,this.updateClasses_()},E.prototype.disable=E.prototype.disable,E.prototype.enable=function(){this.input_.disabled=!1,this.updateClasses_()},E.prototype.enable=E.prototype.enable,E.prototype.change=function(e){this.input_.value=e||"",this.updateClasses_()},E.prototype.change=E.prototype.change,E.prototype.init=function(){if(this.element_&&(this.label_=this.element_.querySelector("."+this.CssClasses_.LABEL),this.input_=this.element_.querySelector("."+this.CssClasses_.INPUT),this.input_)){this.input_.hasAttribute(this.Constant_.MAX_ROWS_ATTRIBUTE)&&(this.maxRows=parseInt(this.input_.getAttribute(this.Constant_.MAX_ROWS_ATTRIBUTE),10),isNaN(this.maxRows)&&(this.maxRows=this.Constant_.NO_MAX_ROWS)),this.boundUpdateClassesHandler=this.updateClasses_.bind(this),this.boundFocusHandler=this.onFocus_.bind(this),this.boundBlurHandler=this.onBlur_.bind(this),this.input_.addEventListener("input",this.boundUpdateClassesHandler),this.input_.addEventListener("focus",this.boundFocusHandler),this.input_.addEventListener("blur",this.boundBlurHandler),this.maxRows!==this.Constant_.NO_MAX_ROWS&&(this.boundKeyDownHandler=this.onKeyDown_.bind(this),this.input_.addEventListener("keydown",this.boundKeyDownHandler));var e=this.element_.classList.contains(this.CssClasses_.IS_INVALID);this.updateClasses_(),this.element_.classList.add(this.CssClasses_.IS_UPGRADED),e&&this.element_.classList.add(this.CssClasses_.IS_INVALID)}},E.prototype.mdlDowngrade_=function(){this.input_.removeEventListener("input",this.boundUpdateClassesHandler),this.input_.removeEventListener("focus",this.boundFocusHandler),this.input_.removeEventListener("blur",this.boundBlurHandler),this.boundKeyDownHandler&&this.input_.removeEventListener("keydown",this.boundKeyDownHandler)},E.prototype.mdlDowngrade=E.prototype.mdlDowngrade_,E.prototype.mdlDowngrade=E.prototype.mdlDowngrade,s.register({constructor:E,classAsString:"MaterialTextfield",cssClass:"mdl-js-textfield",widget:!0});var L=function(e){this.element_=e,this.init()};window.MaterialTooltip=L,L.prototype.Constant_={},L.prototype.CssClasses_={IS_ACTIVE:"is-active"},L.prototype.handleMouseEnter_=function(e){e.stopPropagation();var t=e.target.getBoundingClientRect(),s=t.left+t.width/2,i=-1*(this.element_.offsetWidth/2);0>s+i?(this.element_.style.left=0,this.element_.style.marginLeft=0):(this.element_.style.left=s+"px",this.element_.style.marginLeft=i+"px"),this.element_.style.top=t.top+t.height+10+"px",this.element_.classList.add(this.CssClasses_.IS_ACTIVE),window.addEventListener("scroll",this.boundMouseLeaveHandler,!1),window.addEventListener("touchmove",this.boundMouseLeaveHandler,!1)},L.prototype.handleMouseLeave_=function(e){e.stopPropagation(),this.element_.classList.remove(this.CssClasses_.IS_ACTIVE),window.removeEventListener("scroll",this.boundMouseLeaveHandler),window.removeEventListener("touchmove",this.boundMouseLeaveHandler,!1)},L.prototype.init=function(){if(this.element_){var e=this.element_.getAttribute("for");e&&(this.forElement_=document.getElementById(e)),this.forElement_&&(this.forElement_.hasAttribute("tabindex")||this.forElement_.setAttribute("tabindex","0"),this.boundMouseEnterHandler=this.handleMouseEnter_.bind(this),this.boundMouseLeaveHandler=this.handleMouseLeave_.bind(this),this.forElement_.addEventListener("mouseenter",this.boundMouseEnterHandler,!1),this.forElement_.addEventListener("click",this.boundMouseEnterHandler,!1),this.forElement_.addEventListener("blur",this.boundMouseLeaveHandler),this.forElement_.addEventListener("touchstart",this.boundMouseEnterHandler,!1),this.forElement_.addEventListener("mouseleave",this.boundMouseLeaveHandler))}},L.prototype.mdlDowngrade_=function(){this.forElement_&&(this.forElement_.removeEventListener("mouseenter",this.boundMouseEnterHandler,!1),this.forElement_.removeEventListener("click",this.boundMouseEnterHandler,!1),this.forElement_.removeEventListener("touchstart",this.boundMouseEnterHandler,!1),this.forElement_.removeEventListener("mouseleave",this.boundMouseLeaveHandler))},L.prototype.mdlDowngrade=L.prototype.mdlDowngrade_,L.prototype.mdlDowngrade=L.prototype.mdlDowngrade,s.register({constructor:L,classAsString:"MaterialTooltip",cssClass:"mdl-tooltip"});var I=function(e){this.element_=e,this.init()};window.MaterialLayout=I,I.prototype.Constant_={MAX_WIDTH:"(max-width: 1024px)",TAB_SCROLL_PIXELS:100,MENU_ICON:"menu",CHEVRON_LEFT:"chevron_left",CHEVRON_RIGHT:"chevron_right"},I.prototype.Mode_={STANDARD:0,SEAMED:1,WATERFALL:2,SCROLL:3},I.prototype.CssClasses_={CONTAINER:"mdl-layout__container",HEADER:"mdl-layout__header",DRAWER:"mdl-layout__drawer",CONTENT:"mdl-layout__content",DRAWER_BTN:"mdl-layout__drawer-button",ICON:"material-icons",JS_RIPPLE_EFFECT:"mdl-js-ripple-effect",RIPPLE_CONTAINER:"mdl-layout__tab-ripple-container",RIPPLE:"mdl-ripple",RIPPLE_IGNORE_EVENTS:"mdl-js-ripple-effect--ignore-events",HEADER_SEAMED:"mdl-layout__header--seamed",HEADER_WATERFALL:"mdl-layout__header--waterfall",HEADER_SCROLL:"mdl-layout__header--scroll",FIXED_HEADER:"mdl-layout--fixed-header",OBFUSCATOR:"mdl-layout__obfuscator",TAB_BAR:"mdl-layout__tab-bar",TAB_CONTAINER:"mdl-layout__tab-bar-container",TAB:"mdl-layout__tab",TAB_BAR_BUTTON:"mdl-layout__tab-bar-button",TAB_BAR_LEFT_BUTTON:"mdl-layout__tab-bar-left-button",TAB_BAR_RIGHT_BUTTON:"mdl-layout__tab-bar-right-button",PANEL:"mdl-layout__tab-panel",HAS_DRAWER:"has-drawer",HAS_TABS:"has-tabs",HAS_SCROLLING_HEADER:"has-scrolling-header",CASTING_SHADOW:"is-casting-shadow",IS_COMPACT:"is-compact",IS_SMALL_SCREEN:"is-small-screen",IS_DRAWER_OPEN:"is-visible",IS_ACTIVE:"is-active",IS_UPGRADED:"is-upgraded",IS_ANIMATING:"is-animating",ON_LARGE_SCREEN:"mdl-layout--large-screen-only",ON_SMALL_SCREEN:"mdl-layout--small-screen-only"},I.prototype.contentScrollHandler_=function(){this.header_.classList.contains(this.CssClasses_.IS_ANIMATING)||(this.content_.scrollTop>0&&!this.header_.classList.contains(this.CssClasses_.IS_COMPACT)?(this.header_.classList.add(this.CssClasses_.CASTING_SHADOW),this.header_.classList.add(this.CssClasses_.IS_COMPACT),this.header_.classList.add(this.CssClasses_.IS_ANIMATING)):this.content_.scrollTop<=0&&this.header_.classList.contains(this.CssClasses_.IS_COMPACT)&&(this.header_.classList.remove(this.CssClasses_.CASTING_SHADOW),this.header_.classList.remove(this.CssClasses_.IS_COMPACT),this.header_.classList.add(this.CssClasses_.IS_ANIMATING)))},I.prototype.screenSizeHandler_=function(){this.screenSizeMediaQuery_.matches?this.element_.classList.add(this.CssClasses_.IS_SMALL_SCREEN):(this.element_.classList.remove(this.CssClasses_.IS_SMALL_SCREEN),this.drawer_&&(this.drawer_.classList.remove(this.CssClasses_.IS_DRAWER_OPEN),this.obfuscator_.classList.remove(this.CssClasses_.IS_DRAWER_OPEN)))},I.prototype.drawerToggleHandler_=function(){this.drawer_.classList.toggle(this.CssClasses_.IS_DRAWER_OPEN),this.obfuscator_.classList.toggle(this.CssClasses_.IS_DRAWER_OPEN)},I.prototype.headerTransitionEndHandler_=function(){this.header_.classList.remove(this.CssClasses_.IS_ANIMATING)},I.prototype.headerClickHandler_=function(){this.header_.classList.contains(this.CssClasses_.IS_COMPACT)&&(this.header_.classList.remove(this.CssClasses_.IS_COMPACT),this.header_.classList.add(this.CssClasses_.IS_ANIMATING))},I.prototype.resetTabState_=function(e){for(var t=0;tn;n++){var a=s[n];a.classList&&a.classList.contains(this.CssClasses_.HEADER)&&(this.header_=a),a.classList&&a.classList.contains(this.CssClasses_.DRAWER)&&(this.drawer_=a),a.classList&&a.classList.contains(this.CssClasses_.CONTENT)&&(this.content_=a)}this.header_&&(this.tabBar_=this.header_.querySelector("."+this.CssClasses_.TAB_BAR));var l=this.Mode_.STANDARD;if(this.header_&&(this.header_.classList.contains(this.CssClasses_.HEADER_SEAMED)?l=this.Mode_.SEAMED:this.header_.classList.contains(this.CssClasses_.HEADER_WATERFALL)?(l=this.Mode_.WATERFALL,this.header_.addEventListener("transitionend",this.headerTransitionEndHandler_.bind(this)),this.header_.addEventListener("click",this.headerClickHandler_.bind(this))):this.header_.classList.contains(this.CssClasses_.HEADER_SCROLL)&&(l=this.Mode_.SCROLL,e.classList.add(this.CssClasses_.HAS_SCROLLING_HEADER)),l===this.Mode_.STANDARD?(this.header_.classList.add(this.CssClasses_.CASTING_SHADOW),this.tabBar_&&this.tabBar_.classList.add(this.CssClasses_.CASTING_SHADOW)):l===this.Mode_.SEAMED||l===this.Mode_.SCROLL?(this.header_.classList.remove(this.CssClasses_.CASTING_SHADOW),this.tabBar_&&this.tabBar_.classList.remove(this.CssClasses_.CASTING_SHADOW)):l===this.Mode_.WATERFALL&&(this.content_.addEventListener("scroll",this.contentScrollHandler_.bind(this)),this.contentScrollHandler_())),this.drawer_){var o=this.element_.querySelector("."+this.CssClasses_.DRAWER_BTN);if(!o){o=document.createElement("div"),o.classList.add(this.CssClasses_.DRAWER_BTN);var r=document.createElement("i");r.classList.add(this.CssClasses_.ICON),r.textContent=this.Constant_.MENU_ICON,o.appendChild(r)}this.drawer_.classList.contains(this.CssClasses_.ON_LARGE_SCREEN)?o.classList.add(this.CssClasses_.ON_LARGE_SCREEN):this.drawer_.classList.contains(this.CssClasses_.ON_SMALL_SCREEN)&&o.classList.add(this.CssClasses_.ON_SMALL_SCREEN),o.addEventListener("click",this.drawerToggleHandler_.bind(this)),this.element_.classList.add(this.CssClasses_.HAS_DRAWER),this.element_.classList.contains(this.CssClasses_.FIXED_HEADER)?this.header_.insertBefore(o,this.header_.firstChild):this.element_.insertBefore(o,this.content_);var d=document.createElement("div");d.classList.add(this.CssClasses_.OBFUSCATOR),this.element_.appendChild(d),d.addEventListener("click",this.drawerToggleHandler_.bind(this)),this.obfuscator_=d}if(this.screenSizeMediaQuery_=window.matchMedia(this.Constant_.MAX_WIDTH),this.screenSizeMediaQuery_.addListener(this.screenSizeHandler_.bind(this)),this.screenSizeHandler_(),this.header_&&this.tabBar_){this.element_.classList.add(this.CssClasses_.HAS_TABS);var _=document.createElement("div");_.classList.add(this.CssClasses_.TAB_CONTAINER),this.header_.insertBefore(_,this.tabBar_),this.header_.removeChild(this.tabBar_);var h=document.createElement("div");h.classList.add(this.CssClasses_.TAB_BAR_BUTTON),h.classList.add(this.CssClasses_.TAB_BAR_LEFT_BUTTON);var p=document.createElement("i");p.classList.add(this.CssClasses_.ICON),p.textContent=this.Constant_.CHEVRON_LEFT,h.appendChild(p),h.addEventListener("click",function(){this.tabBar_.scrollLeft-=this.Constant_.TAB_SCROLL_PIXELS}.bind(this));var c=document.createElement("div");c.classList.add(this.CssClasses_.TAB_BAR_BUTTON),c.classList.add(this.CssClasses_.TAB_BAR_RIGHT_BUTTON);var u=document.createElement("i");u.classList.add(this.CssClasses_.ICON),u.textContent=this.Constant_.CHEVRON_RIGHT,c.appendChild(u),c.addEventListener("click",function(){this.tabBar_.scrollLeft+=this.Constant_.TAB_SCROLL_PIXELS}.bind(this)),_.appendChild(h),_.appendChild(this.tabBar_),_.appendChild(c);var C=function(){this.tabBar_.scrollLeft>0?h.classList.add(this.CssClasses_.IS_ACTIVE):h.classList.remove(this.CssClasses_.IS_ACTIVE),this.tabBar_.scrollLeft0)return;this.setFrameCount(1);var i,n,a=e.currentTarget.getBoundingClientRect();if(0===e.clientX&&0===e.clientY)i=Math.round(a.width/2),n=Math.round(a.height/2);else{var l=e.clientX?e.clientX:e.touches[0].clientX,o=e.clientY?e.clientY:e.touches[0].clientY;i=Math.round(l-a.left),n=Math.round(o-a.top)}this.setRippleXY(i,n),this.setRippleStyles(!0),window.requestAnimationFrame(this.animFrameHandler.bind(this))}},b.prototype.upHandler_=function(e){e&&2!==e.detail&&this.rippleElement_.classList.remove(this.CssClasses_.IS_VISIBLE),window.setTimeout(function(){this.rippleElement_.classList.remove(this.CssClasses_.IS_VISIBLE)}.bind(this),0)},b.prototype.init=function(){if(this.element_){var e=this.element_.classList.contains(this.CssClasses_.RIPPLE_CENTER);this.element_.classList.contains(this.CssClasses_.RIPPLE_EFFECT_IGNORE_EVENTS)||(this.rippleElement_=this.element_.querySelector("."+this.CssClasses_.RIPPLE),this.frameCount_=0,this.rippleSize_=0,this.x_=0,this.y_=0,this.ignoringMouseDown_=!1,this.boundDownHandler=this.downHandler_.bind(this),this.element_.addEventListener("mousedown",this.boundDownHandler),this.element_.addEventListener("touchstart",this.boundDownHandler),this.boundUpHandler=this.upHandler_.bind(this),this.element_.addEventListener("mouseup",this.boundUpHandler),this.element_.addEventListener("mouseleave",this.boundUpHandler),this.element_.addEventListener("touchend",this.boundUpHandler),this.element_.addEventListener("blur",this.boundUpHandler),this.getFrameCount=function(){return this.frameCount_},this.setFrameCount=function(e){this.frameCount_=e},this.getRippleElement=function(){return this.rippleElement_},this.setRippleXY=function(e,t){this.x_=e,this.y_=t},this.setRippleStyles=function(t){if(null!==this.rippleElement_){var s,i,n,a="translate("+this.x_+"px, "+this.y_+"px)";t?(i=this.Constant_.INITIAL_SCALE,n=this.Constant_.INITIAL_SIZE):(i=this.Constant_.FINAL_SCALE,n=this.rippleSize_+"px",e&&(a="translate("+this.boundWidth/2+"px, "+this.boundHeight/2+"px)")),s="translate(-50%, -50%) "+a+i,this.rippleElement_.style.webkitTransform=s,this.rippleElement_.style.msTransform=s,this.rippleElement_.style.transform=s,t?this.rippleElement_.classList.remove(this.CssClasses_.IS_ANIMATING):this.rippleElement_.classList.add(this.CssClasses_.IS_ANIMATING)}},this.animFrameHandler=function(){this.frameCount_-->0?window.requestAnimationFrame(this.animFrameHandler.bind(this)):this.setRippleStyles(!1)})}},b.prototype.mdlDowngrade_=function(){this.element_.removeEventListener("mousedown",this.boundDownHandler),this.element_.removeEventListener("touchstart",this.boundDownHandler),this.element_.removeEventListener("mouseup",this.boundUpHandler),this.element_.removeEventListener("mouseleave",this.boundUpHandler),this.element_.removeEventListener("touchend",this.boundUpHandler),this.element_.removeEventListener("blur",this.boundUpHandler)},b.prototype.mdlDowngrade=b.prototype.mdlDowngrade_,b.prototype.mdlDowngrade=b.prototype.mdlDowngrade,s.register({constructor:b,classAsString:"MaterialRipple",cssClass:"mdl-js-ripple-effect",widget:!1})}(); +//# sourceMappingURL=material.min.js.map diff --git a/examples/scalajs-play-core-react/server/src/main/assets/material/material.min.js.map b/examples/scalajs-play-core-react/server/src/main/assets/material/material.min.js.map new file mode 100644 index 0000000..86ca989 --- /dev/null +++ b/examples/scalajs-play-core-react/server/src/main/assets/material/material.min.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["material.js","mdlComponentHandler.js","tabs.js","layout.js","rAF.js","button.js","checkbox.js","icon-toggle.js","menu.js","progress.js","radio.js","slider.js","spinner.js","switch.js","textfield.js","tooltip.js","data-table.js","ripple.js"],"names":["MaterialTab","tab","ctx","element_","classList","contains","CssClasses_","MDL_JS_RIPPLE_EFFECT","rippleContainer","document","createElement","add","MDL_RIPPLE_CONTAINER","ripple","MDL_RIPPLE","appendChild","addEventListener","e","preventDefault","href","split","panel","querySelector","resetTabState_","resetPanelState_","ACTIVE_CLASS","MaterialLayoutTab","tabs","panels","layout","tabBar_","JS_RIPPLE_EFFECT","RIPPLE_CONTAINER","RIPPLE","content_","IS_ACTIVE","componentHandler","upgradeDom","optJsClass","optCssClass","upgradeElement","element","upgradeElements","elements","upgradeAllRegistered","registerUpgradedCallback","jsClass","callback","register","config","downgradeElements","nodes","findRegisteredClass_","name","optReplace","i","registeredComponents_","length","className","getUpgradedListOfElement_","dataUpgraded","getAttribute","isElementUpgraded_","upgradedList","indexOf","upgradeDomInternal","cssClass","registeredClass","querySelectorAll","n","upgradeElementInternal","Element","Error","classesToUpgrade","push","forEach","component","setAttribute","join","instance","classConstructor","componentConfigProperty_","createdComponents_","j","m","callbacks","widget","ev","createEvent","initEvent","dispatchEvent","upgradeElementsInternal","Array","isArray","item","prototype","slice","call","HTMLElement","children","registerInternal","widgetMissing","newConfig","constructor","classAsString","hasOwnProperty","found","registerUpgradedCallbackInternal","regClass","upgradeAllRegisteredInternal","findCreatedComponentByNodeInternal","node","deconstructComponentInternal","downgradeMethod_","componentIndex","splice","upgrades","componentPlace","downgradeNodesInternal","downgradeNode","NodeList","Node","ComponentConfigPublic","ComponentConfig","Component","window","documentElement","Date","now","getTime","vendors","requestAnimationFrame","vp","cancelAnimationFrame","test","navigator","userAgent","lastTime","nextTime","Math","max","setTimeout","clearTimeout","MaterialButton","this","init","Constant_","RIPPLE_EFFECT","blurHandler_","event","blur","disable","disabled","enable","rippleElement_","boundRippleBlurHandler","bind","boundButtonBlurHandler","mdlDowngrade_","removeEventListener","mdlDowngrade","MaterialCheckbox","TINY_TIMEOUT","INPUT","BOX_OUTLINE","FOCUS_HELPER","TICK_OUTLINE","RIPPLE_IGNORE_EVENTS","RIPPLE_CENTER","IS_FOCUSED","IS_DISABLED","IS_CHECKED","IS_UPGRADED","onChange_","updateClasses_","onFocus_","onBlur_","remove","onMouseUp_","blur_","checkDisabled","checkToggleState","inputElement_","checked","check","uncheck","boxOutline","tickContainer","tickOutline","rippleContainerElement_","boundRippleMouseUp","boundInputOnChange","boundInputOnFocus","boundInputOnBlur","boundElementMouseUp","MaterialIconToggle","boundElementOnMouseUp","MaterialMenu","TRANSITION_DURATION_SECONDS","TRANSITION_DURATION_FRACTION","CLOSE_TIMEOUT","Keycodes_","ENTER","ESCAPE","SPACE","UP_ARROW","DOWN_ARROW","CONTAINER","OUTLINE","ITEM","ITEM_RIPPLE_CONTAINER","IS_VISIBLE","IS_ANIMATING","BOTTOM_LEFT","BOTTOM_RIGHT","TOP_LEFT","TOP_RIGHT","UNALIGNED","container","parentElement","insertBefore","removeChild","container_","outline","outline_","forElId","forEl","getElementById","forElement_","handleForClick_","handleForKeyboardEvent_","items","boundItemKeydown_","handleItemKeyboardEvent_","boundItemClick_","handleItemClick_","tabIndex","evt","rect","getBoundingClientRect","forRect","style","right","top","offsetTop","offsetHeight","left","offsetLeft","bottom","toggle","keyCode","focus","currentIndex","target","MouseEvent","click","hide","hasAttribute","stopPropagation","closing_","applyClip_","height","width","clip","addAnimationEndListener_","cleanup","show","transitionDuration","itemDelay","transitionDelay","parentNode","MaterialProgress","INDETERMINATE_CLASS","setProgress","p","progressbar_","setBuffer","bufferbar_","auxbar_","el","firstChild","MaterialRadio","JS_RADIO","RADIO_BTN","RADIO_OUTER_CIRCLE","RADIO_INNER_CIRCLE","radios","getElementsByClassName","button","btnElement_","onMouseup_","boundChangeHandler_","boundFocusHandler_","boundBlurHandler_","boundMouseUpHandler_","outerCircle","innerCircle","MaterialSlider","isIE_","msPointerEnabled","IE_CONTAINER","SLIDER_CONTAINER","BACKGROUND_FLEX","BACKGROUND_LOWER","BACKGROUND_UPPER","IS_LOWEST_VALUE","onInput_","updateValueStyles_","onContainerMouseDown_","newEvent","buttons","clientX","clientY","y","fraction","value","min","backgroundLower_","flex","webkitFlex","backgroundUpper_","change","containerIE","backgroundFlex","boundInputHandler","boundChangeHandler","boundMouseUpHandler","boundContainerMouseDownHandler","MaterialSpinner","MDL_SPINNER_LAYER_COUNT","MDL_SPINNER_LAYER","MDL_SPINNER_CIRCLE_CLIPPER","MDL_SPINNER_CIRCLE","MDL_SPINNER_GAP_PATCH","MDL_SPINNER_LEFT","MDL_SPINNER_RIGHT","createLayer","index","layer","leftClipper","gapPatch","rightClipper","circleOwners","circle","stop","start","MaterialSwitch","TRACK","THUMB","on","off","track","thumb","focusHelper","boundFocusHandler","boundBlurHandler","MaterialTabs","TAB_CLASS","PANEL_CLASS","UPGRADED_CLASS","MDL_JS_RIPPLE_EFFECT_IGNORE_EVENTS","initTabs_","tabs_","panels_","k","MaterialTextfield","maxRows","NO_MAX_ROWS","MAX_ROWS_ATTRIBUTE","LABEL","IS_DIRTY","IS_INVALID","onKeyDown_","currentRowCount","checkValidity","checkDirty","input_","validity","valid","label_","parseInt","isNaN","boundUpdateClassesHandler","boundKeyDownHandler","invalid","MaterialTooltip","handleMouseEnter_","props","marginLeft","offsetWidth","boundMouseLeaveHandler","handleMouseLeave_","boundMouseEnterHandler","MaterialLayout","MAX_WIDTH","TAB_SCROLL_PIXELS","MENU_ICON","CHEVRON_LEFT","CHEVRON_RIGHT","Mode_","STANDARD","SEAMED","WATERFALL","SCROLL","HEADER","DRAWER","CONTENT","DRAWER_BTN","ICON","HEADER_SEAMED","HEADER_WATERFALL","HEADER_SCROLL","FIXED_HEADER","OBFUSCATOR","TAB_BAR","TAB_CONTAINER","TAB","TAB_BAR_BUTTON","TAB_BAR_LEFT_BUTTON","TAB_BAR_RIGHT_BUTTON","PANEL","HAS_DRAWER","HAS_TABS","HAS_SCROLLING_HEADER","CASTING_SHADOW","IS_COMPACT","IS_SMALL_SCREEN","IS_DRAWER_OPEN","ON_LARGE_SCREEN","ON_SMALL_SCREEN","contentScrollHandler_","header_","scrollTop","screenSizeHandler_","screenSizeMediaQuery_","matches","drawer_","obfuscator_","drawerToggleHandler_","headerTransitionEndHandler_","headerClickHandler_","tabBar","directChildren","childNodes","numChildren","c","child","mode","drawerButton","drawerButtonIcon","textContent","obfuscator","matchMedia","addListener","tabContainer","leftButton","leftButtonIcon","scrollLeft","rightButton","rightButtonIcon","tabScrollHandler","scrollWidth","MaterialDataTable","DATA_TABLE","SELECTABLE","SELECT_ELEMENT","IS_SELECTED","selectRow_","checkbox","row","opt_rows","createCheckbox_","label","labelClasses","type","firstHeader","rows","th","headerCheckbox","firstCell","td","rowCheckbox","MaterialRipple","INITIAL_SCALE","INITIAL_SIZE","INITIAL_OPACITY","FINAL_OPACITY","FINAL_SCALE","RIPPLE_EFFECT_IGNORE_EVENTS","downHandler_","boundHeight","boundWidth","rippleSize_","sqrt","ignoringMouseDown_","frameCount","getFrameCount","setFrameCount","x","bound","currentTarget","round","touches","setRippleXY","setRippleStyles","animFrameHandler","upHandler_","detail","recentering","frameCount_","x_","y_","boundDownHandler","boundUpHandler","fC","getRippleElement","newX","newY","transformString","scale","size","offset","webkitTransform","msTransform","transform"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,CCPA,WACA,YC+GA,SAAAA,GAAAC,EAAAC,GACA,GAAAD,EAAA,CACA,GAAAC,EAAAC,SAAAC,UAAAC,SAAAH,EAAAI,YAAAC,sBAAA,CACA,GAAAC,GAAAC,SAAAC,cAAA,OACAF,GAAAJ,UAAAO,IAAAT,EAAAI,YAAAM,sBACAJ,EAAAJ,UAAAO,IAAAT,EAAAI,YAAAC,qBACA,IAAAM,GAAAJ,SAAAC,cAAA,OACAG,GAAAT,UAAAO,IAAAT,EAAAI,YAAAQ,YACAN,EAAAO,YAAAF,GACAZ,EAAAc,YAAAP,GAEAP,EAAAe,iBAAA,QAAA,SAAAC,GACAA,EAAAC,gBACA,IAAAC,GAAAlB,EAAAkB,KAAAC,MAAA,KAAA,GACAC,EAAAnB,EAAAC,SAAAmB,cAAA,IAAAH,EACAjB,GAAAqB,iBACArB,EAAAsB,mBACAvB,EAAAG,UAAAO,IAAAT,EAAAI,YAAAmB,cACAJ,EAAAjB,UAAAO,IAAAT,EAAAI,YAAAmB,iBC8NA,QAAAC,GAAAzB,EAAA0B,EAAAC,EAAAC,GACA,GAAAA,EAAAC,QAAA1B,UAAAC,SAAAwB,EAAAvB,YAAAyB,kBAAA,CACA,GAAAvB,GAAAC,SAAAC,cAAA,OACAF,GAAAJ,UAAAO,IAAAkB,EAAAvB,YAAA0B,kBACAxB,EAAAJ,UAAAO,IAAAkB,EAAAvB,YAAAyB,iBACA,IAAAlB,GAAAJ,SAAAC,cAAA,OACAG,GAAAT,UAAAO,IAAAkB,EAAAvB,YAAA2B,QACAzB,EAAAO,YAAAF,GACAZ,EAAAc,YAAAP,GAEAP,EAAAe,iBAAA,QAAA,SAAAC,GACAA,EAAAC,gBACA,IAAAC,GAAAlB,EAAAkB,KAAAC,MAAA,KAAA,GACAC,EAAAQ,EAAAK,SAAAZ,cAAA,IAAAH,EACAU,GAAAN,eAAAI,GACAE,EAAAL,iBAAAI,GACA3B,EAAAG,UAAAO,IAAAkB,EAAAvB,YAAA6B,WACAd,EAAAjB,UAAAO,IAAAkB,EAAAvB,YAAA6B,aFlVA,GAAAC,IAUAC,WAAA,SAAAC,EAAAC,KAQAC,eAAA,SAAAC,EAAAH,KAOAI,gBAAA,SAAAC,KAKAC,qBAAA,aAWAC,yBAAA,SAAAC,EAAAC,KAMAC,SAAA,SAAAC,KAMAC,kBAAA,SAAAC,KAGAf,GAAA,WAqBA,QAAAgB,GAAAC,EAAAC,GACA,IAAA,GAAAC,GAAA,EAAAA,EAAAC,EAAAC,OAAAF,IACA,GAAAC,EAAAD,GAAAG,YAAAL,EAIA,MAHA,mBAAAC,KACAE,EAAAD,GAAAD,GAEAE,EAAAD,EAGA,QAAA,EAUA,QAAAI,GAAAlB,GACA,GAAAmB,GAAAnB,EAAAoB,aAAA,gBAEA,OAAA,QAAAD,GAAA,IAAAA,EAAAxC,MAAA,KAYA,QAAA0C,GAAArB,EAAAK,GACA,GAAAiB,GAAAJ,EAAAlB,EACA,OAAA,KAAAsB,EAAAC,QAAAlB,GAYA,QAAAmB,GAAA3B,EAAAC,GACA,GAAA,mBAAAD,IACA,mBAAAC,GACA,IAAA,GAAAgB,GAAA,EAAAA,EAAAC,EAAAC,OAAAF,IACAU,EAAAT,EAAAD,GAAAG,UACAF,EAAAD,GAAAW,cAEA,CACA,GAAApB,GAAA,CACA,IAAA,mBAAAP,GAAA,CACA,GAAA4B,GAAAf,EAAAN,EACAqB,KACA5B,EAAA4B,EAAAD,UAKA,IAAA,GADAvB,GAAAlC,SAAA2D,iBAAA,IAAA7B,GACA8B,EAAA,EAAAA,EAAA1B,EAAAc,OAAAY,IACAC,EAAA3B,EAAA0B,GAAAvB,IAYA,QAAAwB,GAAA7B,EAAAH,GAEA,KAAA,gBAAAG,IAAAA,YAAA8B,UACA,KAAA,IAAAC,OAAA,oDAEA,IAAAT,GAAAJ,EAAAlB,GACAgC,IAGA,IAAAnC,EAUAwB,EAAArB,EAAAH,IACAmC,EAAAC,KAAAtB,EAAAd,QAXA,CACA,GAAAlC,GAAAqC,EAAArC,SACAoD,GAAAmB,QAAA,SAAAC,GAEAxE,EAAAC,SAAAuE,EAAAV,WACA,KAAAO,EAAAT,QAAAY,KACAd,EAAArB,EAAAmC,EAAAlB,YACAe,EAAAC,KAAAE,KAQA,IAAA,GAAAT,GAAAZ,EAAA,EAAAc,EAAAI,EAAAhB,OAAAY,EAAAd,EAAAA,IAAA,CAEA,GADAY,EAAAM,EAAAlB,IACAY,EAiBA,KAAA,IAAAK,OACA,6DAhBAT,GAAAW,KAAAP,EAAAT,WACAjB,EAAAoC,aAAA,gBAAAd,EAAAe,KAAA,KACA,IAAAC,GAAA,GAAAZ,GAAAa,iBAAAvC,EACAsC,GAAAE,GAAAd,EACAe,EAAAR,KAAAK,EAEA,KAAA,GAAAI,GAAA,EAAAC,EAAAjB,EAAAkB,UAAA5B,OAAA2B,EAAAD,EAAAA,IACAhB,EAAAkB,UAAAF,GAAA1C,EAGA0B,GAAAmB,SAEA7C,EAAA0B,EAAAT,WAAAqB,EAOA,IAAAQ,GAAA9E,SAAA+E,YAAA,SACAD,GAAAE,UAAA,yBAAA,GAAA,GACAhD,EAAAiD,cAAAH,IAUA,QAAAI,GAAAhD,GACAiD,MAAAC,QAAAlD,KAEAA,EADA,kBAAAA,GAAAmD,KACAF,MAAAG,UAAAC,MAAAC,KAAA,IAEAtD,GAGA,KAAA,GAAAF,GAAAc,EAAA,EAAAc,EAAA1B,EAAAc,OAAAY,EAAAd,EAAAA,IACAd,EAAAE,EAAAY,GACAd,YAAAyD,eACA5B,EAAA7B,GACAA,EAAA0D,SAAA1C,OAAA,GACAkC,EAAAlD,EAAA0D,WAWA,QAAAC,GAAAnD,GAKA,GAAAoD,GAAA,mBAAApD,GAAAqC,QACA,mBAAArC,GAAA,OACAqC,GAAA,CAEAe,KACAf,EAAArC,EAAAqC,QAAArC,EAAA,OAGA,IAAAqD,IACAtB,iBAAA/B,EAAAsD,aAAAtD,EAAA,YACAS,UAAAT,EAAAuD,eAAAvD,EAAA,cACAiB,SAAAjB,EAAAiB,UAAAjB,EAAA,SACAqC,OAAAA,EACAD,aAYA,IATA7B,EAAAmB,QAAA,SAAAmB,GACA,GAAAA,EAAA5B,WAAAoC,EAAApC,SACA,KAAA,IAAAM,OAAA,sDAAAsB,EAAA5B,SAEA,IAAA4B,EAAApC,YAAA4C,EAAA5C,UACA,KAAA,IAAAc,OAAA,wDAIAvB,EAAAsD,YAAAR,UACAU,eAAAxB,GACA,KAAA,IAAAT,OACA,uCAAAS,EACA,0BAGA,IAAAyB,GAAAtD,EAAAH,EAAAuD,cAAAF,EAEAI,IACAlD,EAAAkB,KAAA4B,GAcA,QAAAK,GAAA7D,EAAAC,GACA,GAAA6D,GAAAxD,EAAAN,EACA8D,IACAA,EAAAvB,UAAAX,KAAA3B,GAQA,QAAA8D,KACA,IAAA,GAAAxC,GAAA,EAAAA,EAAAb,EAAAC,OAAAY,IACAJ,EAAAT,EAAAa,GAAAX,WAUA,QAAAoD,GAAAC,GACA,IAAA,GAAA1C,GAAA,EAAAA,EAAAa,EAAAzB,OAAAY,IAAA,CACA,GAAAO,GAAAM,EAAAb,EACA,IAAAO,EAAAzE,WAAA4G,EACA,MAAAnC,IAYA,QAAAoC,GAAApC,GACA,GAAAA,GACAA,EAAAK,GACAD,iBAAAe,UACAU,eAAAQ,GAAA,CACArC,EAAAqC,IACA,IAAAC,GAAAhC,EAAAlB,QAAAY,EACAM,GAAAiC,OAAAD,EAAA,EAEA,IAAAE,GAAAxC,EAAAzE,SAAA0D,aAAA,iBAAAzC,MAAA,KACAiG,EAAAD,EAAApD,QACAY,EAAAK,GAAAuB,cACAY,GAAAD,OAAAE,EAAA,GACAzC,EAAAzE,SAAA0E,aAAA,gBAAAuC,EAAAtC,KAAA,KAEA,IAAAS,GAAA9E,SAAA+E,YAAA,SACAD,GAAAE,UAAA,2BAAA,GAAA,GACAb,EAAAzE,SAAAuF,cAAAH,IASA,QAAA+B,GAAAnE,GAKA,GAAAoE,GAAA,SAAAR,GACAC,EAAAF,EAAAC,IAEA,IAAA5D,YAAAyC,QAAAzC,YAAAqE,UACA,IAAA,GAAAnD,GAAA,EAAAA,EAAAlB,EAAAM,OAAAY,IACAkD,EAAApE,EAAAkB,QAEA,CAAA,KAAAlB,YAAAsE,OAGA,KAAA,IAAAjD,OAAA,oDAFA+C,GAAApE,IAtTA,GAAAK,MAGA0B,KAEA+B,EAAA,eACAhC,EAAA,6BAwTA,QACA5C,WAAA4B,EACAzB,eAAA8B,EACA5B,gBAAAiD,EACA/C,qBAAAiE,EACAhE,yBAAA8D,EACA3D,SAAAoD,EACAlD,kBAAAoE,MAeAlF,EAAAsF,sBAcAtF,EAAAuF,gBAcAvF,EAAAwF,UAIAxF,EAAA,WAAAA,EAAAC,WACAD,EAAA,eAAAA,EAAAI,eACAJ,EAAA,gBAAAA,EAAAM,gBACAN,EAAA,qBACAA,EAAAQ,qBACAR,EAAA,yBACAA,EAAAS,yBACAT,EAAA,SAAAA,EAAAY,SACAZ,EAAA,kBAAAA,EAAAc,kBACA2E,OAAAzF,iBAAAA,EACAyF,OAAA,iBAAAzF,EAEAyF,OAAA7G,iBAAA,OAAA,WAQA,aAAAP,UAAAC,cAAA,QACA,iBAAAD,WACA,oBAAAoH,SAAAjC,MAAAG,UAAApB,SACAlE,SAAAqH,gBAAA1H,UAAAO,IAAA,UACAyB,EAAAQ,yBAKAR,EAAAI,eAAA,aAIAJ,EAAAY,SAAA,gBGteA+E,KAAAC,MAKAD,KAAAC,IAAA,WACA,OAAA,GAAAD,OAAAE,WAEAF,KAAA,IAAAA,KAAAC,IAMA,KAAA,GAJAE,IACA,SACA,OAEA3E,EAAA,EAAAA,EAAA2E,EAAAzE,SAAAoE,OAAAM,wBAAA5E,EAAA,CACA,GAAA6E,GAAAF,EAAA3E,EACAsE,QAAAM,sBAAAN,OAAAO,EAAA,yBACAP,OAAAQ,qBAAAR,OAAAO,EAAA,yBAAAP,OAAAO,EAAA,+BACAP,OAAA,sBAAAA,OAAAM,sBACAN,OAAA,qBAAAA,OAAAQ,qBAEA,GAAA,uBAAAC,KAAAT,OAAAU,UAAAC,aAAAX,OAAAM,wBAAAN,OAAAQ,qBAAA,CACA,GAAAI,GAAA,CAKAZ,QAAAM,sBAAA,SAAApF,GACA,GAAAiF,GAAAD,KAAAC,MACAU,EAAAC,KAAAC,IAAAH,EAAA,GAAAT,EACA,OAAAa,YAAA,WACA9F,EAAA0F,EAAAC,IACAA,EAAAV,IAEAH,OAAAQ,qBAAAS,aC5CAjB,OAAA,sBAAAA,OAAAM,sBACAN,OAAA,qBAAAA,OAAAQ,qBAyBA,GAAAU,GAAA,SAAAtG,GACAuG,KAAA7I,SAAAsC,EAEAuG,KAAAC,OAEApB,QAAA,eAAAkB,EAOAA,EAAAhD,UAAAmD,aASAH,EAAAhD,UAAAzF,aACA6I,cAAA,uBACAnH,iBAAA,+BACAC,OAAA,cAQA8G,EAAAhD,UAAAqD,aAAA,SAAAC,GACAA,GACAL,KAAA7I,SAAAmJ,QASAP,EAAAhD,UAAAwD,QAAA,WACAP,KAAA7I,SAAAqJ,UAAA,GAEAT,EAAAhD,UAAA,QAAAgD,EAAAhD,UAAAwD,QAMAR,EAAAhD,UAAA0D,OAAA,WACAT,KAAA7I,SAAAqJ,UAAA,GAEAT,EAAAhD,UAAA,OAAAgD,EAAAhD,UAAA0D,OAIAV,EAAAhD,UAAAkD,KAAA,WACA,GAAAD,KAAA7I,SAAA,CACA,GAAA6I,KAAA7I,SAAAC,UAAAC,SAAA2I,KAAA1I,YAAA6I,eAAA,CACA,GAAA3I,GAAAC,SAAAC,cAAA,OACAF,GAAAJ,UAAAO,IAAAqI,KAAA1I,YAAA0B,kBACAgH,KAAAU,eAAAjJ,SAAAC,cAAA,QACAsI,KAAAU,eAAAtJ,UAAAO,IAAAqI,KAAA1I,YAAA2B,QACAzB,EAAAO,YAAAiI,KAAAU,gBACAV,KAAAW,uBAAAX,KAAAI,aAAAQ,KAAAZ,MACAA,KAAAU,eAAA1I,iBAAA,UAAAgI,KAAAW,wBACAX,KAAA7I,SAAAY,YAAAP,GAEAwI,KAAAa,uBAAAb,KAAAI,aAAAQ,KAAAZ,MACAA,KAAA7I,SAAAa,iBAAA,UAAAgI,KAAAa,wBACAb,KAAA7I,SAAAa,iBAAA,aAAAgI,KAAAa,0BAQAd,EAAAhD,UAAA+D,cAAA,WACAd,KAAAU,gBACAV,KAAAU,eAAAK,oBAAA,UAAAf,KAAAW,wBAEAX,KAAA7I,SAAA4J,oBAAA,UAAAf,KAAAa,wBACAb,KAAA7I,SAAA4J,oBAAA,aAAAf,KAAAa,yBAOAd,EAAAhD,UAAAiE,aAAAjB,EAAAhD,UAAA+D,cACAf,EAAAhD,UAAA,aAAAgD,EAAAhD,UAAAiE,aAGA5H,EAAAY,UACAuD,YAAAwC,EACAvC,cAAA,iBC7HAtC,SAAA,gBACAoB,QAAA,GA0BA,IAAA2E,GAAA,SAAAxH,GACAuG,KAAA7I,SAAAsC,EAEAuG,KAAAC,OAEApB,QAAA,iBAAAoC,EAOAA,EAAAlE,UAAAmD,WAAAgB,aAAA,MASAD,EAAAlE,UAAAzF,aACA6J,MAAA,sBACAC,YAAA,4BACAC,aAAA,6BACAC,aAAA,6BACAnB,cAAA,uBACAoB,qBAAA,sCACAvI,iBAAA,iCACAwI,cAAA,qBACAvI,OAAA,aACAwI,WAAA,aACAC,YAAA,cACAC,WAAA,aACAC,YAAA,eAQAX,EAAAlE,UAAA8E,UAAA,SAAAxB,GACAL,KAAA8B,kBAQAb,EAAAlE,UAAAgF,SAAA,SAAA1B,GACAL,KAAA7I,SAAAC,UAAAO,IAAAqI,KAAA1I,YAAAmK,aAQAR,EAAAlE,UAAAiF,QAAA,SAAA3B,GACAL,KAAA7I,SAAAC,UAAA6K,OAAAjC,KAAA1I,YAAAmK,aAQAR,EAAAlE,UAAAmF,WAAA,SAAA7B,GACAL,KAAAmC,SAOAlB,EAAAlE,UAAA+E,eAAA,WACA9B,KAAAoC,gBACApC,KAAAqC,oBAOApB,EAAAlE,UAAAoF,MAAA,WAGAtD,OAAAgB,WAAA,WACAG,KAAAsC,cAAAhC,QACAM,KAAAZ,MAAAA,KAAAE,UAAAgB,eAQAD,EAAAlE,UAAAsF,iBAAA,WACArC,KAAAsC,cAAAC,QACAvC,KAAA7I,SAAAC,UAAAO,IAAAqI,KAAA1I,YAAAqK,YAEA3B,KAAA7I,SAAAC,UAAA6K,OAAAjC,KAAA1I,YAAAqK,aAGAV,EAAAlE,UAAA,iBAAAkE,EAAAlE,UAAAsF,iBAMApB,EAAAlE,UAAAqF,cAAA,WACApC,KAAAsC,cAAA9B,SACAR,KAAA7I,SAAAC,UAAAO,IAAAqI,KAAA1I,YAAAoK,aAEA1B,KAAA7I,SAAAC,UAAA6K,OAAAjC,KAAA1I,YAAAoK,cAGAT,EAAAlE,UAAA,cAAAkE,EAAAlE,UAAAqF,cAMAnB,EAAAlE,UAAAwD,QAAA,WACAP,KAAAsC,cAAA9B,UAAA,EACAR,KAAA8B,kBAEAb,EAAAlE,UAAA,QAAAkE,EAAAlE,UAAAwD,QAMAU,EAAAlE,UAAA0D,OAAA,WACAT,KAAAsC,cAAA9B,UAAA,EACAR,KAAA8B,kBAEAb,EAAAlE,UAAA,OAAAkE,EAAAlE,UAAA0D,OAMAQ,EAAAlE,UAAAyF,MAAA,WACAxC,KAAAsC,cAAAC,SAAA,EACAvC,KAAA8B,kBAEAb,EAAAlE,UAAA,MAAAkE,EAAAlE,UAAAyF,MAMAvB,EAAAlE,UAAA0F,QAAA,WACAzC,KAAAsC,cAAAC,SAAA,EACAvC,KAAA8B,kBAEAb,EAAAlE,UAAA,QAAAkE,EAAAlE,UAAA0F,QAIAxB,EAAAlE,UAAAkD,KAAA,WACA,GAAAD,KAAA7I,SAAA,CACA6I,KAAAsC,cAAAtC,KAAA7I,SAAAmB,cAAA,IAAA0H,KAAA1I,YAAA6J,MACA,IAAAuB,GAAAjL,SAAAC,cAAA,OACAgL,GAAAtL,UAAAO,IAAAqI,KAAA1I,YAAA8J,YACA,IAAAuB,GAAAlL,SAAAC,cAAA,OACAiL,GAAAvL,UAAAO,IAAAqI,KAAA1I,YAAA+J,aACA,IAAAuB,GAAAnL,SAAAC,cAAA,OAKA,IAJAkL,EAAAxL,UAAAO,IAAAqI,KAAA1I,YAAAgK,cACAoB,EAAA3K,YAAA6K,GACA5C,KAAA7I,SAAAY,YAAA4K,GACA3C,KAAA7I,SAAAY,YAAA2K,GACA1C,KAAA7I,SAAAC,UAAAC,SAAA2I,KAAA1I,YAAA6I,eAAA,CACAH,KAAA7I,SAAAC,UAAAO,IAAAqI,KAAA1I,YAAAiK,sBACAvB,KAAA6C,wBAAApL,SAAAC,cAAA,QACAsI,KAAA6C,wBAAAzL,UAAAO,IAAAqI,KAAA1I,YAAA0B,kBACAgH,KAAA6C,wBAAAzL,UAAAO,IAAAqI,KAAA1I,YAAA6I,eACAH,KAAA6C,wBAAAzL,UAAAO,IAAAqI,KAAA1I,YAAAkK,eACAxB,KAAA8C,mBAAA9C,KAAAkC,WAAAtB,KAAAZ,MACAA,KAAA6C,wBAAA7K,iBAAA,UAAAgI,KAAA8C,mBACA,IAAAjL,GAAAJ,SAAAC,cAAA,OACAG,GAAAT,UAAAO,IAAAqI,KAAA1I,YAAA2B,QACA+G,KAAA6C,wBAAA9K,YAAAF,GACAmI,KAAA7I,SAAAY,YAAAiI,KAAA6C,yBAEA7C,KAAA+C,mBAAA/C,KAAA6B,UAAAjB,KAAAZ,MACAA,KAAAgD,kBAAAhD,KAAA+B,SAAAnB,KAAAZ,MACAA,KAAAiD,iBAAAjD,KAAAgC,QAAApB,KAAAZ,MACAA,KAAAkD,oBAAAlD,KAAAkC,WAAAtB,KAAAZ,MACAA,KAAAsC,cAAAtK,iBAAA,SAAAgI,KAAA+C,oBACA/C,KAAAsC,cAAAtK,iBAAA,QAAAgI,KAAAgD,mBACAhD,KAAAsC,cAAAtK,iBAAA,OAAAgI,KAAAiD,kBACAjD,KAAA7I,SAAAa,iBAAA,UAAAgI,KAAAkD,qBACAlD,KAAA8B,iBACA9B,KAAA7I,SAAAC,UAAAO,IAAAqI,KAAA1I,YAAAsK,eAQAX,EAAAlE,UAAA+D,cAAA,WACAd,KAAA6C,yBACA7C,KAAA6C,wBAAA9B,oBAAA,UAAAf,KAAA8C,oBAEA9C,KAAAsC,cAAAvB,oBAAA,SAAAf,KAAA+C,oBACA/C,KAAAsC,cAAAvB,oBAAA,QAAAf,KAAAgD,mBACAhD,KAAAsC,cAAAvB,oBAAA,OAAAf,KAAAiD,kBACAjD,KAAA7I,SAAA4J,oBAAA,UAAAf,KAAAkD,sBAOAjC,EAAAlE,UAAAiE,aAAAC,EAAAlE,UAAA+D,cACAG,EAAAlE,UAAA,aAAAkE,EAAAlE,UAAAiE,aAGA5H,EAAAY,UACAuD,YAAA0D,EACAzD,cAAA,mBC5PAtC,SAAA,kBACAoB,QAAA,GA0BA,IAAA6G,GAAA,SAAA1J,GACAuG,KAAA7I,SAAAsC,EAEAuG,KAAAC,OAEApB,QAAA,mBAAAsE,EAOAA,EAAApG,UAAAmD,WAAAgB,aAAA,MASAiC,EAAApG,UAAAzF,aACA6J,MAAA,yBACApI,iBAAA,uBACAwI,qBAAA,sCACAvI,iBAAA,oCACAwI,cAAA,qBACAvI,OAAA,aACAwI,WAAA,aACAC,YAAA,cACAC,WAAA,cAQAwB,EAAApG,UAAA8E,UAAA,SAAAxB,GACAL,KAAA8B,kBAQAqB,EAAApG,UAAAgF,SAAA,SAAA1B,GACAL,KAAA7I,SAAAC,UAAAO,IAAAqI,KAAA1I,YAAAmK,aAQA0B,EAAApG,UAAAiF,QAAA,SAAA3B,GACAL,KAAA7I,SAAAC,UAAA6K,OAAAjC,KAAA1I,YAAAmK,aAQA0B,EAAApG,UAAAmF,WAAA,SAAA7B,GACAL,KAAAmC,SAOAgB,EAAApG,UAAA+E,eAAA,WACA9B,KAAAoC,gBACApC,KAAAqC,oBAOAc,EAAApG,UAAAoF,MAAA,WAGAtD,OAAAgB,WAAA,WACAG,KAAAsC,cAAAhC,QACAM,KAAAZ,MAAAA,KAAAE,UAAAgB,eAQAiC,EAAApG,UAAAsF,iBAAA,WACArC,KAAAsC,cAAAC,QACAvC,KAAA7I,SAAAC,UAAAO,IAAAqI,KAAA1I,YAAAqK,YAEA3B,KAAA7I,SAAAC,UAAA6K,OAAAjC,KAAA1I,YAAAqK,aAGAwB,EAAApG,UAAA,iBAAAoG,EAAApG,UAAAsF,iBAMAc,EAAApG,UAAAqF,cAAA,WACApC,KAAAsC,cAAA9B,SACAR,KAAA7I,SAAAC,UAAAO,IAAAqI,KAAA1I,YAAAoK,aAEA1B,KAAA7I,SAAAC,UAAA6K,OAAAjC,KAAA1I,YAAAoK,cAGAyB,EAAApG,UAAA,cAAAoG,EAAApG,UAAAqF,cAMAe,EAAApG,UAAAwD,QAAA,WACAP,KAAAsC,cAAA9B,UAAA,EACAR,KAAA8B,kBAEAqB,EAAApG,UAAA,QAAAoG,EAAApG,UAAAwD,QAMA4C,EAAApG,UAAA0D,OAAA,WACAT,KAAAsC,cAAA9B,UAAA,EACAR,KAAA8B,kBAEAqB,EAAApG,UAAA,OAAAoG,EAAApG,UAAA0D,OAMA0C,EAAApG,UAAAyF,MAAA,WACAxC,KAAAsC,cAAAC,SAAA,EACAvC,KAAA8B,kBAEAqB,EAAApG,UAAA,MAAAoG,EAAApG,UAAAyF,MAMAW,EAAApG,UAAA0F,QAAA,WACAzC,KAAAsC,cAAAC,SAAA,EACAvC,KAAA8B,kBAEAqB,EAAApG,UAAA,QAAAoG,EAAApG,UAAA0F,QAIAU,EAAApG,UAAAkD,KAAA,WACA,GAAAD,KAAA7I,SAAA,CAEA,GADA6I,KAAAsC,cAAAtC,KAAA7I,SAAAmB,cAAA,IAAA0H,KAAA1I,YAAA6J,OACAnB,KAAA7I,SAAAC,UAAAC,SAAA2I,KAAA1I,YAAAyB,kBAAA,CACAiH,KAAA7I,SAAAC,UAAAO,IAAAqI,KAAA1I,YAAAiK,sBACAvB,KAAA6C,wBAAApL,SAAAC,cAAA,QACAsI,KAAA6C,wBAAAzL,UAAAO,IAAAqI,KAAA1I,YAAA0B,kBACAgH,KAAA6C,wBAAAzL,UAAAO,IAAAqI,KAAA1I,YAAAyB,kBACAiH,KAAA6C,wBAAAzL,UAAAO,IAAAqI,KAAA1I,YAAAkK,eACAxB,KAAA8C,mBAAA9C,KAAAkC,WAAAtB,KAAAZ,MACAA,KAAA6C,wBAAA7K,iBAAA,UAAAgI,KAAA8C,mBACA,IAAAjL,GAAAJ,SAAAC,cAAA,OACAG,GAAAT,UAAAO,IAAAqI,KAAA1I,YAAA2B,QACA+G,KAAA6C,wBAAA9K,YAAAF,GACAmI,KAAA7I,SAAAY,YAAAiI,KAAA6C,yBAEA7C,KAAA+C,mBAAA/C,KAAA6B,UAAAjB,KAAAZ,MACAA,KAAAgD,kBAAAhD,KAAA+B,SAAAnB,KAAAZ,MACAA,KAAAiD,iBAAAjD,KAAAgC,QAAApB,KAAAZ,MACAA,KAAAoD,sBAAApD,KAAAkC,WAAAtB,KAAAZ,MACAA,KAAAsC,cAAAtK,iBAAA,SAAAgI,KAAA+C,oBACA/C,KAAAsC,cAAAtK,iBAAA,QAAAgI,KAAAgD,mBACAhD,KAAAsC,cAAAtK,iBAAA,OAAAgI,KAAAiD,kBACAjD,KAAA7I,SAAAa,iBAAA,UAAAgI,KAAAoD,uBACApD,KAAA8B,iBACA9B,KAAA7I,SAAAC,UAAAO,IAAA,iBAQAwL,EAAApG,UAAA+D,cAAA,WACAd,KAAA6C,yBACA7C,KAAA6C,wBAAA9B,oBAAA,UAAAf,KAAA8C,oBAEA9C,KAAAsC,cAAAvB,oBAAA,SAAAf,KAAA+C,oBACA/C,KAAAsC,cAAAvB,oBAAA,QAAAf,KAAAgD,mBACAhD,KAAAsC,cAAAvB,oBAAA,OAAAf,KAAAiD,kBACAjD,KAAA7I,SAAA4J,oBAAA,UAAAf,KAAAoD,wBAOAD,EAAApG,UAAAiE,aAAAmC,EAAApG,UAAA+D,cACAqC,EAAApG,UAAA,aAAAoG,EAAApG,UAAAiE,aAGA5H,EAAAY,UACAuD,YAAA4F,EACA3F,cAAA,qBC/OAtC,SAAA,qBACAoB,QAAA,GA0BA,IAAA+G,GAAA,SAAA5J,GACAuG,KAAA7I,SAAAsC,EAEAuG,KAAAC,OAEApB,QAAA,aAAAwE,EAOAA,EAAAtG,UAAAmD,WAEAoD,4BAAA,GAEAC,6BAAA,GAGAC,cAAA,KAQAH,EAAAtG,UAAA0G,WACAC,MAAA,GACAC,OAAA,GACAC,MAAA,GACAC,SAAA,GACAC,WAAA,IAUAT,EAAAtG,UAAAzF,aACAyM,UAAA,sBACAC,QAAA,oBACAC,KAAA,iBACAC,sBAAA,kCACA/D,cAAA,uBACAoB,qBAAA,sCACAtI,OAAA,aAEA2I,YAAA,cACAuC,WAAA,aACAC,aAAA,eAEAC,YAAA,wBAEAC,aAAA,yBACAC,SAAA,qBACAC,UAAA,sBACAC,UAAA,uBAKApB,EAAAtG,UAAAkD,KAAA,WACA,GAAAD,KAAA7I,SAAA,CAEA,GAAAuN,GAAAjN,SAAAC,cAAA,MACAgN,GAAAtN,UAAAO,IAAAqI,KAAA1I,YAAAyM,WACA/D,KAAA7I,SAAAwN,cAAAC,aAAAF,EAAA1E,KAAA7I,UACA6I,KAAA7I,SAAAwN,cAAAE,YAAA7E,KAAA7I,UACAuN,EAAA3M,YAAAiI,KAAA7I,UACA6I,KAAA8E,WAAAJ,CAEA,IAAAK,GAAAtN,SAAAC,cAAA,MACAqN,GAAA3N,UAAAO,IAAAqI,KAAA1I,YAAA0M,SACAhE,KAAAgF,SAAAD,EACAL,EAAAE,aAAAG,EAAA/E,KAAA7I,SAEA,IAAA8N,GAAAjF,KAAA7I,SAAA0D,aAAA,OACAqK,EAAA,IACAD,KACAC,EAAAzN,SAAA0N,eAAAF,GACAC,IACAlF,KAAAoF,YAAAF,EACAA,EAAAlN,iBAAA,QAAAgI,KAAAqF,gBAAAzE,KAAAZ,OACAkF,EAAAlN,iBAAA,UAAAgI,KAAAsF,wBAAA1E,KAAAZ,QAGA,IAAAuF,GAAAvF,KAAA7I,SAAAiE,iBAAA,IAAA4E,KAAA1I,YAAA2M,KACAjE,MAAAwF,kBAAAxF,KAAAyF,yBAAA7E,KAAAZ,MACAA,KAAA0F,gBAAA1F,KAAA2F,iBAAA/E,KAAAZ,KACA,KAAA,GAAAzF,GAAA,EAAAA,EAAAgL,EAAA9K,OAAAF,IAEAgL,EAAAhL,GAAAvC,iBAAA,QAAAgI,KAAA0F,iBAEAH,EAAAhL,GAAAqL,SAAA,KAEAL,EAAAhL,GAAAvC,iBAAA,UAAAgI,KAAAwF,kBAGA,IAAAxF,KAAA7I,SAAAC,UAAAC,SAAA2I,KAAA1I,YAAA6I,eAEA,IADAH,KAAA7I,SAAAC,UAAAO,IAAAqI,KAAA1I,YAAAiK,sBACAhH,EAAA,EAAAA,EAAAgL,EAAA9K,OAAAF,IAAA,CACA,GAAAuC,GAAAyI,EAAAhL,GACA/C,EAAAC,SAAAC,cAAA,OACAF,GAAAJ,UAAAO,IAAAqI,KAAA1I,YAAA4M,sBACA,IAAArM,GAAAJ,SAAAC,cAAA,OACAG,GAAAT,UAAAO,IAAAqI,KAAA1I,YAAA2B,QACAzB,EAAAO,YAAAF,GACAiF,EAAA/E,YAAAP,GACAsF,EAAA1F,UAAAO,IAAAqI,KAAA1I,YAAA6I,eAIAH,KAAA7I,SAAAC,UAAAC,SAAA2I,KAAA1I,YAAA+M,cACArE,KAAAgF,SAAA5N,UAAAO,IAAAqI,KAAA1I,YAAA+M,aAEArE,KAAA7I,SAAAC,UAAAC,SAAA2I,KAAA1I,YAAAgN,eACAtE,KAAAgF,SAAA5N,UAAAO,IAAAqI,KAAA1I,YAAAgN,cAEAtE,KAAA7I,SAAAC,UAAAC,SAAA2I,KAAA1I,YAAAiN,WACAvE,KAAAgF,SAAA5N,UAAAO,IAAAqI,KAAA1I,YAAAiN,UAEAvE,KAAA7I,SAAAC,UAAAC,SAAA2I,KAAA1I,YAAAkN,YACAxE,KAAAgF,SAAA5N,UAAAO,IAAAqI,KAAA1I,YAAAkN,WAEAxE,KAAA7I,SAAAC,UAAAC,SAAA2I,KAAA1I,YAAAmN,YACAzE,KAAAgF,SAAA5N,UAAAO,IAAAqI,KAAA1I,YAAAmN,WAEAC,EAAAtN,UAAAO,IAAAqI,KAAA1I,YAAAsK,eAUAyB,EAAAtG,UAAAsI,gBAAA,SAAAQ,GACA,GAAA7F,KAAA7I,UAAA6I,KAAAoF,YAAA,CACA,GAAAU,GAAA9F,KAAAoF,YAAAW,wBACAC,EAAAhG,KAAAoF,YAAAT,cAAAoB,uBACA/F,MAAA7I,SAAAC,UAAAC,SAAA2I,KAAA1I,YAAAmN,aACAzE,KAAA7I,SAAAC,UAAAC,SAAA2I,KAAA1I,YAAAgN,eAEAtE,KAAA8E,WAAAmB,MAAAC,MAAAF,EAAAE,MAAAJ,EAAAI,MAAA,KACAlG,KAAA8E,WAAAmB,MAAAE,IAAAnG,KAAAoF,YAAAgB,UAAApG,KAAAoF,YAAAiB,aAAA,MACArG,KAAA7I,SAAAC,UAAAC,SAAA2I,KAAA1I,YAAAiN,WAEAvE,KAAA8E,WAAAmB,MAAAK,KAAAtG,KAAAoF,YAAAmB,WAAA,KACAvG,KAAA8E,WAAAmB,MAAAO,OAAAR,EAAAQ,OAAAV,EAAAK,IAAA,MACAnG,KAAA7I,SAAAC,UAAAC,SAAA2I,KAAA1I,YAAAkN,YAEAxE,KAAA8E,WAAAmB,MAAAC,MAAAF,EAAAE,MAAAJ,EAAAI,MAAA,KACAlG,KAAA8E,WAAAmB,MAAAO,OAAAR,EAAAQ,OAAAV,EAAAK,IAAA,OAGAnG,KAAA8E,WAAAmB,MAAAK,KAAAtG,KAAAoF,YAAAmB,WAAA,KACAvG,KAAA8E,WAAAmB,MAAAE,IAAAnG,KAAAoF,YAAAgB,UAAApG,KAAAoF,YAAAiB,aAAA,OAGArG,KAAAyG,OAAAZ,IAQAxC,EAAAtG,UAAAuI,wBAAA,SAAAO,GACA,GAAA7F,KAAA7I,UAAA6I,KAAA8E,YAAA9E,KAAAoF,YAAA,CACA,GAAAG,GAAAvF,KAAA7I,SAAAiE,iBAAA,IAAA4E,KAAA1I,YAAA2M,KAAA,mBACAsB,IAAAA,EAAA9K,OAAA,GAAAuF,KAAA8E,WAAA1N,UAAAC,SAAA2I,KAAA1I,YAAA6M,cACA0B,EAAAa,UAAA1G,KAAAyD,UAAAI,UACAgC,EAAA3N,iBACAqN,EAAAA,EAAA9K,OAAA,GAAAkM,SACAd,EAAAa,UAAA1G,KAAAyD,UAAAK,aACA+B,EAAA3N,iBACAqN,EAAA,GAAAoB,YAWAtD,EAAAtG,UAAA0I,yBAAA,SAAAI,GACA,GAAA7F,KAAA7I,UAAA6I,KAAA8E,WAAA,CACA,GAAAS,GAAAvF,KAAA7I,SAAAiE,iBAAA,IAAA4E,KAAA1I,YAAA2M,KAAA,mBACA,IAAAsB,GAAAA,EAAA9K,OAAA,GAAAuF,KAAA8E,WAAA1N,UAAAC,SAAA2I,KAAA1I,YAAA6M,YAAA,CACA,GAAAyC,GAAAhK,MAAAG,UAAAC,MAAAC,KAAAsI,GAAAvK,QAAA6K,EAAAgB,OACA,IAAAhB,EAAAa,UAAA1G,KAAAyD,UAAAI,SACAgC,EAAA3N,iBACA0O,EAAA,EACArB,EAAAqB,EAAA,GAAAD,QAEApB,EAAAA,EAAA9K,OAAA,GAAAkM,YAEA,IAAAd,EAAAa,UAAA1G,KAAAyD,UAAAK,WACA+B,EAAA3N,iBACAqN,EAAA9K,OAAAmM,EAAA,EACArB,EAAAqB,EAAA,GAAAD,QAEApB,EAAA,GAAAoB,YAEA,IAAAd,EAAAa,UAAA1G,KAAAyD,UAAAG,OAAAiC,EAAAa,UAAA1G,KAAAyD,UAAAC,MAAA,CACAmC,EAAA3N,gBAEA,IAAAD,GAAA,GAAA6O,YAAA,YACAjB,GAAAgB,OAAAnK,cAAAzE,GACAA,EAAA,GAAA6O,YAAA,WACAjB,EAAAgB,OAAAnK,cAAAzE,GAEA4N,EAAAgB,OAAAE,YACAlB,GAAAa,UAAA1G,KAAAyD,UAAAE,SACAkC,EAAA3N,iBACA8H,KAAAgH,WAWA3D,EAAAtG,UAAA4I,iBAAA,SAAAE,GACAA,EAAAgB,OAAAI,aAAA,YACApB,EAAAqB,mBAGAlH,KAAAmH,UAAA,EACAtI,OAAAgB,WAAA,SAAAgG,GACA7F,KAAAgH,OACAhH,KAAAmH,UAAA,GACAvG,KAAAZ,MAAAA,KAAAE,UAAAsD,iBAYAH,EAAAtG,UAAAqK,WAAA,SAAAC,EAAAC,GACAtH,KAAA7I,SAAAC,UAAAC,SAAA2I,KAAA1I,YAAAmN,WAEAzE,KAAA7I,SAAA8O,MAAAsB,KAAA,GACAvH,KAAA7I,SAAAC,UAAAC,SAAA2I,KAAA1I,YAAAgN,cAEAtE,KAAA7I,SAAA8O,MAAAsB,KAAA,UAAAD,EAAA,QAAAA,EAAA,MACAtH,KAAA7I,SAAAC,UAAAC,SAAA2I,KAAA1I,YAAAiN,UAEAvE,KAAA7I,SAAA8O,MAAAsB,KAAA,QAAAF,EAAA,QAAAA,EAAA,QACArH,KAAA7I,SAAAC,UAAAC,SAAA2I,KAAA1I,YAAAkN,WAEAxE,KAAA7I,SAAA8O,MAAAsB,KAAA,QAAAF,EAAA,MAAAC,EAAA,MAAAD,EAAA,MAAAC,EAAA,MAGAtH,KAAA7I,SAAA8O,MAAAsB,KAAA,IAQAlE,EAAAtG,UAAAyK,yBAAA,WACA,GAAAC,GAAA,WACAzH,KAAA7I,SAAA4J,oBAAA,gBAAA0G,GACAzH,KAAA7I,SAAA4J,oBAAA,sBAAA0G,GACAzH,KAAA7I,SAAAC,UAAA6K,OAAAjC,KAAA1I,YAAA8M,eACAxD,KAAAZ,KAEAA,MAAA7I,SAAAa,iBAAA,gBAAAyP,GACAzH,KAAA7I,SAAAa,iBAAA,sBAAAyP,IAOApE,EAAAtG,UAAA2K,KAAA,SAAA7B,GACA,GAAA7F,KAAA7I,UAAA6I,KAAA8E,YAAA9E,KAAAgF,SAAA,CAEA,GAAAqC,GAAArH,KAAA7I,SAAA4O,wBAAAsB,OACAC,EAAAtH,KAAA7I,SAAA4O,wBAAAuB,KAEAtH,MAAA8E,WAAAmB,MAAAqB,MAAAA,EAAA,KACAtH,KAAA8E,WAAAmB,MAAAoB,OAAAA,EAAA,KACArH,KAAAgF,SAAAiB,MAAAqB,MAAAA,EAAA,KACAtH,KAAAgF,SAAAiB,MAAAoB,OAAAA,EAAA,IAKA,KAAA,GAJAM,GAAA3H,KAAAE,UAAAoD,4BAAAtD,KAAAE,UAAAqD,6BAGAgC,EAAAvF,KAAA7I,SAAAiE,iBAAA,IAAA4E,KAAA1I,YAAA2M,MACA1J,EAAA,EAAAA,EAAAgL,EAAA9K,OAAAF,IAAA,CACA,GAAAqN,GAAA,IAEAA,GADA5H,KAAA7I,SAAAC,UAAAC,SAAA2I,KAAA1I,YAAAiN,WAAAvE,KAAA7I,SAAAC,UAAAC,SAAA2I,KAAA1I,YAAAkN,YACA6C,EAAA9B,EAAAhL,GAAA6L,UAAAb,EAAAhL,GAAA8L,cAAAgB,EAAAM,EAAA,IAEApC,EAAAhL,GAAA6L,UAAAiB,EAAAM,EAAA,IAEApC,EAAAhL,GAAA0L,MAAA4B,gBAAAD,EAGA5H,KAAAoH,WAAAC,EAAAC,GAGAzI,OAAAM,sBAAA,WACAa,KAAA7I,SAAAC,UAAAO,IAAAqI,KAAA1I,YAAA8M,cACApE,KAAA7I,SAAA8O,MAAAsB,KAAA,UAAAD,EAAA,MAAAD,EAAA,QACArH,KAAA8E,WAAA1N,UAAAO,IAAAqI,KAAA1I,YAAA6M,aACAvD,KAAAZ,OAEAA,KAAAwH,0BAEA,IAAAzN,GAAA,SAAA9B,GAOAA,IAAA4N,GAAA7F,KAAAmH,UAAAlP,EAAA4O,OAAAiB,aAAA9H,KAAA7I,WACAM,SAAAsJ,oBAAA,QAAAhH,GACAiG,KAAAgH,SAEApG,KAAAZ,KACAvI,UAAAO,iBAAA,QAAA+B,KAGAsJ,EAAAtG,UAAA,KAAAsG,EAAAtG,UAAA2K,KAMArE,EAAAtG,UAAAiK,KAAA,WACA,GAAAhH,KAAA7I,UAAA6I,KAAA8E,YAAA9E,KAAAgF,SAAA,CAGA,IAAA,GAFAO,GAAAvF,KAAA7I,SAAAiE,iBAAA,IAAA4E,KAAA1I,YAAA2M,MAEA1J,EAAA,EAAAA,EAAAgL,EAAA9K,OAAAF,IACAgL,EAAAhL,GAAA0L,MAAA4B,gBAAA,IAGA,IAAA/B,GAAA9F,KAAA7I,SAAA4O,wBACAsB,EAAAvB,EAAAuB,OACAC,EAAAxB,EAAAwB,KAGAtH,MAAA7I,SAAAC,UAAAO,IAAAqI,KAAA1I,YAAA8M,cACApE,KAAAoH,WAAAC,EAAAC,GACAtH,KAAA8E,WAAA1N,UAAA6K,OAAAjC,KAAA1I,YAAA6M,YAEAnE,KAAAwH,6BAGAnE,EAAAtG,UAAA,KAAAsG,EAAAtG,UAAAiK,KAMA3D,EAAAtG,UAAA0J,OAAA,SAAAZ,GACA7F,KAAA8E,WAAA1N,UAAAC,SAAA2I,KAAA1I,YAAA6M,YACAnE,KAAAgH,OAEAhH,KAAA0H,KAAA7B,IAGAxC,EAAAtG,UAAA,OAAAsG,EAAAtG,UAAA0J,OAMApD,EAAAtG,UAAA+D,cAAA,WAEA,IAAA,GADAyE,GAAAvF,KAAA7I,SAAAiE,iBAAA,IAAA4E,KAAA1I,YAAA2M,MACA1J,EAAA,EAAAA,EAAAgL,EAAA9K,OAAAF,IACAgL,EAAAhL,GAAAwG,oBAAA,QAAAf,KAAA0F,iBACAH,EAAAhL,GAAAwG,oBAAA,UAAAf,KAAAwF,oBAQAnC,EAAAtG,UAAAiE,aAAAqC,EAAAtG,UAAA+D,cACAuC,EAAAtG,UAAA,aAAAsG,EAAAtG,UAAAiE,aAGA5H,EAAAY,UACAuD,YAAA8F,EACA7F,cAAA,eChbAtC,SAAA,cACAoB,QAAA,GA0BA,IAAAyL,GAAA,SAAAtO,GACAuG,KAAA7I,SAAAsC,EAEAuG,KAAAC,OAEApB,QAAA,iBAAAkJ,EAOAA,EAAAhL,UAAAmD,aASA6H,EAAAhL,UAAAzF,aAAA0Q,oBAAA,+BAOAD,EAAAhL,UAAAkL,YAAA,SAAAC,GACAlI,KAAA7I,SAAAC,UAAAC,SAAA2I,KAAA1I,YAAA0Q,uBAGAhI,KAAAmI,aAAAlC,MAAAqB,MAAAY,EAAA,MAEAH,EAAAhL,UAAA,YAAAgL,EAAAhL,UAAAkL,YAOAF,EAAAhL,UAAAqL,UAAA,SAAAF,GACAlI,KAAAqI,WAAApC,MAAAqB,MAAAY,EAAA,IACAlI,KAAAsI,QAAArC,MAAAqB,MAAA,IAAAY,EAAA,KAEAH,EAAAhL,UAAA,UAAAgL,EAAAhL,UAAAqL,UAIAL,EAAAhL,UAAAkD,KAAA,WACA,GAAAD,KAAA7I,SAAA,CACA,GAAAoR,GAAA9Q,SAAAC,cAAA,MACA6Q,GAAA7N,UAAA,uBACAsF,KAAA7I,SAAAY,YAAAwQ,GACAvI,KAAAmI,aAAAI,EACAA,EAAA9Q,SAAAC,cAAA,OACA6Q,EAAA7N,UAAA,qBACAsF,KAAA7I,SAAAY,YAAAwQ,GACAvI,KAAAqI,WAAAE,EACAA,EAAA9Q,SAAAC,cAAA,OACA6Q,EAAA7N,UAAA,kBACAsF,KAAA7I,SAAAY,YAAAwQ,GACAvI,KAAAsI,QAAAC,EACAvI,KAAAmI,aAAAlC,MAAAqB,MAAA,KACAtH,KAAAqI,WAAApC,MAAAqB,MAAA,OACAtH,KAAAsI,QAAArC,MAAAqB,MAAA,KACAtH,KAAA7I,SAAAC,UAAAO,IAAA,iBAQAoQ,EAAAhL,UAAA+D,cAAA,WACA,KAAAd,KAAA7I,SAAAqR,YACAxI,KAAA7I,SAAA0N,YAAA7E,KAAA7I,SAAAqR,aAQAT,EAAAhL,UAAAiE,aAAA+G,EAAAhL,UAAA+D,cACAiH,EAAAhL,UAAA,aAAAgL,EAAAhL,UAAAiE,aAGA5H,EAAAY,UACAuD,YAAAwK,EACAvK,cAAA,mBCrHAtC,SAAA,kBACAoB,QAAA,GA0BA,IAAAmM,GAAA,SAAAhP,GACAuG,KAAA7I,SAAAsC,EAEAuG,KAAAC,OAEApB,QAAA,cAAA4J,EAOAA,EAAA1L,UAAAmD,WAAAgB,aAAA,MASAuH,EAAA1L,UAAAzF,aACAmK,WAAA,aACAC,YAAA,cACAC,WAAA,aACAC,YAAA,cACA8G,SAAA,eACAC,UAAA,oBACAC,mBAAA,0BACAC,mBAAA,0BACA1I,cAAA,uBACAoB,qBAAA,sCACAvI,iBAAA,8BACAwI,cAAA,qBACAvI,OAAA,cAQAwP,EAAA1L,UAAA8E,UAAA,SAAAxB,GAIA,IAAA,GADAyI,GAAArR,SAAAsR,uBAAA/I,KAAA1I,YAAAoR,UACAnO,EAAA,EAAAA,EAAAuO,EAAArO,OAAAF,IAAA,CACA,GAAAyO,GAAAF,EAAAvO,GAAAjC,cAAA,IAAA0H,KAAA1I,YAAAqR,UAEAK,GAAAnO,aAAA,UAAAmF,KAAAiJ,YAAApO,aAAA,SACAiO,EAAAvO,GAAA,cAAAuH,mBAUA2G,EAAA1L,UAAAgF,SAAA,SAAA1B,GACAL,KAAA7I,SAAAC,UAAAO,IAAAqI,KAAA1I,YAAAmK,aAQAgH,EAAA1L,UAAAiF,QAAA,SAAA3B,GACAL,KAAA7I,SAAAC,UAAA6K,OAAAjC,KAAA1I,YAAAmK,aAQAgH,EAAA1L,UAAAmM,WAAA,SAAA7I,GACAL,KAAAmC,SAOAsG,EAAA1L,UAAA+E,eAAA,WACA9B,KAAAoC,gBACApC,KAAAqC,oBAOAoG,EAAA1L,UAAAoF,MAAA,WAGAtD,OAAAgB,WAAA,WACAG,KAAAiJ,YAAA3I,QACAM,KAAAZ,MAAAA,KAAAE,UAAAgB,eAQAuH,EAAA1L,UAAAqF,cAAA,WACApC,KAAAiJ,YAAAzI,SACAR,KAAA7I,SAAAC,UAAAO,IAAAqI,KAAA1I,YAAAoK,aAEA1B,KAAA7I,SAAAC,UAAA6K,OAAAjC,KAAA1I,YAAAoK,cAGA+G,EAAA1L,UAAA,cAAA0L,EAAA1L,UAAAqF,cAMAqG,EAAA1L,UAAAsF,iBAAA,WACArC,KAAAiJ,YAAA1G,QACAvC,KAAA7I,SAAAC,UAAAO,IAAAqI,KAAA1I,YAAAqK,YAEA3B,KAAA7I,SAAAC,UAAA6K,OAAAjC,KAAA1I,YAAAqK,aAGA8G,EAAA1L,UAAA,iBAAA0L,EAAA1L,UAAAsF,iBAMAoG,EAAA1L,UAAAwD,QAAA,WACAP,KAAAiJ,YAAAzI,UAAA,EACAR,KAAA8B,kBAEA2G,EAAA1L,UAAA,QAAA0L,EAAA1L,UAAAwD,QAMAkI,EAAA1L,UAAA0D,OAAA,WACAT,KAAAiJ,YAAAzI,UAAA,EACAR,KAAA8B,kBAEA2G,EAAA1L,UAAA,OAAA0L,EAAA1L,UAAA0D,OAMAgI,EAAA1L,UAAAyF,MAAA,WACAxC,KAAAiJ,YAAA1G,SAAA,EACAvC,KAAA8B,kBAEA2G,EAAA1L,UAAA,MAAA0L,EAAA1L,UAAAyF,MAMAiG,EAAA1L,UAAA0F,QAAA,WACAzC,KAAAiJ,YAAA1G,SAAA,EACAvC,KAAA8B,kBAEA2G,EAAA1L,UAAA,QAAA0L,EAAA1L,UAAA0F,QAIAgG,EAAA1L,UAAAkD,KAAA,WACA,GAAAD,KAAA7I,SAAA,CACA6I,KAAAiJ,YAAAjJ,KAAA7I,SAAAmB,cAAA,IAAA0H,KAAA1I,YAAAqR,WACA3I,KAAAmJ,oBAAAnJ,KAAA6B,UAAAjB,KAAAZ,MACAA,KAAAoJ,mBAAApJ,KAAA6B,UAAAjB,KAAAZ,MACAA,KAAAqJ,kBAAArJ,KAAAgC,QAAApB,KAAAZ,MACAA,KAAAsJ,qBAAAtJ,KAAAkJ,WAAAtI,KAAAZ,KACA,IAAAuJ,GAAA9R,SAAAC,cAAA,OACA6R,GAAAnS,UAAAO,IAAAqI,KAAA1I,YAAAsR,mBACA,IAAAY,GAAA/R,SAAAC,cAAA,OACA8R,GAAApS,UAAAO,IAAAqI,KAAA1I,YAAAuR,oBACA7I,KAAA7I,SAAAY,YAAAwR,GACAvJ,KAAA7I,SAAAY,YAAAyR,EACA,IAAAhS,EACA,IAAAwI,KAAA7I,SAAAC,UAAAC,SAAA2I,KAAA1I,YAAA6I,eAAA,CACAH,KAAA7I,SAAAC,UAAAO,IAAAqI,KAAA1I,YAAAiK,sBACA/J,EAAAC,SAAAC,cAAA,QACAF,EAAAJ,UAAAO,IAAAqI,KAAA1I,YAAA0B,kBACAxB,EAAAJ,UAAAO,IAAAqI,KAAA1I,YAAA6I,eACA3I,EAAAJ,UAAAO,IAAAqI,KAAA1I,YAAAkK,eACAhK,EAAAQ,iBAAA,UAAAgI,KAAAsJ,qBACA,IAAAzR,GAAAJ,SAAAC,cAAA,OACAG,GAAAT,UAAAO,IAAAqI,KAAA1I,YAAA2B,QACAzB,EAAAO,YAAAF,GACAmI,KAAA7I,SAAAY,YAAAP,GAEAwI,KAAAiJ,YAAAjR,iBAAA,SAAAgI,KAAAmJ,qBACAnJ,KAAAiJ,YAAAjR,iBAAA,QAAAgI,KAAAoJ,oBACApJ,KAAAiJ,YAAAjR,iBAAA,OAAAgI,KAAAqJ,mBACArJ,KAAA7I,SAAAa,iBAAA,UAAAgI,KAAAsJ,sBACAtJ,KAAA8B,iBACA9B,KAAA7I,SAAAC,UAAAO,IAAAqI,KAAA1I,YAAAsK,eAQA6G,EAAA1L,UAAA+D,cAAA,WACA,GAAAtJ,GAAAwI,KAAA7I,SAAAmB,cAAA,IAAA0H,KAAA1I,YAAA0B,iBACAgH,MAAAiJ,YAAAlI,oBAAA,SAAAf,KAAAmJ,qBACAnJ,KAAAiJ,YAAAlI,oBAAA,QAAAf,KAAAoJ,oBACApJ,KAAAiJ,YAAAlI,oBAAA,OAAAf,KAAAqJ,mBACArJ,KAAA7I,SAAA4J,oBAAA,UAAAf,KAAAsJ,sBACA9R,IACAA,EAAAuJ,oBAAA,UAAAf,KAAAsJ,sBACAtJ,KAAA7I,SAAA0N,YAAArN,KAQAiR,EAAA1L,UAAAiE,aAAAyH,EAAA1L,UAAA+D,cACA2H,EAAA1L,UAAA,aAAA0L,EAAA1L,UAAAiE,aAGA5H,EAAAY,UACAuD,YAAAkL,EACAjL,cAAA,gBCpQAtC,SAAA,eACAoB,QAAA,GA0BA,IAAAmN,GAAA,SAAAhQ,GACAuG,KAAA7I,SAAAsC,EAEAuG,KAAA0J,MAAA7K,OAAAU,UAAAoK,iBAEA3J,KAAAC,OAEApB,QAAA,eAAA4K,EAOAA,EAAA1M,UAAAmD,aASAuJ,EAAA1M,UAAAzF,aACAsS,aAAA,2BACAC,iBAAA,wBACAC,gBAAA,8BACAC,iBAAA,+BACAC,iBAAA,+BACAC,gBAAA,kBACArI,YAAA,eAQA6H,EAAA1M,UAAAmN,SAAA,SAAA7J,GACAL,KAAAmK,sBAQAV,EAAA1M,UAAA8E,UAAA,SAAAxB,GACAL,KAAAmK,sBAQAV,EAAA1M,UAAAmF,WAAA,SAAA7B,GACAA,EAAAwG,OAAAvG,QAYAmJ,EAAA1M,UAAAqN,sBAAA,SAAA/J,GAGA,GAAAA,EAAAwG,SAAA7G,KAAA7I,SAAAwN,cAAA,CAKAtE,EAAAnI,gBACA,IAAAmS,GAAA,GAAAvD,YAAA,aACAD,OAAAxG,EAAAwG,OACAyD,QAAAjK,EAAAiK,QACAC,QAAAlK,EAAAkK,QACAC,QAAAxK,KAAA7I,SAAA4O,wBAAA0E,GAEAzK,MAAA7I,SAAAuF,cAAA2N,KAOAZ,EAAA1M,UAAAoN,mBAAA,WAEA,GAAAO,IAAA1K,KAAA7I,SAAAwT,MAAA3K,KAAA7I,SAAAyT,MAAA5K,KAAA7I,SAAAyI,IAAAI,KAAA7I,SAAAyT,IACA,KAAAF,EACA1K,KAAA7I,SAAAC,UAAAO,IAAAqI,KAAA1I,YAAA2S,iBAEAjK,KAAA7I,SAAAC,UAAA6K,OAAAjC,KAAA1I,YAAA2S,iBAEAjK,KAAA0J,QACA1J,KAAA6K,iBAAA5E,MAAA6E,KAAAJ,EACA1K,KAAA6K,iBAAA5E,MAAA8E,WAAAL,EACA1K,KAAAgL,iBAAA/E,MAAA6E,KAAA,EAAAJ,EACA1K,KAAAgL,iBAAA/E,MAAA8E,WAAA,EAAAL,IASAjB,EAAA1M,UAAAwD,QAAA,WACAP,KAAA7I,SAAAqJ,UAAA,GAEAiJ,EAAA1M,UAAA,QAAA0M,EAAA1M,UAAAwD,QAMAkJ,EAAA1M,UAAA0D,OAAA,WACAT,KAAA7I,SAAAqJ,UAAA,GAEAiJ,EAAA1M,UAAA,OAAA0M,EAAA1M,UAAA0D,OAOAgJ,EAAA1M,UAAAkO,OAAA,SAAAN,GACA,mBAAAA,KACA3K,KAAA7I,SAAAwT,MAAAA,GAEA3K,KAAAmK,sBAEAV,EAAA1M,UAAA,OAAA0M,EAAA1M,UAAAkO,OAIAxB,EAAA1M,UAAAkD,KAAA,WACA,GAAAD,KAAA7I,SAAA,CACA,GAAA6I,KAAA0J,MAAA,CAIA,GAAAwB,GAAAzT,SAAAC,cAAA,MACAwT,GAAA9T,UAAAO,IAAAqI,KAAA1I,YAAAsS;AACA5J,KAAA7I,SAAAwN,cAAAC,aAAAsG,EAAAlL,KAAA7I,UACA6I,KAAA7I,SAAAwN,cAAAE,YAAA7E,KAAA7I,UACA+T,EAAAnT,YAAAiI,KAAA7I,cACA,CAIA,GAAAuN,GAAAjN,SAAAC,cAAA,MACAgN,GAAAtN,UAAAO,IAAAqI,KAAA1I,YAAAuS,kBACA7J,KAAA7I,SAAAwN,cAAAC,aAAAF,EAAA1E,KAAA7I,UACA6I,KAAA7I,SAAAwN,cAAAE,YAAA7E,KAAA7I,UACAuN,EAAA3M,YAAAiI,KAAA7I,SACA,IAAAgU,GAAA1T,SAAAC,cAAA,MACAyT,GAAA/T,UAAAO,IAAAqI,KAAA1I,YAAAwS,iBACApF,EAAA3M,YAAAoT,GACAnL,KAAA6K,iBAAApT,SAAAC,cAAA,OACAsI,KAAA6K,iBAAAzT,UAAAO,IAAAqI,KAAA1I,YAAAyS,kBACAoB,EAAApT,YAAAiI,KAAA6K,kBACA7K,KAAAgL,iBAAAvT,SAAAC,cAAA,OACAsI,KAAAgL,iBAAA5T,UAAAO,IAAAqI,KAAA1I,YAAA0S,kBACAmB,EAAApT,YAAAiI,KAAAgL,kBAEAhL,KAAAoL,kBAAApL,KAAAkK,SAAAtJ,KAAAZ,MACAA,KAAAqL,mBAAArL,KAAA6B,UAAAjB,KAAAZ,MACAA,KAAAsL,oBAAAtL,KAAAkC,WAAAtB,KAAAZ,MACAA,KAAAuL,+BAAAvL,KAAAoK,sBAAAxJ,KAAAZ,MACAA,KAAA7I,SAAAa,iBAAA,QAAAgI,KAAAoL,mBACApL,KAAA7I,SAAAa,iBAAA,SAAAgI,KAAAqL,oBACArL,KAAA7I,SAAAa,iBAAA,UAAAgI,KAAAsL,qBACAtL,KAAA7I,SAAAwN,cAAA3M,iBAAA,YAAAgI,KAAAuL,gCACAvL,KAAAmK,qBACAnK,KAAA7I,SAAAC,UAAAO,IAAAqI,KAAA1I,YAAAsK,eAQA6H,EAAA1M,UAAA+D,cAAA,WACAd,KAAA7I,SAAA4J,oBAAA,QAAAf,KAAAoL,mBACApL,KAAA7I,SAAA4J,oBAAA,SAAAf,KAAAqL,oBACArL,KAAA7I,SAAA4J,oBAAA,UAAAf,KAAAsL,qBACAtL,KAAA7I,SAAAwN,cAAA5D,oBAAA,YAAAf,KAAAuL,iCAOA9B,EAAA1M,UAAAiE,aAAAyI,EAAA1M,UAAA+D,cACA2I,EAAA1M,UAAA,aAAA0M,EAAA1M,UAAAiE,aAGA5H,EAAAY,UACAuD,YAAAkM,EACAjM,cAAA,iBCxOAtC,SAAA,gBACAoB,QAAA,GA0BA,IAAAkP,GAAA,SAAA/R,GACAuG,KAAA7I,SAAAsC,EAEAuG,KAAAC,OAEApB,QAAA,gBAAA2M,EAOAA,EAAAzO,UAAAmD,WAAAuL,wBAAA,GASAD,EAAAzO,UAAAzF,aACAoU,kBAAA,qBACAC,2BAAA,8BACAC,mBAAA,sBACAC,sBAAA,yBACAC,iBAAA,oBACAC,kBAAA,sBAQAP,EAAAzO,UAAAiP,YAAA,SAAAC,GACA,GAAAC,GAAAzU,SAAAC,cAAA,MACAwU,GAAA9U,UAAAO,IAAAqI,KAAA1I,YAAAoU,mBACAQ,EAAA9U,UAAAO,IAAAqI,KAAA1I,YAAAoU,kBAAA,IAAAO,EACA,IAAAE,GAAA1U,SAAAC,cAAA,MACAyU,GAAA/U,UAAAO,IAAAqI,KAAA1I,YAAAqU,4BACAQ,EAAA/U,UAAAO,IAAAqI,KAAA1I,YAAAwU,iBACA,IAAAM,GAAA3U,SAAAC,cAAA,MACA0U,GAAAhV,UAAAO,IAAAqI,KAAA1I,YAAAuU,sBACA,IAAAQ,GAAA5U,SAAAC,cAAA,MACA2U,GAAAjV,UAAAO,IAAAqI,KAAA1I,YAAAqU,4BACAU,EAAAjV,UAAAO,IAAAqI,KAAA1I,YAAAyU,kBAMA,KAAA,GALAO,IACAH,EACAC,EACAC,GAEA9R,EAAA,EAAAA,EAAA+R,EAAA7R,OAAAF,IAAA,CACA,GAAAgS,GAAA9U,SAAAC,cAAA,MACA6U,GAAAnV,UAAAO,IAAAqI,KAAA1I,YAAAsU,oBACAU,EAAA/R,GAAAxC,YAAAwU,GAEAL,EAAAnU,YAAAoU,GACAD,EAAAnU,YAAAqU,GACAF,EAAAnU,YAAAsU,GACArM,KAAA7I,SAAAY,YAAAmU,IAEAV,EAAAzO,UAAA,YAAAyO,EAAAzO,UAAAiP,YAOAR,EAAAzO,UAAAyP,KAAA,WACAxM,KAAA7I,SAAAC,UAAA6K,OAAA,cAEAuJ,EAAAzO,UAAA,KAAAyO,EAAAzO,UAAAyP,KAQAhB,EAAAzO,UAAA0P,MAAA,WACAzM,KAAA7I,SAAAC,UAAAO,IAAA,cAEA6T,EAAAzO,UAAA,MAAAyO,EAAAzO,UAAA0P,MAIAjB,EAAAzO,UAAAkD,KAAA,WACA,GAAAD,KAAA7I,SAAA,CACA,IAAA,GAAAoD,GAAA,EAAAA,GAAAyF,KAAAE,UAAAuL,wBAAAlR,IACAyF,KAAAgM,YAAAzR,EAEAyF,MAAA7I,SAAAC,UAAAO,IAAA,iBAKAyB,EAAAY,UACAuD,YAAAiO,EACAhO,cAAA,kBC9HAtC,SAAA,iBACAoB,QAAA,GA0BA,IAAAoQ,GAAA,SAAAjT,GACAuG,KAAA7I,SAAAsC,EAEAuG,KAAAC,OAEApB,QAAA,eAAA6N,EAOAA,EAAA3P,UAAAmD,WAAAgB,aAAA,MASAwL,EAAA3P,UAAAzF,aACA6J,MAAA,oBACAwL,MAAA,oBACAC,MAAA,oBACAvL,aAAA,2BACAlB,cAAA,uBACAoB,qBAAA,sCACAvI,iBAAA,+BACAwI,cAAA,qBACAvI,OAAA,aACAwI,WAAA,aACAC,YAAA,cACAC,WAAA,cAQA+K,EAAA3P,UAAA8E,UAAA,SAAAxB,GACAL,KAAA8B,kBAQA4K,EAAA3P,UAAAgF,SAAA,SAAA1B,GACAL,KAAA7I,SAAAC,UAAAO,IAAAqI,KAAA1I,YAAAmK,aAQAiL,EAAA3P,UAAAiF,QAAA,SAAA3B,GACAL,KAAA7I,SAAAC,UAAA6K,OAAAjC,KAAA1I,YAAAmK,aAQAiL,EAAA3P,UAAAmF,WAAA,SAAA7B,GACAL,KAAAmC,SAOAuK,EAAA3P,UAAA+E,eAAA,WACA9B,KAAAoC,gBACApC,KAAAqC,oBAOAqK,EAAA3P,UAAAoF,MAAA,WAGAtD,OAAAgB,WAAA,WACAG,KAAAsC,cAAAhC,QACAM,KAAAZ,MAAAA,KAAAE,UAAAgB,eAQAwL,EAAA3P,UAAAqF,cAAA,WACApC,KAAAsC,cAAA9B,SACAR,KAAA7I,SAAAC,UAAAO,IAAAqI,KAAA1I,YAAAoK,aAEA1B,KAAA7I,SAAAC,UAAA6K,OAAAjC,KAAA1I,YAAAoK,cAGAgL,EAAA3P,UAAA,cAAA2P,EAAA3P,UAAAqF,cAMAsK,EAAA3P,UAAAsF,iBAAA,WACArC,KAAAsC,cAAAC,QACAvC,KAAA7I,SAAAC,UAAAO,IAAAqI,KAAA1I,YAAAqK,YAEA3B,KAAA7I,SAAAC,UAAA6K,OAAAjC,KAAA1I,YAAAqK,aAGA+K,EAAA3P,UAAA,iBAAA2P,EAAA3P,UAAAsF,iBAMAqK,EAAA3P,UAAAwD,QAAA,WACAP,KAAAsC,cAAA9B,UAAA,EACAR,KAAA8B,kBAEA4K,EAAA3P,UAAA,QAAA2P,EAAA3P,UAAAwD,QAMAmM,EAAA3P,UAAA0D,OAAA,WACAT,KAAAsC,cAAA9B,UAAA,EACAR,KAAA8B,kBAEA4K,EAAA3P,UAAA,OAAA2P,EAAA3P,UAAA0D,OAMAiM,EAAA3P,UAAA8P,GAAA,WACA7M,KAAAsC,cAAAC,SAAA,EACAvC,KAAA8B,kBAEA4K,EAAA3P,UAAA,GAAA2P,EAAA3P,UAAA8P,GAMAH,EAAA3P,UAAA+P,IAAA,WACA9M,KAAAsC,cAAAC,SAAA,EACAvC,KAAA8B,kBAEA4K,EAAA3P,UAAA,IAAA2P,EAAA3P,UAAA+P,IAIAJ,EAAA3P,UAAAkD,KAAA,WACA,GAAAD,KAAA7I,SAAA,CACA6I,KAAAsC,cAAAtC,KAAA7I,SAAAmB,cAAA,IAAA0H,KAAA1I,YAAA6J,MACA,IAAA4L,GAAAtV,SAAAC,cAAA,MACAqV,GAAA3V,UAAAO,IAAAqI,KAAA1I,YAAAqV,MACA,IAAAK,GAAAvV,SAAAC,cAAA,MACAsV,GAAA5V,UAAAO,IAAAqI,KAAA1I,YAAAsV,MACA,IAAAK,GAAAxV,SAAAC,cAAA,OAMA,IALAuV,EAAA7V,UAAAO,IAAAqI,KAAA1I,YAAA+J,cACA2L,EAAAjV,YAAAkV,GACAjN,KAAA7I,SAAAY,YAAAgV,GACA/M,KAAA7I,SAAAY,YAAAiV,GACAhN,KAAAsL,oBAAAtL,KAAAkC,WAAAtB,KAAAZ,MACAA,KAAA7I,SAAAC,UAAAC,SAAA2I,KAAA1I,YAAA6I,eAAA,CACAH,KAAA7I,SAAAC,UAAAO,IAAAqI,KAAA1I,YAAAiK,sBACAvB,KAAA6C,wBAAApL,SAAAC,cAAA,QACAsI,KAAA6C,wBAAAzL,UAAAO,IAAAqI,KAAA1I,YAAA0B,kBACAgH,KAAA6C,wBAAAzL,UAAAO,IAAAqI,KAAA1I,YAAA6I,eACAH,KAAA6C,wBAAAzL,UAAAO,IAAAqI,KAAA1I,YAAAkK,eACAxB,KAAA6C,wBAAA7K,iBAAA,UAAAgI,KAAAsL,oBACA,IAAAzT,GAAAJ,SAAAC,cAAA,OACAG,GAAAT,UAAAO,IAAAqI,KAAA1I,YAAA2B,QACA+G,KAAA6C,wBAAA9K,YAAAF,GACAmI,KAAA7I,SAAAY,YAAAiI,KAAA6C,yBAEA7C,KAAAqL,mBAAArL,KAAA6B,UAAAjB,KAAAZ,MACAA,KAAAkN,kBAAAlN,KAAA+B,SAAAnB,KAAAZ,MACAA,KAAAmN,iBAAAnN,KAAAgC,QAAApB,KAAAZ,MACAA,KAAAsC,cAAAtK,iBAAA,SAAAgI,KAAAqL,oBACArL,KAAAsC,cAAAtK,iBAAA,QAAAgI,KAAAkN,mBACAlN,KAAAsC,cAAAtK,iBAAA,OAAAgI,KAAAmN,kBACAnN,KAAA7I,SAAAa,iBAAA,UAAAgI,KAAAsL,qBACAtL,KAAA8B,iBACA9B,KAAA7I,SAAAC,UAAAO,IAAA,iBAQA+U,EAAA3P,UAAA+D,cAAA,WACAd,KAAA6C,yBACA7C,KAAA6C,wBAAA9B,oBAAA,UAAAf,KAAAsL,qBAEAtL,KAAAsC,cAAAvB,oBAAA,SAAAf,KAAAqL,oBACArL,KAAAsC,cAAAvB,oBAAA,QAAAf,KAAAkN,mBACAlN,KAAAsC,cAAAvB,oBAAA,OAAAf,KAAAmN,kBACAnN,KAAA7I,SAAA4J,oBAAA,UAAAf,KAAAsL,sBAOAoB,EAAA3P,UAAAiE,aAAA0L,EAAA3P,UAAA+D,cACA4L,EAAA3P,UAAA,aAAA2P,EAAA3P,UAAAiE,aAGA5H,EAAAY,UACAuD,YAAAmP,EACAlP,cAAA,iBX1PAtC,SAAA,gBACAoB,QAAA,GA0BA,IAAA8Q,GAAA,SAAA3T,GAEAuG,KAAA7I,SAAAsC,EAEAuG,KAAAC,OAEApB,QAAA,aAAAuO,EAOAA,EAAArQ,UAAAmD,aASAkN,EAAArQ,UAAAzF,aACA+V,UAAA,gBACAC,YAAA,kBACA7U,aAAA,YACA8U,eAAA,cACAhW,qBAAA,uBACAK,qBAAA,6BACAE,WAAA,aACA0V,mCAAA,uCAOAJ,EAAArQ,UAAA0Q,UAAA,WACAzN,KAAA7I,SAAAC,UAAAC,SAAA2I,KAAA1I,YAAAC,uBACAyI,KAAA7I,SAAAC,UAAAO,IAAAqI,KAAA1I,YAAAkW,oCAGAxN,KAAA0N,MAAA1N,KAAA7I,SAAAiE,iBAAA,IAAA4E,KAAA1I,YAAA+V,WACArN,KAAA2N,QAAA3N,KAAA7I,SAAAiE,iBAAA,IAAA4E,KAAA1I,YAAAgW,YAEA,KAAA,GAAA/S,GAAA,EAAAA,EAAAyF,KAAA0N,MAAAjT,OAAAF,IACA,GAAAvD,GAAAgJ,KAAA0N,MAAAnT,GAAAyF,KAEAA,MAAA7I,SAAAC,UAAAO,IAAAqI,KAAA1I,YAAAiW,iBAOAH,EAAArQ,UAAAxE,eAAA,WACA,IAAA,GAAAqV,GAAA,EAAAA,EAAA5N,KAAA0N,MAAAjT,OAAAmT,IACA5N,KAAA0N,MAAAE,GAAAxW,UAAA6K,OAAAjC,KAAA1I,YAAAmB,eAQA2U,EAAArQ,UAAAvE,iBAAA,WACA,IAAA,GAAA2D,GAAA,EAAAA,EAAA6D,KAAA2N,QAAAlT,OAAA0B,IACA6D,KAAA2N,QAAAxR,GAAA/E,UAAA6K,OAAAjC,KAAA1I,YAAAmB,eAMA2U,EAAArQ,UAAAkD,KAAA,WACAD,KAAA7I,UACA6I,KAAAyN,aAkCArU,EAAAY,UACAuD,YAAA6P,EYzIA5P,cAAA,eACAtC,SAAA,eA0BA,IAAA2S,GAAA,SAAApU,GACAuG,KAAA7I,SAAAsC,EACAuG,KAAA8N,QAAA9N,KAAAE,UAAA6N,YAEA/N,KAAAC,OAEApB,QAAA,kBAAAgP,EAOAA,EAAA9Q,UAAAmD,WACA6N,YAAA,GACAC,mBAAA,WAUAH,EAAA9Q,UAAAzF,aACA2W,MAAA,uBACA9M,MAAA,uBACA+M,SAAA,WACAzM,WAAA,aACAC,YAAA,cACAyM,WAAA,aACAvM,YAAA,eAQAiM,EAAA9Q,UAAAqR,WAAA,SAAA/N,GACA,GAAAgO,GAAAhO,EAAAwG,OAAA8D,MAAAvS,MAAA,MAAAqC,MACA,MAAA4F,EAAAqG,SACA2H,GAAArO,KAAA8N,SACAzN,EAAAnI,kBAUA2V,EAAA9Q,UAAAgF,SAAA,SAAA1B,GACAL,KAAA7I,SAAAC,UAAAO,IAAAqI,KAAA1I,YAAAmK,aAQAoM,EAAA9Q,UAAAiF,QAAA,SAAA3B,GACAL,KAAA7I,SAAAC,UAAA6K,OAAAjC,KAAA1I,YAAAmK,aAOAoM,EAAA9Q,UAAA+E,eAAA,WACA9B,KAAAoC,gBACApC,KAAAsO,gBACAtO,KAAAuO,cAQAV,EAAA9Q,UAAAqF,cAAA,WACApC,KAAAwO,OAAAhO,SACAR,KAAA7I,SAAAC,UAAAO,IAAAqI,KAAA1I,YAAAoK,aAEA1B,KAAA7I,SAAAC,UAAA6K,OAAAjC,KAAA1I,YAAAoK,cAGAmM,EAAA9Q,UAAA,cAAA8Q,EAAA9Q,UAAAqF,cAMAyL,EAAA9Q,UAAAuR,cAAA,WACAtO,KAAAwO,OAAAC,WACAzO,KAAAwO,OAAAC,SAAAC,MACA1O,KAAA7I,SAAAC,UAAA6K,OAAAjC,KAAA1I,YAAA6W,YAEAnO,KAAA7I,SAAAC,UAAAO,IAAAqI,KAAA1I,YAAA6W,cAIAN,EAAA9Q,UAAA,cAAA8Q,EAAA9Q,UAAAuR,cAMAT,EAAA9Q,UAAAwR,WAAA,WACAvO,KAAAwO,OAAA7D,OAAA3K,KAAAwO,OAAA7D,MAAAlQ,OAAA,EACAuF,KAAA7I,SAAAC,UAAAO,IAAAqI,KAAA1I,YAAA4W,UAEAlO,KAAA7I,SAAAC,UAAA6K,OAAAjC,KAAA1I,YAAA4W,WAGAL,EAAA9Q,UAAA,WAAA8Q,EAAA9Q,UAAAwR,WAMAV,EAAA9Q,UAAAwD,QAAA,WACAP,KAAAwO,OAAAhO,UAAA,EACAR,KAAA8B,kBAEA+L,EAAA9Q,UAAA,QAAA8Q,EAAA9Q,UAAAwD,QAMAsN,EAAA9Q,UAAA0D,OAAA,WACAT,KAAAwO,OAAAhO,UAAA,EACAR,KAAA8B,kBAEA+L,EAAA9Q,UAAA,OAAA8Q,EAAA9Q,UAAA0D,OAOAoN,EAAA9Q,UAAAkO,OAAA,SAAAN,GACA3K,KAAAwO,OAAA7D,MAAAA,GAAA,GACA3K,KAAA8B,kBAEA+L,EAAA9Q,UAAA,OAAA8Q,EAAA9Q,UAAAkO,OAIA4C,EAAA9Q,UAAAkD,KAAA,WACA,GAAAD,KAAA7I,WACA6I,KAAA2O,OAAA3O,KAAA7I,SAAAmB,cAAA,IAAA0H,KAAA1I,YAAA2W,OACAjO,KAAAwO,OAAAxO,KAAA7I,SAAAmB,cAAA,IAAA0H,KAAA1I,YAAA6J,OACAnB,KAAAwO,QAAA,CACAxO,KAAAwO,OAAAvH,aAAAjH,KAAAE,UAAA8N,sBACAhO,KAAA8N,QAAAc,SAAA5O,KAAAwO,OAAA3T,aAAAmF,KAAAE,UAAA8N,oBAAA,IACAa,MAAA7O,KAAA8N,WACA9N,KAAA8N,QAAA9N,KAAAE,UAAA6N,cAGA/N,KAAA8O,0BAAA9O,KAAA8B,eAAAlB,KAAAZ,MACAA,KAAAkN,kBAAAlN,KAAA+B,SAAAnB,KAAAZ,MACAA,KAAAmN,iBAAAnN,KAAAgC,QAAApB,KAAAZ,MACAA,KAAAwO,OAAAxW,iBAAA,QAAAgI,KAAA8O,2BACA9O,KAAAwO,OAAAxW,iBAAA,QAAAgI,KAAAkN,mBACAlN,KAAAwO,OAAAxW,iBAAA,OAAAgI,KAAAmN,kBACAnN,KAAA8N,UAAA9N,KAAAE,UAAA6N,cAGA/N,KAAA+O,oBAAA/O,KAAAoO,WAAAxN,KAAAZ,MACAA,KAAAwO,OAAAxW,iBAAA,UAAAgI,KAAA+O,qBAEA,IAAAC,GAAAhP,KAAA7I,SAAAC,UAAAC,SAAA2I,KAAA1I,YAAA6W,WACAnO,MAAA8B,iBACA9B,KAAA7I,SAAAC,UAAAO,IAAAqI,KAAA1I,YAAAsK,aACAoN,GACAhP,KAAA7I,SAAAC,UAAAO,IAAAqI,KAAA1I,YAAA6W,cAUAN,EAAA9Q,UAAA+D,cAAA,WACAd,KAAAwO,OAAAzN,oBAAA,QAAAf,KAAA8O,2BACA9O,KAAAwO,OAAAzN,oBAAA,QAAAf,KAAAkN,mBACAlN,KAAAwO,OAAAzN,oBAAA,OAAAf,KAAAmN,kBACAnN,KAAA+O,qBACA/O,KAAAwO,OAAAzN,oBAAA,UAAAf,KAAA+O,sBAQAlB,EAAA9Q,UAAAiE,aAAA6M,EAAA9Q,UAAA+D,cACA+M,EAAA9Q,UAAA,aAAA8Q,EAAA9Q,UAAAiE,aAGA5H,EAAAY,UACAuD,YAAAsQ,EACArQ,cAAA,oBC3OAtC,SAAA,mBACAoB,QAAA,GA0BA,IAAA2S,GAAA,SAAAxV,GACAuG,KAAA7I,SAAAsC,EAEAuG,KAAAC,OAEApB,QAAA,gBAAAoQ,EAOAA,EAAAlS,UAAAmD,aASA+O,EAAAlS,UAAAzF,aAAA6B,UAAA,aAOA8V,EAAAlS,UAAAmS,kBAAA,SAAA7O,GACAA,EAAA6G,iBACA,IAAAiI,GAAA9O,EAAAwG,OAAAd,wBACAO,EAAA6I,EAAA7I,KAAA6I,EAAA7H,MAAA,EACA8H,EAAA,IAAApP,KAAA7I,SAAAkY,YAAA,EACA,GAAA/I,EAAA8I,GACApP,KAAA7I,SAAA8O,MAAAK,KAAA,EACAtG,KAAA7I,SAAA8O,MAAAmJ,WAAA,IAEApP,KAAA7I,SAAA8O,MAAAK,KAAAA,EAAA,KACAtG,KAAA7I,SAAA8O,MAAAmJ,WAAAA,EAAA,MAEApP,KAAA7I,SAAA8O,MAAAE,IAAAgJ,EAAAhJ,IAAAgJ,EAAA9H,OAAA,GAAA,KACArH,KAAA7I,SAAAC,UAAAO,IAAAqI,KAAA1I,YAAA6B,WACA0F,OAAA7G,iBAAA,SAAAgI,KAAAsP,wBAAA,GACAzQ,OAAA7G,iBAAA,YAAAgI,KAAAsP,wBAAA,IAQAL,EAAAlS,UAAAwS,kBAAA,SAAAlP,GACAA,EAAA6G,kBACAlH,KAAA7I,SAAAC,UAAA6K,OAAAjC,KAAA1I,YAAA6B,WACA0F,OAAAkC,oBAAA,SAAAf,KAAAsP,wBACAzQ,OAAAkC,oBAAA,YAAAf,KAAAsP,wBAAA,IAKAL,EAAAlS,UAAAkD,KAAA,WACA,GAAAD,KAAA7I,SAAA,CACA,GAAA8N,GAAAjF,KAAA7I,SAAA0D,aAAA,MACAoK,KACAjF,KAAAoF,YAAA3N,SAAA0N,eAAAF,IAEAjF,KAAAoF,cAEApF,KAAAoF,YAAA6B,aAAA,aACAjH,KAAAoF,YAAAvJ,aAAA,WAAA,KAEAmE,KAAAwP,uBAAAxP,KAAAkP,kBAAAtO,KAAAZ,MACAA,KAAAsP,uBAAAtP,KAAAuP,kBAAA3O,KAAAZ,MACAA,KAAAoF,YAAApN,iBAAA,aAAAgI,KAAAwP,wBAAA,GACAxP,KAAAoF,YAAApN,iBAAA,QAAAgI,KAAAwP,wBAAA,GACAxP,KAAAoF,YAAApN,iBAAA,OAAAgI,KAAAsP,wBACAtP,KAAAoF,YAAApN,iBAAA,aAAAgI,KAAAwP,wBAAA,GACAxP,KAAAoF,YAAApN,iBAAA,aAAAgI,KAAAsP,2BASAL,EAAAlS,UAAA+D,cAAA,WACAd,KAAAoF,cACApF,KAAAoF,YAAArE,oBAAA,aAAAf,KAAAwP,wBAAA,GACAxP,KAAAoF,YAAArE,oBAAA,QAAAf,KAAAwP,wBAAA,GACAxP,KAAAoF,YAAArE,oBAAA,aAAAf,KAAAwP,wBAAA,GACAxP,KAAAoF,YAAArE,oBAAA,aAAAf,KAAAsP,0BAQAL,EAAAlS,UAAAiE,aAAAiO,EAAAlS,UAAA+D,cACAmO,EAAAlS,UAAA,aAAAkS,EAAAlS,UAAAiE,aAGA5H,EAAAY,UACAuD,YAAA0R,EZnIAzR,cAAA,kBACAtC,SAAA,eA0BA,IAAAuU,GAAA,SAAAhW,GACAuG,KAAA7I,SAAAsC,EAEAuG,KAAAC,OAEApB,QAAA,eAAA4Q,EAOAA,EAAA1S,UAAAmD,WACAwP,UAAA,sBACAC,kBAAA,IACAC,UAAA,OACAC,aAAA,eACAC,cAAA,iBAQAL,EAAA1S,UAAAgT,OACAC,SAAA,EACAC,OAAA,EACAC,UAAA,EACAC,OAAA,GAUAV,EAAA1S,UAAAzF,aACAyM,UAAA,wBACAqM,OAAA,qBACAC,OAAA,qBACAC,QAAA,sBACAC,WAAA,4BACAC,KAAA,iBACAzX,iBAAA,uBACAC,iBAAA,mCACAC,OAAA,aACAsI,qBAAA,sCACAkP,cAAA,6BACAC,iBAAA,gCACAC,cAAA,6BACAC,aAAA,2BACAC,WAAA,yBACAC,QAAA,sBACAC,cAAA,gCACAC,IAAA,kBACAC,eAAA,6BACAC,oBAAA,kCACAC,qBAAA,mCACAC,MAAA,wBACAC,WAAA,aACAC,SAAA,WACAC,qBAAA,uBACAC,eAAA,oBACAC,WAAA,aACAC,gBAAA,kBACAC,eAAA,aACAxY,UAAA,YACAyI,YAAA,cACAwC,aAAA,eACAwN,gBAAA,gCACAC,gBAAA,iCAOApC,EAAA1S,UAAA+U,sBAAA,WACA9R,KAAA+R,QAAA3a,UAAAC,SAAA2I,KAAA1I,YAAA8M,gBAGApE,KAAA9G,SAAA8Y,UAAA,IAAAhS,KAAA+R,QAAA3a,UAAAC,SAAA2I,KAAA1I,YAAAma,aACAzR,KAAA+R,QAAA3a,UAAAO,IAAAqI,KAAA1I,YAAAka,gBACAxR,KAAA+R,QAAA3a,UAAAO,IAAAqI,KAAA1I,YAAAma,YACAzR,KAAA+R,QAAA3a,UAAAO,IAAAqI,KAAA1I,YAAA8M,eACApE,KAAA9G,SAAA8Y,WAAA,GAAAhS,KAAA+R,QAAA3a,UAAAC,SAAA2I,KAAA1I,YAAAma,cACAzR,KAAA+R,QAAA3a,UAAA6K,OAAAjC,KAAA1I,YAAAka,gBACAxR,KAAA+R,QAAA3a,UAAA6K,OAAAjC,KAAA1I,YAAAma,YACAzR,KAAA+R,QAAA3a,UAAAO,IAAAqI,KAAA1I,YAAA8M,iBAQAqL,EAAA1S,UAAAkV,mBAAA,WACAjS,KAAAkS,sBAAAC,QACAnS,KAAA7I,SAAAC,UAAAO,IAAAqI,KAAA1I,YAAAoa,kBAEA1R,KAAA7I,SAAAC,UAAA6K,OAAAjC,KAAA1I,YAAAoa,iBAEA1R,KAAAoS,UACApS,KAAAoS,QAAAhb,UAAA6K,OAAAjC,KAAA1I,YAAAqa,gBACA3R,KAAAqS,YAAAjb,UAAA6K,OAAAjC,KAAA1I,YAAAqa,mBASAlC,EAAA1S,UAAAuV,qBAAA,WACAtS,KAAAoS,QAAAhb,UAAAqP,OAAAzG,KAAA1I,YAAAqa,gBACA3R,KAAAqS,YAAAjb,UAAAqP,OAAAzG,KAAA1I,YAAAqa,iBAOAlC,EAAA1S,UAAAwV,4BAAA,WACAvS,KAAA+R,QAAA3a,UAAA6K,OAAAjC,KAAA1I,YAAA8M,eAOAqL,EAAA1S,UAAAyV,oBAAA,WACAxS,KAAA+R,QAAA3a,UAAAC,SAAA2I,KAAA1I,YAAAma,cACAzR,KAAA+R,QAAA3a,UAAA6K,OAAAjC,KAAA1I,YAAAma,YACAzR,KAAA+R,QAAA3a,UAAAO,IAAAqI,KAAA1I,YAAA8M,gBAQAqL,EAAA1S,UAAAxE,eAAA,SAAAka,GACA,IAAA,GAAA7E,GAAA,EAAAA,EAAA6E,EAAAhY,OAAAmT,IACA6E,EAAA7E,GAAAxW,UAAA6K,OAAAjC,KAAA1I,YAAA6B,YAQAsW,EAAA1S,UAAAvE,iBAAA,SAAAI,GACA,IAAA,GAAAuD,GAAA,EAAAA,EAAAvD,EAAA6B,OAAA0B,IACAvD,EAAAuD,GAAA/E,UAAA6K,OAAAjC,KAAA1I,YAAA6B,YAMAsW,EAAA1S,UAAAkD,KAAA,WACA,GAAAD,KAAA7I,SAAA,CACA,GAAAuN,GAAAjN,SAAAC,cAAA,MACAgN,GAAAtN,UAAAO,IAAAqI,KAAA1I,YAAAyM,WACA/D,KAAA7I,SAAAwN,cAAAC,aAAAF,EAAA1E,KAAA7I,UACA6I,KAAA7I,SAAAwN,cAAAE,YAAA7E,KAAA7I,UACAuN,EAAA3M,YAAAiI,KAAA7I,SAGA,KAAA,GAFAub,GAAA1S,KAAA7I,SAAAwb,WACAC,EAAAF,EAAAjY,OACAoY,EAAA,EAAAD,EAAAC,EAAAA,IAAA,CACA,GAAAC,GAAAJ,EAAAG,EACAC,GAAA1b,WAAA0b,EAAA1b,UAAAC,SAAA2I,KAAA1I,YAAA8Y,UACApQ,KAAA+R,QAAAe,GAEAA,EAAA1b,WAAA0b,EAAA1b,UAAAC,SAAA2I,KAAA1I,YAAA+Y,UACArQ,KAAAoS,QAAAU,GAEAA,EAAA1b,WAAA0b,EAAA1b,UAAAC,SAAA2I,KAAA1I,YAAAgZ,WACAtQ,KAAA9G,SAAA4Z,GAGA9S,KAAA+R,UACA/R,KAAAlH,QAAAkH,KAAA+R,QAAAzZ,cAAA,IAAA0H,KAAA1I,YAAAwZ,SAEA,IAAAiC,GAAA/S,KAAA+P,MAAAC,QA+BA,IA9BAhQ,KAAA+R,UACA/R,KAAA+R,QAAA3a,UAAAC,SAAA2I,KAAA1I,YAAAmZ,eACAsC,EAAA/S,KAAA+P,MAAAE,OACAjQ,KAAA+R,QAAA3a,UAAAC,SAAA2I,KAAA1I,YAAAoZ,mBACAqC,EAAA/S,KAAA+P,MAAAG,UACAlQ,KAAA+R,QAAA/Z,iBAAA,gBAAAgI,KAAAuS,4BAAA3R,KAAAZ,OACAA,KAAA+R,QAAA/Z,iBAAA,QAAAgI,KAAAwS,oBAAA5R,KAAAZ,QACAA,KAAA+R,QAAA3a,UAAAC,SAAA2I,KAAA1I,YAAAqZ,iBACAoC,EAAA/S,KAAA+P,MAAAI,OACAzL,EAAAtN,UAAAO,IAAAqI,KAAA1I,YAAAia,uBAEAwB,IAAA/S,KAAA+P,MAAAC,UACAhQ,KAAA+R,QAAA3a,UAAAO,IAAAqI,KAAA1I,YAAAka,gBACAxR,KAAAlH,SACAkH,KAAAlH,QAAA1B,UAAAO,IAAAqI,KAAA1I,YAAAka,iBAEAuB,IAAA/S,KAAA+P,MAAAE,QAAA8C,IAAA/S,KAAA+P,MAAAI,QACAnQ,KAAA+R,QAAA3a,UAAA6K,OAAAjC,KAAA1I,YAAAka,gBACAxR,KAAAlH,SACAkH,KAAAlH,QAAA1B,UAAA6K,OAAAjC,KAAA1I,YAAAka,iBAEAuB,IAAA/S,KAAA+P,MAAAG,YAIAlQ,KAAA9G,SAAAlB,iBAAA,SAAAgI,KAAA8R,sBAAAlR,KAAAZ,OACAA,KAAA8R,0BAIA9R,KAAAoS,QAAA,CACA,GAAAY,GAAAhT,KAAA7I,SAAAmB,cAAA,IAAA0H,KAAA1I,YAAAiZ,WACA,KAAAyC,EAAA,CACAA,EAAAvb,SAAAC,cAAA,OACAsb,EAAA5b,UAAAO,IAAAqI,KAAA1I,YAAAiZ,WACA,IAAA0C,GAAAxb,SAAAC,cAAA,IACAub,GAAA7b,UAAAO,IAAAqI,KAAA1I,YAAAkZ,MACAyC,EAAAC,YAAAlT,KAAAE,UAAA0P,UACAoD,EAAAjb,YAAAkb,GAEAjT,KAAAoS,QAAAhb,UAAAC,SAAA2I,KAAA1I,YAAAsa,iBAEAoB,EAAA5b,UAAAO,IAAAqI,KAAA1I,YAAAsa,iBACA5R,KAAAoS,QAAAhb,UAAAC,SAAA2I,KAAA1I,YAAAua,kBAEAmB,EAAA5b,UAAAO,IAAAqI,KAAA1I,YAAAua,iBAEAmB,EAAAhb,iBAAA,QAAAgI,KAAAsS,qBAAA1R,KAAAZ,OAIAA,KAAA7I,SAAAC,UAAAO,IAAAqI,KAAA1I,YAAA+Z,YAGArR,KAAA7I,SAAAC,UAAAC,SAAA2I,KAAA1I,YAAAsZ,cACA5Q,KAAA+R,QAAAnN,aAAAoO,EAAAhT,KAAA+R,QAAAvJ,YAEAxI,KAAA7I,SAAAyN,aAAAoO,EAAAhT,KAAA9G,SAEA,IAAAia,GAAA1b,SAAAC,cAAA,MACAyb,GAAA/b,UAAAO,IAAAqI,KAAA1I,YAAAuZ,YACA7Q,KAAA7I,SAAAY,YAAAob,GACAA,EAAAnb,iBAAA,QAAAgI,KAAAsS,qBAAA1R,KAAAZ,OACAA,KAAAqS,YAAAc,EAQA,GAJAnT,KAAAkS,sBAAArT,OAAAuU,WAAApT,KAAAE,UAAAwP,WACA1P,KAAAkS,sBAAAmB,YAAArT,KAAAiS,mBAAArR,KAAAZ,OACAA,KAAAiS,qBAEAjS,KAAA+R,SAAA/R,KAAAlH,QAAA,CACAkH,KAAA7I,SAAAC,UAAAO,IAAAqI,KAAA1I,YAAAga,SACA,IAAAgC,GAAA7b,SAAAC,cAAA,MACA4b,GAAAlc,UAAAO,IAAAqI,KAAA1I,YAAAyZ,eACA/Q,KAAA+R,QAAAnN,aAAA0O,EAAAtT,KAAAlH,SACAkH,KAAA+R,QAAAlN,YAAA7E,KAAAlH,QACA,IAAAya,GAAA9b,SAAAC,cAAA,MACA6b,GAAAnc,UAAAO,IAAAqI,KAAA1I,YAAA2Z,gBACAsC,EAAAnc,UAAAO,IAAAqI,KAAA1I,YAAA4Z,oBACA,IAAAsC,GAAA/b,SAAAC,cAAA,IACA8b,GAAApc,UAAAO,IAAAqI,KAAA1I,YAAAkZ,MACAgD,EAAAN,YAAAlT,KAAAE,UAAA2P,aACA0D,EAAAxb,YAAAyb,GACAD,EAAAvb,iBAAA,QAAA,WACAgI,KAAAlH,QAAA2a,YAAAzT,KAAAE,UAAAyP,mBACA/O,KAAAZ,MACA,IAAA0T,GAAAjc,SAAAC,cAAA,MACAgc,GAAAtc,UAAAO,IAAAqI,KAAA1I,YAAA2Z,gBACAyC,EAAAtc,UAAAO,IAAAqI,KAAA1I,YAAA6Z,qBACA,IAAAwC,GAAAlc,SAAAC,cAAA,IACAic,GAAAvc,UAAAO,IAAAqI,KAAA1I,YAAAkZ,MACAmD,EAAAT,YAAAlT,KAAAE,UAAA4P,cACA4D,EAAA3b,YAAA4b,GACAD,EAAA1b,iBAAA,QAAA,WACAgI,KAAAlH,QAAA2a,YAAAzT,KAAAE,UAAAyP,mBACA/O,KAAAZ,OACAsT,EAAAvb,YAAAwb,GACAD,EAAAvb,YAAAiI,KAAAlH,SACAwa,EAAAvb,YAAA2b,EAEA,IAAAE,GAAA,WACA5T,KAAAlH,QAAA2a,WAAA,EACAF,EAAAnc,UAAAO,IAAAqI,KAAA1I,YAAA6B,WAEAoa,EAAAnc,UAAA6K,OAAAjC,KAAA1I,YAAA6B,WAEA6G,KAAAlH,QAAA2a,WAAAzT,KAAAlH,QAAA+a,YAAA7T,KAAAlH,QAAAuW,YACAqE,EAAAtc,UAAAO,IAAAqI,KAAA1I,YAAA6B,WAEAua,EAAAtc,UAAA6K,OAAAjC,KAAA1I,YAAA6B,YAEAyH,KAAAZ,KACAA,MAAAlH,QAAAd,iBAAA,SAAA4b,GACAA,IACA5T,KAAAlH,QAAA1B,UAAAC,SAAA2I,KAAA1I,YAAAyB,mBACAiH,KAAAlH,QAAA1B,UAAAO,IAAAqI,KAAA1I,YAAAiK,qBAMA,KAAA,GAHA5I,GAAAqH,KAAAlH,QAAAsC,iBAAA,IAAA4E,KAAA1I,YAAA0Z,KACApY,EAAAoH,KAAA9G,SAAAkC,iBAAA,IAAA4E,KAAA1I,YAAA8Z,OAEA7W,EAAA,EAAAA,EAAA5B,EAAA8B,OAAAF,IACA,GAAA7B,GAAAC,EAAA4B,GAAA5B,EAAAC,EAAAoH,MAGAA,KAAA7I,SAAAC,UAAAO,IAAAqI,KAAA1I,YAAAsK,eAkCAxI,EAAAY,UACAuD,YAAAkS,EavXAjS,cAAA,iBACAtC,SAAA,iBA0BA,IAAA4Y,GAAA,SAAAra,GACAuG,KAAA7I,SAAAsC,EAEAuG,KAAAC,OAEApB,QAAA,kBAAAiV,EAOAA,EAAA/W,UAAAmD,aASA4T,EAAA/W,UAAAzF,aACAyc,WAAA,iBACAC,WAAA,6BACAC,eAAA,yBACAC,YAAA,cACAtS,YAAA,eAWAkS,EAAA/W,UAAAoX,WAAA,SAAAC,EAAAC,EAAAC,GACA,MAAAD,GACA,WACAD,EAAA7R,QACA8R,EAAAjd,UAAAO,IAAAqI,KAAA1I,YAAA4c,aAEAG,EAAAjd,UAAA6K,OAAAjC,KAAA1I,YAAA4c,cAEAtT,KAAAZ,MAEAsU,EACA,WACA,GAAA/Z,GACAgO,CACA,IAAA6L,EAAA7R,QACA,IAAAhI,EAAA,EAAAA,EAAA+Z,EAAA7Z,OAAAF,IACAgO,EAAA+L,EAAA/Z,GAAAjC,cAAA,MAAAA,cAAA,iBACAiQ,EAAA,iBAAA/F,QACA8R,EAAA/Z,GAAAnD,UAAAO,IAAAqI,KAAA1I,YAAA4c,iBAGA,KAAA3Z,EAAA,EAAAA,EAAA+Z,EAAA7Z,OAAAF,IACAgO,EAAA+L,EAAA/Z,GAAAjC,cAAA,MAAAA,cAAA,iBACAiQ,EAAA,iBAAA9F,UACA6R,EAAA/Z,GAAAnD,UAAA6K,OAAAjC,KAAA1I,YAAA4c,cAGAtT,KAAAZ,MAjBA,QA4BA8T,EAAA/W,UAAAwX,gBAAA,SAAAF,EAAAC,GACA,GAAAE,GAAA/c,SAAAC,cAAA,SACA+c,GACA,eACA,kBACA,uBACAzU,KAAA1I,YAAA2c,eAEAO,GAAA9Z,UAAA+Z,EAAA3Y,KAAA,IACA,IAAAsY,GAAA3c,SAAAC,cAAA,QAMA,OALA0c,GAAAM,KAAA,WACAN,EAAAhd,UAAAO,IAAA,uBACAyc,EAAApc,iBAAA,SAAAgI,KAAAmU,WAAAC,EAAAC,EAAAC,IACAE,EAAAzc,YAAAqc,GACAhb,EAAAI,eAAAgb,EAAA,oBACAA,GAKAV,EAAA/W,UAAAkD,KAAA,WACA,GAAAD,KAAA7I,SAAA,CACA,GAAAwd,GAAA3U,KAAA7I,SAAAmB,cAAA,MACAsc,EAAA5U,KAAA7I,SAAAmB,cAAA,SAAA8C,iBAAA,KACA,IAAA4E,KAAA7I,SAAAC,UAAAC,SAAA2I,KAAA1I,YAAA0c,YAAA,CACA,GAAAa,GAAApd,SAAAC,cAAA,MACAod,EAAA9U,KAAAuU,gBAAA,KAAAK,EACAC,GAAA9c,YAAA+c,GACAH,EAAAhQ,cAAAC,aAAAiQ,EAAAF,EACA,KAAA,GAAApa,GAAA,EAAAA,EAAAqa,EAAAna,OAAAF,IAAA,CACA,GAAAwa,GAAAH,EAAAra,GAAAjC,cAAA,KACA,IAAAyc,EAAA,CACA,GAAAC,GAAAvd,SAAAC,cAAA,MACAud,EAAAjV,KAAAuU,gBAAAK,EAAAra,GACAya,GAAAjd,YAAAkd,GACAL,EAAAra,GAAAqK,aAAAoQ,EAAAD,KAIA/U,KAAA7I,SAAAC,UAAAO,IAAAqI,KAAA1I,YAAAsK,eAKAxI,EAAAY,UACAuD,YAAAuW,ECnJAtW,cAAA,oBACAtC,SAAA,qBA0BA,IAAAga,GAAA,SAAAzb,GACAuG,KAAA7I,SAAAsC,EAEAuG,KAAAC,OAEApB,QAAA,eAAAqW,EAOAA,EAAAnY,UAAAmD,WACAiV,cAAA,wBACAC,aAAA,MACAC,gBAAA,MACAC,cAAA,IACAC,YAAA,IAUAL,EAAAnY,UAAAzF,aACAkK,cAAA,qBACAgU,4BAAA,sCACAvc,OAAA,aACAmL,aAAA,eACAD,WAAA,cAQA+Q,EAAAnY,UAAA0Y,aAAA,SAAApV,GACA,IAAAL,KAAAU,eAAAuF,MAAAqB,QAAAtH,KAAAU,eAAAuF,MAAAoB,OAAA,CACA,GAAAvB,GAAA9F,KAAA7I,SAAA4O,uBACA/F,MAAA0V,YAAA5P,EAAAuB,OACArH,KAAA2V,WAAA7P,EAAAwB,MACAtH,KAAA4V,YAAA,EAAAjW,KAAAkW,KAAA/P,EAAAwB,MAAAxB,EAAAwB,MAAAxB,EAAAuB,OAAAvB,EAAAuB,QAAA,EACArH,KAAAU,eAAAuF,MAAAqB,MAAAtH,KAAA4V,YAAA,KACA5V,KAAAU,eAAAuF,MAAAoB,OAAArH,KAAA4V,YAAA,KAGA,GADA5V,KAAAU,eAAAtJ,UAAAO,IAAAqI,KAAA1I,YAAA6M,YACA,cAAA9D,EAAAqU,MAAA1U,KAAA8V,mBACA9V,KAAA8V,oBAAA,MACA,CACA,eAAAzV,EAAAqU,OACA1U,KAAA8V,oBAAA,EAEA,IAAAC,GAAA/V,KAAAgW,eACA,IAAAD,EAAA,EACA,MAEA/V,MAAAiW,cAAA,EACA,IACAC,GACAzL,EAFA0L,EAAA9V,EAAA+V,cAAArQ,uBAIA,IAAA,IAAA1F,EAAAkK,SAAA,IAAAlK,EAAAmK,QACA0L,EAAAvW,KAAA0W,MAAAF,EAAA7O,MAAA,GACAmD,EAAA9K,KAAA0W,MAAAF,EAAA9O,OAAA,OACA,CACA,GAAAkD,GAAAlK,EAAAkK,QAAAlK,EAAAkK,QAAAlK,EAAAiW,QAAA,GAAA/L,QACAC,EAAAnK,EAAAmK,QAAAnK,EAAAmK,QAAAnK,EAAAiW,QAAA,GAAA9L,OACA0L,GAAAvW,KAAA0W,MAAA9L,EAAA4L,EAAA7P,MACAmE,EAAA9K,KAAA0W,MAAA7L,EAAA2L,EAAAhQ,KAEAnG,KAAAuW,YAAAL,EAAAzL,GACAzK,KAAAwW,iBAAA,GACA3X,OAAAM,sBAAAa,KAAAyW,iBAAA7V,KAAAZ,SASAkV,EAAAnY,UAAA2Z,WAAA,SAAArW,GAEAA,GAAA,IAAAA,EAAAsW,QACA3W,KAAAU,eAAAtJ,UAAA6K,OAAAjC,KAAA1I,YAAA6M,YAKAtF,OAAAgB,WAAA,WACAG,KAAAU,eAAAtJ,UAAA6K,OAAAjC,KAAA1I,YAAA6M,aACAvD,KAAAZ,MAAA,IAKAkV,EAAAnY,UAAAkD,KAAA,WACA,GAAAD,KAAA7I,SAAA,CACA,GAAAyf,GAAA5W,KAAA7I,SAAAC,UAAAC,SAAA2I,KAAA1I,YAAAkK,cACAxB,MAAA7I,SAAAC,UAAAC,SAAA2I,KAAA1I,YAAAke,+BACAxV,KAAAU,eAAAV,KAAA7I,SAAAmB,cAAA,IAAA0H,KAAA1I,YAAA2B,QACA+G,KAAA6W,YAAA,EACA7W,KAAA4V,YAAA,EACA5V,KAAA8W,GAAA,EACA9W,KAAA+W,GAAA,EAIA/W,KAAA8V,oBAAA,EACA9V,KAAAgX,iBAAAhX,KAAAyV,aAAA7U,KAAAZ,MACAA,KAAA7I,SAAAa,iBAAA,YAAAgI,KAAAgX,kBACAhX,KAAA7I,SAAAa,iBAAA,aAAAgI,KAAAgX,kBACAhX,KAAAiX,eAAAjX,KAAA0W,WAAA9V,KAAAZ,MACAA,KAAA7I,SAAAa,iBAAA,UAAAgI,KAAAiX,gBACAjX,KAAA7I,SAAAa,iBAAA,aAAAgI,KAAAiX,gBACAjX,KAAA7I,SAAAa,iBAAA,WAAAgI,KAAAiX,gBACAjX,KAAA7I,SAAAa,iBAAA,OAAAgI,KAAAiX,gBAKAjX,KAAAgW,cAAA,WACA,MAAAhW,MAAA6W,aAMA7W,KAAAiW,cAAA,SAAAiB,GACAlX,KAAA6W,YAAAK,GAMAlX,KAAAmX,iBAAA,WACA,MAAAnX,MAAAU,gBAOAV,KAAAuW,YAAA,SAAAa,EAAAC,GACArX,KAAA8W,GAAAM,EACApX,KAAA+W,GAAAM,GAMArX,KAAAwW,gBAAA,SAAA/J,GACA,GAAA,OAAAzM,KAAAU,eAAA,CACA,GAAA4W,GACAC,EACAC,EACAC,EAAA,aAAAzX,KAAA8W,GAAA,OAAA9W,KAAA+W,GAAA,KACAtK,IACA8K,EAAAvX,KAAAE,UAAAiV,cACAqC,EAAAxX,KAAAE,UAAAkV,eAEAmC,EAAAvX,KAAAE,UAAAqV,YACAiC,EAAAxX,KAAA4V,YAAA,KACAgB,IACAa,EAAA,aAAAzX,KAAA2V,WAAA,EAAA,OAAA3V,KAAA0V,YAAA,EAAA,QAGA4B,EAAA,yBAAAG,EAAAF,EACAvX,KAAAU,eAAAuF,MAAAyR,gBAAAJ,EACAtX,KAAAU,eAAAuF,MAAA0R,YAAAL,EACAtX,KAAAU,eAAAuF,MAAA2R,UAAAN,EACA7K,EACAzM,KAAAU,eAAAtJ,UAAA6K,OAAAjC,KAAA1I,YAAA8M,cAEApE,KAAAU,eAAAtJ,UAAAO,IAAAqI,KAAA1I,YAAA8M,gBAOApE,KAAAyW,iBAAA,WACAzW,KAAA6W,cAAA,EACAhY,OAAAM,sBAAAa,KAAAyW,iBAAA7V,KAAAZ,OAEAA,KAAAwW,iBAAA,OAWAtB,EAAAnY,UAAA+D,cAAA,WACAd,KAAA7I,SAAA4J,oBAAA,YAAAf,KAAAgX,kBACAhX,KAAA7I,SAAA4J,oBAAA,aAAAf,KAAAgX,kBACAhX,KAAA7I,SAAA4J,oBAAA,UAAAf,KAAAiX,gBACAjX,KAAA7I,SAAA4J,oBAAA,aAAAf,KAAAiX,gBACAjX,KAAA7I,SAAA4J,oBAAA,WAAAf,KAAAiX,gBACAjX,KAAA7I,SAAA4J,oBAAA,OAAAf,KAAAiX,iBAOA/B,EAAAnY,UAAAiE,aAAAkU,EAAAnY,UAAA+D,cACAoU,EAAAnY,UAAA,aAAAmY,EAAAnY,UAAAiE,aAGA5H,EAAAY,UACAuD,YAAA2X,EACA1X,cAAA,iBjB+hHItC,SAAU,uBACVoB,QAAQ","file":"material.min.js","sourcesContent":[";(function() {\n\"use strict\";\n\n/**\n * @license\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * A component handler interface using the revealing module design pattern.\n * More details on this design pattern here:\n * https://github.com/jasonmayes/mdl-component-design-pattern\n *\n * @author Jason Mayes.\n */\n/* exported componentHandler */\n\n// Pre-defining the componentHandler interface, for closure documentation and\n// static verification.\nvar componentHandler = {\n /**\n * Searches existing DOM for elements of our component type and upgrades them\n * if they have not already been upgraded.\n *\n * @param {string=} optJsClass the programatic name of the element class we\n * need to create a new instance of.\n * @param {string=} optCssClass the name of the CSS class elements of this\n * type will have.\n */\n upgradeDom: function(optJsClass, optCssClass) {},\n /**\n * Upgrades a specific element rather than all in the DOM.\n *\n * @param {!Element} element The element we wish to upgrade.\n * @param {string=} optJsClass Optional name of the class we want to upgrade\n * the element to.\n */\n upgradeElement: function(element, optJsClass) {},\n /**\n * Upgrades a specific list of elements rather than all in the DOM.\n *\n * @param {!Element|!Array|!NodeList|!HTMLCollection} elements\n * The elements we wish to upgrade.\n */\n upgradeElements: function(elements) {},\n /**\n * Upgrades all registered components found in the current DOM. This is\n * automatically called on window load.\n */\n upgradeAllRegistered: function() {},\n /**\n * Allows user to be alerted to any upgrades that are performed for a given\n * component type\n *\n * @param {string} jsClass The class name of the MDL component we wish\n * to hook into for any upgrades performed.\n * @param {function(!HTMLElement)} callback The function to call upon an\n * upgrade. This function should expect 1 parameter - the HTMLElement which\n * got upgraded.\n */\n registerUpgradedCallback: function(jsClass, callback) {},\n /**\n * Registers a class for future use and attempts to upgrade existing DOM.\n *\n * @param {componentHandler.ComponentConfigPublic} config the registration configuration\n */\n register: function(config) {},\n /**\n * Downgrade either a given node, an array of nodes, or a NodeList.\n *\n * @param {!Node|!Array|!NodeList} nodes\n */\n downgradeElements: function(nodes) {}\n};\n\ncomponentHandler = (function() {\n 'use strict';\n\n /** @type {!Array} */\n var registeredComponents_ = [];\n\n /** @type {!Array} */\n var createdComponents_ = [];\n\n var downgradeMethod_ = 'mdlDowngrade';\n var componentConfigProperty_ = 'mdlComponentConfigInternal_';\n\n /**\n * Searches registered components for a class we are interested in using.\n * Optionally replaces a match with passed object if specified.\n *\n * @param {string} name The name of a class we want to use.\n * @param {componentHandler.ComponentConfig=} optReplace Optional object to replace match with.\n * @return {!Object|boolean}\n * @private\n */\n function findRegisteredClass_(name, optReplace) {\n for (var i = 0; i < registeredComponents_.length; i++) {\n if (registeredComponents_[i].className === name) {\n if (typeof optReplace !== 'undefined') {\n registeredComponents_[i] = optReplace;\n }\n return registeredComponents_[i];\n }\n }\n return false;\n }\n\n /**\n * Returns an array of the classNames of the upgraded classes on the element.\n *\n * @param {!Element} element The element to fetch data from.\n * @return {!Array}\n * @private\n */\n function getUpgradedListOfElement_(element) {\n var dataUpgraded = element.getAttribute('data-upgraded');\n // Use `['']` as default value to conform the `,name,name...` style.\n return dataUpgraded === null ? [''] : dataUpgraded.split(',');\n }\n\n /**\n * Returns true if the given element has already been upgraded for the given\n * class.\n *\n * @param {!Element} element The element we want to check.\n * @param {string} jsClass The class to check for.\n * @returns {boolean}\n * @private\n */\n function isElementUpgraded_(element, jsClass) {\n var upgradedList = getUpgradedListOfElement_(element);\n return upgradedList.indexOf(jsClass) !== -1;\n }\n\n /**\n * Searches existing DOM for elements of our component type and upgrades them\n * if they have not already been upgraded.\n *\n * @param {string=} optJsClass the programatic name of the element class we\n * need to create a new instance of.\n * @param {string=} optCssClass the name of the CSS class elements of this\n * type will have.\n */\n function upgradeDomInternal(optJsClass, optCssClass) {\n if (typeof optJsClass === 'undefined' &&\n typeof optCssClass === 'undefined') {\n for (var i = 0; i < registeredComponents_.length; i++) {\n upgradeDomInternal(registeredComponents_[i].className,\n registeredComponents_[i].cssClass);\n }\n } else {\n var jsClass = /** @type {string} */ (optJsClass);\n if (typeof optCssClass === 'undefined') {\n var registeredClass = findRegisteredClass_(jsClass);\n if (registeredClass) {\n optCssClass = registeredClass.cssClass;\n }\n }\n\n var elements = document.querySelectorAll('.' + optCssClass);\n for (var n = 0; n < elements.length; n++) {\n upgradeElementInternal(elements[n], jsClass);\n }\n }\n }\n\n /**\n * Upgrades a specific element rather than all in the DOM.\n *\n * @param {!Element} element The element we wish to upgrade.\n * @param {string=} optJsClass Optional name of the class we want to upgrade\n * the element to.\n */\n function upgradeElementInternal(element, optJsClass) {\n // Verify argument type.\n if (!(typeof element === 'object' && element instanceof Element)) {\n throw new Error('Invalid argument provided to upgrade MDL element.');\n }\n var upgradedList = getUpgradedListOfElement_(element);\n var classesToUpgrade = [];\n // If jsClass is not provided scan the registered components to find the\n // ones matching the element's CSS classList.\n if (!optJsClass) {\n var classList = element.classList;\n registeredComponents_.forEach(function(component) {\n // Match CSS & Not to be upgraded & Not upgraded.\n if (classList.contains(component.cssClass) &&\n classesToUpgrade.indexOf(component) === -1 &&\n !isElementUpgraded_(element, component.className)) {\n classesToUpgrade.push(component);\n }\n });\n } else if (!isElementUpgraded_(element, optJsClass)) {\n classesToUpgrade.push(findRegisteredClass_(optJsClass));\n }\n\n // Upgrade the element for each classes.\n for (var i = 0, n = classesToUpgrade.length, registeredClass; i < n; i++) {\n registeredClass = classesToUpgrade[i];\n if (registeredClass) {\n // Mark element as upgraded.\n upgradedList.push(registeredClass.className);\n element.setAttribute('data-upgraded', upgradedList.join(','));\n var instance = new registeredClass.classConstructor(element);\n instance[componentConfigProperty_] = registeredClass;\n createdComponents_.push(instance);\n // Call any callbacks the user has registered with this component type.\n for (var j = 0, m = registeredClass.callbacks.length; j < m; j++) {\n registeredClass.callbacks[j](element);\n }\n\n if (registeredClass.widget) {\n // Assign per element instance for control over API\n element[registeredClass.className] = instance;\n }\n } else {\n throw new Error(\n 'Unable to find a registered component for the given class.');\n }\n\n var ev = document.createEvent('Events');\n ev.initEvent('mdl-componentupgraded', true, true);\n element.dispatchEvent(ev);\n }\n }\n\n /**\n * Upgrades a specific list of elements rather than all in the DOM.\n *\n * @param {!Element|!Array|!NodeList|!HTMLCollection} elements\n * The elements we wish to upgrade.\n */\n function upgradeElementsInternal(elements) {\n if (!Array.isArray(elements)) {\n if (typeof elements.item === 'function') {\n elements = Array.prototype.slice.call(/** @type {Array} */ (elements));\n } else {\n elements = [elements];\n }\n }\n for (var i = 0, n = elements.length, element; i < n; i++) {\n element = elements[i];\n if (element instanceof HTMLElement) {\n upgradeElementInternal(element);\n if (element.children.length > 0) {\n upgradeElementsInternal(element.children);\n }\n }\n }\n }\n\n /**\n * Registers a class for future use and attempts to upgrade existing DOM.\n *\n * @param {componentHandler.ComponentConfigPublic} config\n */\n function registerInternal(config) {\n // In order to support both Closure-compiled and uncompiled code accessing\n // this method, we need to allow for both the dot and array syntax for\n // property access. You'll therefore see the `foo.bar || foo['bar']`\n // pattern repeated across this method.\n var widgetMissing = (typeof config.widget === 'undefined' &&\n typeof config['widget'] === 'undefined');\n var widget = true;\n\n if (!widgetMissing) {\n widget = config.widget || config['widget'];\n }\n\n var newConfig = /** @type {componentHandler.ComponentConfig} */ ({\n classConstructor: config.constructor || config['constructor'],\n className: config.classAsString || config['classAsString'],\n cssClass: config.cssClass || config['cssClass'],\n widget: widget,\n callbacks: []\n });\n\n registeredComponents_.forEach(function(item) {\n if (item.cssClass === newConfig.cssClass) {\n throw new Error('The provided cssClass has already been registered: ' + item.cssClass);\n }\n if (item.className === newConfig.className) {\n throw new Error('The provided className has already been registered');\n }\n });\n\n if (config.constructor.prototype\n .hasOwnProperty(componentConfigProperty_)) {\n throw new Error(\n 'MDL component classes must not have ' + componentConfigProperty_ +\n ' defined as a property.');\n }\n\n var found = findRegisteredClass_(config.classAsString, newConfig);\n\n if (!found) {\n registeredComponents_.push(newConfig);\n }\n }\n\n /**\n * Allows user to be alerted to any upgrades that are performed for a given\n * component type\n *\n * @param {string} jsClass The class name of the MDL component we wish\n * to hook into for any upgrades performed.\n * @param {function(!HTMLElement)} callback The function to call upon an\n * upgrade. This function should expect 1 parameter - the HTMLElement which\n * got upgraded.\n */\n function registerUpgradedCallbackInternal(jsClass, callback) {\n var regClass = findRegisteredClass_(jsClass);\n if (regClass) {\n regClass.callbacks.push(callback);\n }\n }\n\n /**\n * Upgrades all registered components found in the current DOM. This is\n * automatically called on window load.\n */\n function upgradeAllRegisteredInternal() {\n for (var n = 0; n < registeredComponents_.length; n++) {\n upgradeDomInternal(registeredComponents_[n].className);\n }\n }\n\n /**\n * Finds a created component by a given DOM node.\n *\n * @param {!Node} node\n * @return {*}\n */\n function findCreatedComponentByNodeInternal(node) {\n for (var n = 0; n < createdComponents_.length; n++) {\n var component = createdComponents_[n];\n if (component.element_ === node) {\n return component;\n }\n }\n }\n\n /**\n * Check the component for the downgrade method.\n * Execute if found.\n * Remove component from createdComponents list.\n *\n * @param {*} component\n */\n function deconstructComponentInternal(component) {\n if (component &&\n component[componentConfigProperty_]\n .classConstructor.prototype\n .hasOwnProperty(downgradeMethod_)) {\n component[downgradeMethod_]();\n var componentIndex = createdComponents_.indexOf(component);\n createdComponents_.splice(componentIndex, 1);\n\n var upgrades = component.element_.getAttribute('data-upgraded').split(',');\n var componentPlace = upgrades.indexOf(\n component[componentConfigProperty_].classAsString);\n upgrades.splice(componentPlace, 1);\n component.element_.setAttribute('data-upgraded', upgrades.join(','));\n\n var ev = document.createEvent('Events');\n ev.initEvent('mdl-componentdowngraded', true, true);\n component.element_.dispatchEvent(ev);\n }\n }\n\n /**\n * Downgrade either a given node, an array of nodes, or a NodeList.\n *\n * @param {!Node|!Array|!NodeList} nodes\n */\n function downgradeNodesInternal(nodes) {\n /**\n * Auxiliary function to downgrade a single node.\n * @param {!Node} node the node to be downgraded\n */\n var downgradeNode = function(node) {\n deconstructComponentInternal(findCreatedComponentByNodeInternal(node));\n };\n if (nodes instanceof Array || nodes instanceof NodeList) {\n for (var n = 0; n < nodes.length; n++) {\n downgradeNode(nodes[n]);\n }\n } else if (nodes instanceof Node) {\n downgradeNode(nodes);\n } else {\n throw new Error('Invalid argument provided to downgrade MDL nodes.');\n }\n }\n\n // Now return the functions that should be made public with their publicly\n // facing names...\n return {\n upgradeDom: upgradeDomInternal,\n upgradeElement: upgradeElementInternal,\n upgradeElements: upgradeElementsInternal,\n upgradeAllRegistered: upgradeAllRegisteredInternal,\n registerUpgradedCallback: registerUpgradedCallbackInternal,\n register: registerInternal,\n downgradeElements: downgradeNodesInternal\n };\n})();\n\n/**\n * Describes the type of a registered component type managed by\n * componentHandler. Provided for benefit of the Closure compiler.\n *\n * @typedef {{\n * constructor: Function,\n * classAsString: string,\n * cssClass: string,\n * widget: (string|boolean|undefined)\n * }}\n */\ncomponentHandler.ComponentConfigPublic; // jshint ignore:line\n\n/**\n * Describes the type of a registered component type managed by\n * componentHandler. Provided for benefit of the Closure compiler.\n *\n * @typedef {{\n * constructor: !Function,\n * className: string,\n * cssClass: string,\n * widget: (string|boolean),\n * callbacks: !Array\n * }}\n */\ncomponentHandler.ComponentConfig; // jshint ignore:line\n\n/**\n * Created component (i.e., upgraded element) type as managed by\n * componentHandler. Provided for benefit of the Closure compiler.\n *\n * @typedef {{\n * element_: !HTMLElement,\n * className: string,\n * classAsString: string,\n * cssClass: string,\n * widget: string\n * }}\n */\ncomponentHandler.Component; // jshint ignore:line\n\n// Export all symbols, for the benefit of Closure compiler.\n// No effect on uncompiled code.\ncomponentHandler['upgradeDom'] = componentHandler.upgradeDom;\ncomponentHandler['upgradeElement'] = componentHandler.upgradeElement;\ncomponentHandler['upgradeElements'] = componentHandler.upgradeElements;\ncomponentHandler['upgradeAllRegistered'] =\n componentHandler.upgradeAllRegistered;\ncomponentHandler['registerUpgradedCallback'] =\n componentHandler.registerUpgradedCallback;\ncomponentHandler['register'] = componentHandler.register;\ncomponentHandler['downgradeElements'] = componentHandler.downgradeElements;\nwindow.componentHandler = componentHandler;\nwindow['componentHandler'] = componentHandler;\n\nwindow.addEventListener('load', function() {\n 'use strict';\n\n /**\n * Performs a \"Cutting the mustard\" test. If the browser supports the features\n * tested, adds a mdl-js class to the element. It then upgrades all MDL\n * components requiring JavaScript.\n */\n if ('classList' in document.createElement('div') &&\n 'querySelector' in document &&\n 'addEventListener' in window && Array.prototype.forEach) {\n document.documentElement.classList.add('mdl-js');\n componentHandler.upgradeAllRegistered();\n } else {\n /**\n * Dummy function to avoid JS errors.\n */\n componentHandler.upgradeElement = function() {};\n /**\n * Dummy function to avoid JS errors.\n */\n componentHandler.register = function() {};\n }\n});\n\n// Source: https://github.com/darius/requestAnimationFrame/blob/master/requestAnimationFrame.js\n// Adapted from https://gist.github.com/paulirish/1579671 which derived from\n// http://paulirish.com/2011/requestanimationframe-for-smart-animating/\n// http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating\n// requestAnimationFrame polyfill by Erik Möller.\n// Fixes from Paul Irish, Tino Zijdel, Andrew Mao, Klemen Slavič, Darius Bacon\n// MIT license\nif (!Date.now) {\n /**\n * Date.now polyfill.\n * @return {number} the current Date\n */\n Date.now = function () {\n return new Date().getTime();\n };\n Date['now'] = Date.now;\n}\nvar vendors = [\n 'webkit',\n 'moz'\n];\nfor (var i = 0; i < vendors.length && !window.requestAnimationFrame; ++i) {\n var vp = vendors[i];\n window.requestAnimationFrame = window[vp + 'RequestAnimationFrame'];\n window.cancelAnimationFrame = window[vp + 'CancelAnimationFrame'] || window[vp + 'CancelRequestAnimationFrame'];\n window['requestAnimationFrame'] = window.requestAnimationFrame;\n window['cancelAnimationFrame'] = window.cancelAnimationFrame;\n}\nif (/iP(ad|hone|od).*OS 6/.test(window.navigator.userAgent) || !window.requestAnimationFrame || !window.cancelAnimationFrame) {\n var lastTime = 0;\n /**\n * requestAnimationFrame polyfill.\n * @param {!Function} callback the callback function.\n */\n window.requestAnimationFrame = function (callback) {\n var now = Date.now();\n var nextTime = Math.max(lastTime + 16, now);\n return setTimeout(function () {\n callback(lastTime = nextTime);\n }, nextTime - now);\n };\n window.cancelAnimationFrame = clearTimeout;\n window['requestAnimationFrame'] = window.requestAnimationFrame;\n window['cancelAnimationFrame'] = window.cancelAnimationFrame;\n}\n/**\n * @license\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Class constructor for Button MDL component.\n * Implements MDL component design pattern defined at:\n * https://github.com/jasonmayes/mdl-component-design-pattern\n *\n * @param {HTMLElement} element The element that will be upgraded.\n */\nvar MaterialButton = function MaterialButton(element) {\n this.element_ = element;\n // Initialize instance.\n this.init();\n};\nwindow['MaterialButton'] = MaterialButton;\n/**\n * Store constants in one place so they can be updated easily.\n *\n * @enum {string | number}\n * @private\n */\nMaterialButton.prototype.Constant_ = {};\n/**\n * Store strings for class names defined by this component that are used in\n * JavaScript. This allows us to simply change it in one place should we\n * decide to modify at a later date.\n *\n * @enum {string}\n * @private\n */\nMaterialButton.prototype.CssClasses_ = {\n RIPPLE_EFFECT: 'mdl-js-ripple-effect',\n RIPPLE_CONTAINER: 'mdl-button__ripple-container',\n RIPPLE: 'mdl-ripple'\n};\n/**\n * Handle blur of element.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialButton.prototype.blurHandler_ = function (event) {\n if (event) {\n this.element_.blur();\n }\n};\n// Public methods.\n/**\n * Disable button.\n *\n * @public\n */\nMaterialButton.prototype.disable = function () {\n this.element_.disabled = true;\n};\nMaterialButton.prototype['disable'] = MaterialButton.prototype.disable;\n/**\n * Enable button.\n *\n * @public\n */\nMaterialButton.prototype.enable = function () {\n this.element_.disabled = false;\n};\nMaterialButton.prototype['enable'] = MaterialButton.prototype.enable;\n/**\n * Initialize element.\n */\nMaterialButton.prototype.init = function () {\n if (this.element_) {\n if (this.element_.classList.contains(this.CssClasses_.RIPPLE_EFFECT)) {\n var rippleContainer = document.createElement('span');\n rippleContainer.classList.add(this.CssClasses_.RIPPLE_CONTAINER);\n this.rippleElement_ = document.createElement('span');\n this.rippleElement_.classList.add(this.CssClasses_.RIPPLE);\n rippleContainer.appendChild(this.rippleElement_);\n this.boundRippleBlurHandler = this.blurHandler_.bind(this);\n this.rippleElement_.addEventListener('mouseup', this.boundRippleBlurHandler);\n this.element_.appendChild(rippleContainer);\n }\n this.boundButtonBlurHandler = this.blurHandler_.bind(this);\n this.element_.addEventListener('mouseup', this.boundButtonBlurHandler);\n this.element_.addEventListener('mouseleave', this.boundButtonBlurHandler);\n }\n};\n/**\n * Downgrade the element.\n *\n * @private\n */\nMaterialButton.prototype.mdlDowngrade_ = function () {\n if (this.rippleElement_) {\n this.rippleElement_.removeEventListener('mouseup', this.boundRippleBlurHandler);\n }\n this.element_.removeEventListener('mouseup', this.boundButtonBlurHandler);\n this.element_.removeEventListener('mouseleave', this.boundButtonBlurHandler);\n};\n/**\n * Public alias for the downgrade method.\n *\n * @public\n */\nMaterialButton.prototype.mdlDowngrade = MaterialButton.prototype.mdlDowngrade_;\nMaterialButton.prototype['mdlDowngrade'] = MaterialButton.prototype.mdlDowngrade;\n// The component registers itself. It can assume componentHandler is available\n// in the global scope.\ncomponentHandler.register({\n constructor: MaterialButton,\n classAsString: 'MaterialButton',\n cssClass: 'mdl-js-button',\n widget: true\n});\n/**\n * @license\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Class constructor for Checkbox MDL component.\n * Implements MDL component design pattern defined at:\n * https://github.com/jasonmayes/mdl-component-design-pattern\n *\n * @constructor\n * @param {HTMLElement} element The element that will be upgraded.\n */\nvar MaterialCheckbox = function MaterialCheckbox(element) {\n this.element_ = element;\n // Initialize instance.\n this.init();\n};\nwindow['MaterialCheckbox'] = MaterialCheckbox;\n/**\n * Store constants in one place so they can be updated easily.\n *\n * @enum {string | number}\n * @private\n */\nMaterialCheckbox.prototype.Constant_ = { TINY_TIMEOUT: 0.001 };\n/**\n * Store strings for class names defined by this component that are used in\n * JavaScript. This allows us to simply change it in one place should we\n * decide to modify at a later date.\n *\n * @enum {string}\n * @private\n */\nMaterialCheckbox.prototype.CssClasses_ = {\n INPUT: 'mdl-checkbox__input',\n BOX_OUTLINE: 'mdl-checkbox__box-outline',\n FOCUS_HELPER: 'mdl-checkbox__focus-helper',\n TICK_OUTLINE: 'mdl-checkbox__tick-outline',\n RIPPLE_EFFECT: 'mdl-js-ripple-effect',\n RIPPLE_IGNORE_EVENTS: 'mdl-js-ripple-effect--ignore-events',\n RIPPLE_CONTAINER: 'mdl-checkbox__ripple-container',\n RIPPLE_CENTER: 'mdl-ripple--center',\n RIPPLE: 'mdl-ripple',\n IS_FOCUSED: 'is-focused',\n IS_DISABLED: 'is-disabled',\n IS_CHECKED: 'is-checked',\n IS_UPGRADED: 'is-upgraded'\n};\n/**\n * Handle change of state.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialCheckbox.prototype.onChange_ = function (event) {\n this.updateClasses_();\n};\n/**\n * Handle focus of element.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialCheckbox.prototype.onFocus_ = function (event) {\n this.element_.classList.add(this.CssClasses_.IS_FOCUSED);\n};\n/**\n * Handle lost focus of element.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialCheckbox.prototype.onBlur_ = function (event) {\n this.element_.classList.remove(this.CssClasses_.IS_FOCUSED);\n};\n/**\n * Handle mouseup.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialCheckbox.prototype.onMouseUp_ = function (event) {\n this.blur_();\n};\n/**\n * Handle class updates.\n *\n * @private\n */\nMaterialCheckbox.prototype.updateClasses_ = function () {\n this.checkDisabled();\n this.checkToggleState();\n};\n/**\n * Add blur.\n *\n * @private\n */\nMaterialCheckbox.prototype.blur_ = function () {\n // TODO: figure out why there's a focus event being fired after our blur,\n // so that we can avoid this hack.\n window.setTimeout(function () {\n this.inputElement_.blur();\n }.bind(this), this.Constant_.TINY_TIMEOUT);\n};\n// Public methods.\n/**\n * Check the inputs toggle state and update display.\n *\n * @public\n */\nMaterialCheckbox.prototype.checkToggleState = function () {\n if (this.inputElement_.checked) {\n this.element_.classList.add(this.CssClasses_.IS_CHECKED);\n } else {\n this.element_.classList.remove(this.CssClasses_.IS_CHECKED);\n }\n};\nMaterialCheckbox.prototype['checkToggleState'] = MaterialCheckbox.prototype.checkToggleState;\n/**\n * Check the inputs disabled state and update display.\n *\n * @public\n */\nMaterialCheckbox.prototype.checkDisabled = function () {\n if (this.inputElement_.disabled) {\n this.element_.classList.add(this.CssClasses_.IS_DISABLED);\n } else {\n this.element_.classList.remove(this.CssClasses_.IS_DISABLED);\n }\n};\nMaterialCheckbox.prototype['checkDisabled'] = MaterialCheckbox.prototype.checkDisabled;\n/**\n * Disable checkbox.\n *\n * @public\n */\nMaterialCheckbox.prototype.disable = function () {\n this.inputElement_.disabled = true;\n this.updateClasses_();\n};\nMaterialCheckbox.prototype['disable'] = MaterialCheckbox.prototype.disable;\n/**\n * Enable checkbox.\n *\n * @public\n */\nMaterialCheckbox.prototype.enable = function () {\n this.inputElement_.disabled = false;\n this.updateClasses_();\n};\nMaterialCheckbox.prototype['enable'] = MaterialCheckbox.prototype.enable;\n/**\n * Check checkbox.\n *\n * @public\n */\nMaterialCheckbox.prototype.check = function () {\n this.inputElement_.checked = true;\n this.updateClasses_();\n};\nMaterialCheckbox.prototype['check'] = MaterialCheckbox.prototype.check;\n/**\n * Uncheck checkbox.\n *\n * @public\n */\nMaterialCheckbox.prototype.uncheck = function () {\n this.inputElement_.checked = false;\n this.updateClasses_();\n};\nMaterialCheckbox.prototype['uncheck'] = MaterialCheckbox.prototype.uncheck;\n/**\n * Initialize element.\n */\nMaterialCheckbox.prototype.init = function () {\n if (this.element_) {\n this.inputElement_ = this.element_.querySelector('.' + this.CssClasses_.INPUT);\n var boxOutline = document.createElement('span');\n boxOutline.classList.add(this.CssClasses_.BOX_OUTLINE);\n var tickContainer = document.createElement('span');\n tickContainer.classList.add(this.CssClasses_.FOCUS_HELPER);\n var tickOutline = document.createElement('span');\n tickOutline.classList.add(this.CssClasses_.TICK_OUTLINE);\n boxOutline.appendChild(tickOutline);\n this.element_.appendChild(tickContainer);\n this.element_.appendChild(boxOutline);\n if (this.element_.classList.contains(this.CssClasses_.RIPPLE_EFFECT)) {\n this.element_.classList.add(this.CssClasses_.RIPPLE_IGNORE_EVENTS);\n this.rippleContainerElement_ = document.createElement('span');\n this.rippleContainerElement_.classList.add(this.CssClasses_.RIPPLE_CONTAINER);\n this.rippleContainerElement_.classList.add(this.CssClasses_.RIPPLE_EFFECT);\n this.rippleContainerElement_.classList.add(this.CssClasses_.RIPPLE_CENTER);\n this.boundRippleMouseUp = this.onMouseUp_.bind(this);\n this.rippleContainerElement_.addEventListener('mouseup', this.boundRippleMouseUp);\n var ripple = document.createElement('span');\n ripple.classList.add(this.CssClasses_.RIPPLE);\n this.rippleContainerElement_.appendChild(ripple);\n this.element_.appendChild(this.rippleContainerElement_);\n }\n this.boundInputOnChange = this.onChange_.bind(this);\n this.boundInputOnFocus = this.onFocus_.bind(this);\n this.boundInputOnBlur = this.onBlur_.bind(this);\n this.boundElementMouseUp = this.onMouseUp_.bind(this);\n this.inputElement_.addEventListener('change', this.boundInputOnChange);\n this.inputElement_.addEventListener('focus', this.boundInputOnFocus);\n this.inputElement_.addEventListener('blur', this.boundInputOnBlur);\n this.element_.addEventListener('mouseup', this.boundElementMouseUp);\n this.updateClasses_();\n this.element_.classList.add(this.CssClasses_.IS_UPGRADED);\n }\n};\n/**\n * Downgrade the component.\n *\n * @private\n */\nMaterialCheckbox.prototype.mdlDowngrade_ = function () {\n if (this.rippleContainerElement_) {\n this.rippleContainerElement_.removeEventListener('mouseup', this.boundRippleMouseUp);\n }\n this.inputElement_.removeEventListener('change', this.boundInputOnChange);\n this.inputElement_.removeEventListener('focus', this.boundInputOnFocus);\n this.inputElement_.removeEventListener('blur', this.boundInputOnBlur);\n this.element_.removeEventListener('mouseup', this.boundElementMouseUp);\n};\n/**\n * Public alias for the downgrade method.\n *\n * @public\n */\nMaterialCheckbox.prototype.mdlDowngrade = MaterialCheckbox.prototype.mdlDowngrade_;\nMaterialCheckbox.prototype['mdlDowngrade'] = MaterialCheckbox.prototype.mdlDowngrade;\n// The component registers itself. It can assume componentHandler is available\n// in the global scope.\ncomponentHandler.register({\n constructor: MaterialCheckbox,\n classAsString: 'MaterialCheckbox',\n cssClass: 'mdl-js-checkbox',\n widget: true\n});\n/**\n * @license\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Class constructor for icon toggle MDL component.\n * Implements MDL component design pattern defined at:\n * https://github.com/jasonmayes/mdl-component-design-pattern\n *\n * @constructor\n * @param {HTMLElement} element The element that will be upgraded.\n */\nvar MaterialIconToggle = function MaterialIconToggle(element) {\n this.element_ = element;\n // Initialize instance.\n this.init();\n};\nwindow['MaterialIconToggle'] = MaterialIconToggle;\n/**\n * Store constants in one place so they can be updated easily.\n *\n * @enum {string | number}\n * @private\n */\nMaterialIconToggle.prototype.Constant_ = { TINY_TIMEOUT: 0.001 };\n/**\n * Store strings for class names defined by this component that are used in\n * JavaScript. This allows us to simply change it in one place should we\n * decide to modify at a later date.\n *\n * @enum {string}\n * @private\n */\nMaterialIconToggle.prototype.CssClasses_ = {\n INPUT: 'mdl-icon-toggle__input',\n JS_RIPPLE_EFFECT: 'mdl-js-ripple-effect',\n RIPPLE_IGNORE_EVENTS: 'mdl-js-ripple-effect--ignore-events',\n RIPPLE_CONTAINER: 'mdl-icon-toggle__ripple-container',\n RIPPLE_CENTER: 'mdl-ripple--center',\n RIPPLE: 'mdl-ripple',\n IS_FOCUSED: 'is-focused',\n IS_DISABLED: 'is-disabled',\n IS_CHECKED: 'is-checked'\n};\n/**\n * Handle change of state.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialIconToggle.prototype.onChange_ = function (event) {\n this.updateClasses_();\n};\n/**\n * Handle focus of element.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialIconToggle.prototype.onFocus_ = function (event) {\n this.element_.classList.add(this.CssClasses_.IS_FOCUSED);\n};\n/**\n * Handle lost focus of element.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialIconToggle.prototype.onBlur_ = function (event) {\n this.element_.classList.remove(this.CssClasses_.IS_FOCUSED);\n};\n/**\n * Handle mouseup.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialIconToggle.prototype.onMouseUp_ = function (event) {\n this.blur_();\n};\n/**\n * Handle class updates.\n *\n * @private\n */\nMaterialIconToggle.prototype.updateClasses_ = function () {\n this.checkDisabled();\n this.checkToggleState();\n};\n/**\n * Add blur.\n *\n * @private\n */\nMaterialIconToggle.prototype.blur_ = function () {\n // TODO: figure out why there's a focus event being fired after our blur,\n // so that we can avoid this hack.\n window.setTimeout(function () {\n this.inputElement_.blur();\n }.bind(this), this.Constant_.TINY_TIMEOUT);\n};\n// Public methods.\n/**\n * Check the inputs toggle state and update display.\n *\n * @public\n */\nMaterialIconToggle.prototype.checkToggleState = function () {\n if (this.inputElement_.checked) {\n this.element_.classList.add(this.CssClasses_.IS_CHECKED);\n } else {\n this.element_.classList.remove(this.CssClasses_.IS_CHECKED);\n }\n};\nMaterialIconToggle.prototype['checkToggleState'] = MaterialIconToggle.prototype.checkToggleState;\n/**\n * Check the inputs disabled state and update display.\n *\n * @public\n */\nMaterialIconToggle.prototype.checkDisabled = function () {\n if (this.inputElement_.disabled) {\n this.element_.classList.add(this.CssClasses_.IS_DISABLED);\n } else {\n this.element_.classList.remove(this.CssClasses_.IS_DISABLED);\n }\n};\nMaterialIconToggle.prototype['checkDisabled'] = MaterialIconToggle.prototype.checkDisabled;\n/**\n * Disable icon toggle.\n *\n * @public\n */\nMaterialIconToggle.prototype.disable = function () {\n this.inputElement_.disabled = true;\n this.updateClasses_();\n};\nMaterialIconToggle.prototype['disable'] = MaterialIconToggle.prototype.disable;\n/**\n * Enable icon toggle.\n *\n * @public\n */\nMaterialIconToggle.prototype.enable = function () {\n this.inputElement_.disabled = false;\n this.updateClasses_();\n};\nMaterialIconToggle.prototype['enable'] = MaterialIconToggle.prototype.enable;\n/**\n * Check icon toggle.\n *\n * @public\n */\nMaterialIconToggle.prototype.check = function () {\n this.inputElement_.checked = true;\n this.updateClasses_();\n};\nMaterialIconToggle.prototype['check'] = MaterialIconToggle.prototype.check;\n/**\n * Uncheck icon toggle.\n *\n * @public\n */\nMaterialIconToggle.prototype.uncheck = function () {\n this.inputElement_.checked = false;\n this.updateClasses_();\n};\nMaterialIconToggle.prototype['uncheck'] = MaterialIconToggle.prototype.uncheck;\n/**\n * Initialize element.\n */\nMaterialIconToggle.prototype.init = function () {\n if (this.element_) {\n this.inputElement_ = this.element_.querySelector('.' + this.CssClasses_.INPUT);\n if (this.element_.classList.contains(this.CssClasses_.JS_RIPPLE_EFFECT)) {\n this.element_.classList.add(this.CssClasses_.RIPPLE_IGNORE_EVENTS);\n this.rippleContainerElement_ = document.createElement('span');\n this.rippleContainerElement_.classList.add(this.CssClasses_.RIPPLE_CONTAINER);\n this.rippleContainerElement_.classList.add(this.CssClasses_.JS_RIPPLE_EFFECT);\n this.rippleContainerElement_.classList.add(this.CssClasses_.RIPPLE_CENTER);\n this.boundRippleMouseUp = this.onMouseUp_.bind(this);\n this.rippleContainerElement_.addEventListener('mouseup', this.boundRippleMouseUp);\n var ripple = document.createElement('span');\n ripple.classList.add(this.CssClasses_.RIPPLE);\n this.rippleContainerElement_.appendChild(ripple);\n this.element_.appendChild(this.rippleContainerElement_);\n }\n this.boundInputOnChange = this.onChange_.bind(this);\n this.boundInputOnFocus = this.onFocus_.bind(this);\n this.boundInputOnBlur = this.onBlur_.bind(this);\n this.boundElementOnMouseUp = this.onMouseUp_.bind(this);\n this.inputElement_.addEventListener('change', this.boundInputOnChange);\n this.inputElement_.addEventListener('focus', this.boundInputOnFocus);\n this.inputElement_.addEventListener('blur', this.boundInputOnBlur);\n this.element_.addEventListener('mouseup', this.boundElementOnMouseUp);\n this.updateClasses_();\n this.element_.classList.add('is-upgraded');\n }\n};\n/**\n * Downgrade the component\n *\n * @private\n */\nMaterialIconToggle.prototype.mdlDowngrade_ = function () {\n if (this.rippleContainerElement_) {\n this.rippleContainerElement_.removeEventListener('mouseup', this.boundRippleMouseUp);\n }\n this.inputElement_.removeEventListener('change', this.boundInputOnChange);\n this.inputElement_.removeEventListener('focus', this.boundInputOnFocus);\n this.inputElement_.removeEventListener('blur', this.boundInputOnBlur);\n this.element_.removeEventListener('mouseup', this.boundElementOnMouseUp);\n};\n/**\n * Public alias for the downgrade method.\n *\n * @public\n */\nMaterialIconToggle.prototype.mdlDowngrade = MaterialIconToggle.prototype.mdlDowngrade_;\nMaterialIconToggle.prototype['mdlDowngrade'] = MaterialIconToggle.prototype.mdlDowngrade;\n// The component registers itself. It can assume componentHandler is available\n// in the global scope.\ncomponentHandler.register({\n constructor: MaterialIconToggle,\n classAsString: 'MaterialIconToggle',\n cssClass: 'mdl-js-icon-toggle',\n widget: true\n});\n/**\n * @license\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Class constructor for dropdown MDL component.\n * Implements MDL component design pattern defined at:\n * https://github.com/jasonmayes/mdl-component-design-pattern\n *\n * @constructor\n * @param {HTMLElement} element The element that will be upgraded.\n */\nvar MaterialMenu = function MaterialMenu(element) {\n this.element_ = element;\n // Initialize instance.\n this.init();\n};\nwindow['MaterialMenu'] = MaterialMenu;\n/**\n * Store constants in one place so they can be updated easily.\n *\n * @enum {string | number}\n * @private\n */\nMaterialMenu.prototype.Constant_ = {\n // Total duration of the menu animation.\n TRANSITION_DURATION_SECONDS: 0.3,\n // The fraction of the total duration we want to use for menu item animations.\n TRANSITION_DURATION_FRACTION: 0.8,\n // How long the menu stays open after choosing an option (so the user can see\n // the ripple).\n CLOSE_TIMEOUT: 150\n};\n/**\n * Keycodes, for code readability.\n *\n * @enum {number}\n * @private\n */\nMaterialMenu.prototype.Keycodes_ = {\n ENTER: 13,\n ESCAPE: 27,\n SPACE: 32,\n UP_ARROW: 38,\n DOWN_ARROW: 40\n};\n/**\n * Store strings for class names defined by this component that are used in\n * JavaScript. This allows us to simply change it in one place should we\n * decide to modify at a later date.\n *\n * @enum {string}\n * @private\n */\nMaterialMenu.prototype.CssClasses_ = {\n CONTAINER: 'mdl-menu__container',\n OUTLINE: 'mdl-menu__outline',\n ITEM: 'mdl-menu__item',\n ITEM_RIPPLE_CONTAINER: 'mdl-menu__item-ripple-container',\n RIPPLE_EFFECT: 'mdl-js-ripple-effect',\n RIPPLE_IGNORE_EVENTS: 'mdl-js-ripple-effect--ignore-events',\n RIPPLE: 'mdl-ripple',\n // Statuses\n IS_UPGRADED: 'is-upgraded',\n IS_VISIBLE: 'is-visible',\n IS_ANIMATING: 'is-animating',\n // Alignment options\n BOTTOM_LEFT: 'mdl-menu--bottom-left',\n // This is the default.\n BOTTOM_RIGHT: 'mdl-menu--bottom-right',\n TOP_LEFT: 'mdl-menu--top-left',\n TOP_RIGHT: 'mdl-menu--top-right',\n UNALIGNED: 'mdl-menu--unaligned'\n};\n/**\n * Initialize element.\n */\nMaterialMenu.prototype.init = function () {\n if (this.element_) {\n // Create container for the menu.\n var container = document.createElement('div');\n container.classList.add(this.CssClasses_.CONTAINER);\n this.element_.parentElement.insertBefore(container, this.element_);\n this.element_.parentElement.removeChild(this.element_);\n container.appendChild(this.element_);\n this.container_ = container;\n // Create outline for the menu (shadow and background).\n var outline = document.createElement('div');\n outline.classList.add(this.CssClasses_.OUTLINE);\n this.outline_ = outline;\n container.insertBefore(outline, this.element_);\n // Find the \"for\" element and bind events to it.\n var forElId = this.element_.getAttribute('for');\n var forEl = null;\n if (forElId) {\n forEl = document.getElementById(forElId);\n if (forEl) {\n this.forElement_ = forEl;\n forEl.addEventListener('click', this.handleForClick_.bind(this));\n forEl.addEventListener('keydown', this.handleForKeyboardEvent_.bind(this));\n }\n }\n var items = this.element_.querySelectorAll('.' + this.CssClasses_.ITEM);\n this.boundItemKeydown_ = this.handleItemKeyboardEvent_.bind(this);\n this.boundItemClick_ = this.handleItemClick_.bind(this);\n for (var i = 0; i < items.length; i++) {\n // Add a listener to each menu item.\n items[i].addEventListener('click', this.boundItemClick_);\n // Add a tab index to each menu item.\n items[i].tabIndex = '-1';\n // Add a keyboard listener to each menu item.\n items[i].addEventListener('keydown', this.boundItemKeydown_);\n }\n // Add ripple classes to each item, if the user has enabled ripples.\n if (this.element_.classList.contains(this.CssClasses_.RIPPLE_EFFECT)) {\n this.element_.classList.add(this.CssClasses_.RIPPLE_IGNORE_EVENTS);\n for (i = 0; i < items.length; i++) {\n var item = items[i];\n var rippleContainer = document.createElement('span');\n rippleContainer.classList.add(this.CssClasses_.ITEM_RIPPLE_CONTAINER);\n var ripple = document.createElement('span');\n ripple.classList.add(this.CssClasses_.RIPPLE);\n rippleContainer.appendChild(ripple);\n item.appendChild(rippleContainer);\n item.classList.add(this.CssClasses_.RIPPLE_EFFECT);\n }\n }\n // Copy alignment classes to the container, so the outline can use them.\n if (this.element_.classList.contains(this.CssClasses_.BOTTOM_LEFT)) {\n this.outline_.classList.add(this.CssClasses_.BOTTOM_LEFT);\n }\n if (this.element_.classList.contains(this.CssClasses_.BOTTOM_RIGHT)) {\n this.outline_.classList.add(this.CssClasses_.BOTTOM_RIGHT);\n }\n if (this.element_.classList.contains(this.CssClasses_.TOP_LEFT)) {\n this.outline_.classList.add(this.CssClasses_.TOP_LEFT);\n }\n if (this.element_.classList.contains(this.CssClasses_.TOP_RIGHT)) {\n this.outline_.classList.add(this.CssClasses_.TOP_RIGHT);\n }\n if (this.element_.classList.contains(this.CssClasses_.UNALIGNED)) {\n this.outline_.classList.add(this.CssClasses_.UNALIGNED);\n }\n container.classList.add(this.CssClasses_.IS_UPGRADED);\n }\n};\n/**\n * Handles a click on the \"for\" element, by positioning the menu and then\n * toggling it.\n *\n * @param {Event} evt The event that fired.\n * @private\n */\nMaterialMenu.prototype.handleForClick_ = function (evt) {\n if (this.element_ && this.forElement_) {\n var rect = this.forElement_.getBoundingClientRect();\n var forRect = this.forElement_.parentElement.getBoundingClientRect();\n if (this.element_.classList.contains(this.CssClasses_.UNALIGNED)) {\n } else if (this.element_.classList.contains(this.CssClasses_.BOTTOM_RIGHT)) {\n // Position below the \"for\" element, aligned to its right.\n this.container_.style.right = forRect.right - rect.right + 'px';\n this.container_.style.top = this.forElement_.offsetTop + this.forElement_.offsetHeight + 'px';\n } else if (this.element_.classList.contains(this.CssClasses_.TOP_LEFT)) {\n // Position above the \"for\" element, aligned to its left.\n this.container_.style.left = this.forElement_.offsetLeft + 'px';\n this.container_.style.bottom = forRect.bottom - rect.top + 'px';\n } else if (this.element_.classList.contains(this.CssClasses_.TOP_RIGHT)) {\n // Position above the \"for\" element, aligned to its right.\n this.container_.style.right = forRect.right - rect.right + 'px';\n this.container_.style.bottom = forRect.bottom - rect.top + 'px';\n } else {\n // Default: position below the \"for\" element, aligned to its left.\n this.container_.style.left = this.forElement_.offsetLeft + 'px';\n this.container_.style.top = this.forElement_.offsetTop + this.forElement_.offsetHeight + 'px';\n }\n }\n this.toggle(evt);\n};\n/**\n * Handles a keyboard event on the \"for\" element.\n *\n * @param {Event} evt The event that fired.\n * @private\n */\nMaterialMenu.prototype.handleForKeyboardEvent_ = function (evt) {\n if (this.element_ && this.container_ && this.forElement_) {\n var items = this.element_.querySelectorAll('.' + this.CssClasses_.ITEM + ':not([disabled])');\n if (items && items.length > 0 && this.container_.classList.contains(this.CssClasses_.IS_VISIBLE)) {\n if (evt.keyCode === this.Keycodes_.UP_ARROW) {\n evt.preventDefault();\n items[items.length - 1].focus();\n } else if (evt.keyCode === this.Keycodes_.DOWN_ARROW) {\n evt.preventDefault();\n items[0].focus();\n }\n }\n }\n};\n/**\n * Handles a keyboard event on an item.\n *\n * @param {Event} evt The event that fired.\n * @private\n */\nMaterialMenu.prototype.handleItemKeyboardEvent_ = function (evt) {\n if (this.element_ && this.container_) {\n var items = this.element_.querySelectorAll('.' + this.CssClasses_.ITEM + ':not([disabled])');\n if (items && items.length > 0 && this.container_.classList.contains(this.CssClasses_.IS_VISIBLE)) {\n var currentIndex = Array.prototype.slice.call(items).indexOf(evt.target);\n if (evt.keyCode === this.Keycodes_.UP_ARROW) {\n evt.preventDefault();\n if (currentIndex > 0) {\n items[currentIndex - 1].focus();\n } else {\n items[items.length - 1].focus();\n }\n } else if (evt.keyCode === this.Keycodes_.DOWN_ARROW) {\n evt.preventDefault();\n if (items.length > currentIndex + 1) {\n items[currentIndex + 1].focus();\n } else {\n items[0].focus();\n }\n } else if (evt.keyCode === this.Keycodes_.SPACE || evt.keyCode === this.Keycodes_.ENTER) {\n evt.preventDefault();\n // Send mousedown and mouseup to trigger ripple.\n var e = new MouseEvent('mousedown');\n evt.target.dispatchEvent(e);\n e = new MouseEvent('mouseup');\n evt.target.dispatchEvent(e);\n // Send click.\n evt.target.click();\n } else if (evt.keyCode === this.Keycodes_.ESCAPE) {\n evt.preventDefault();\n this.hide();\n }\n }\n }\n};\n/**\n * Handles a click event on an item.\n *\n * @param {Event} evt The event that fired.\n * @private\n */\nMaterialMenu.prototype.handleItemClick_ = function (evt) {\n if (evt.target.hasAttribute('disabled')) {\n evt.stopPropagation();\n } else {\n // Wait some time before closing menu, so the user can see the ripple.\n this.closing_ = true;\n window.setTimeout(function (evt) {\n this.hide();\n this.closing_ = false;\n }.bind(this), this.Constant_.CLOSE_TIMEOUT);\n }\n};\n/**\n * Calculates the initial clip (for opening the menu) or final clip (for closing\n * it), and applies it. This allows us to animate from or to the correct point,\n * that is, the point it's aligned to in the \"for\" element.\n *\n * @param {number} height Height of the clip rectangle\n * @param {number} width Width of the clip rectangle\n * @private\n */\nMaterialMenu.prototype.applyClip_ = function (height, width) {\n if (this.element_.classList.contains(this.CssClasses_.UNALIGNED)) {\n // Do not clip.\n this.element_.style.clip = '';\n } else if (this.element_.classList.contains(this.CssClasses_.BOTTOM_RIGHT)) {\n // Clip to the top right corner of the menu.\n this.element_.style.clip = 'rect(0 ' + width + 'px ' + '0 ' + width + 'px)';\n } else if (this.element_.classList.contains(this.CssClasses_.TOP_LEFT)) {\n // Clip to the bottom left corner of the menu.\n this.element_.style.clip = 'rect(' + height + 'px 0 ' + height + 'px 0)';\n } else if (this.element_.classList.contains(this.CssClasses_.TOP_RIGHT)) {\n // Clip to the bottom right corner of the menu.\n this.element_.style.clip = 'rect(' + height + 'px ' + width + 'px ' + height + 'px ' + width + 'px)';\n } else {\n // Default: do not clip (same as clipping to the top left corner).\n this.element_.style.clip = '';\n }\n};\n/**\n * Adds an event listener to clean up after the animation ends.\n *\n * @private\n */\nMaterialMenu.prototype.addAnimationEndListener_ = function () {\n var cleanup = function () {\n this.element_.removeEventListener('transitionend', cleanup);\n this.element_.removeEventListener('webkitTransitionEnd', cleanup);\n this.element_.classList.remove(this.CssClasses_.IS_ANIMATING);\n }.bind(this);\n // Remove animation class once the transition is done.\n this.element_.addEventListener('transitionend', cleanup);\n this.element_.addEventListener('webkitTransitionEnd', cleanup);\n};\n/**\n * Displays the menu.\n *\n * @public\n */\nMaterialMenu.prototype.show = function (evt) {\n if (this.element_ && this.container_ && this.outline_) {\n // Measure the inner element.\n var height = this.element_.getBoundingClientRect().height;\n var width = this.element_.getBoundingClientRect().width;\n // Apply the inner element's size to the container and outline.\n this.container_.style.width = width + 'px';\n this.container_.style.height = height + 'px';\n this.outline_.style.width = width + 'px';\n this.outline_.style.height = height + 'px';\n var transitionDuration = this.Constant_.TRANSITION_DURATION_SECONDS * this.Constant_.TRANSITION_DURATION_FRACTION;\n // Calculate transition delays for individual menu items, so that they fade\n // in one at a time.\n var items = this.element_.querySelectorAll('.' + this.CssClasses_.ITEM);\n for (var i = 0; i < items.length; i++) {\n var itemDelay = null;\n if (this.element_.classList.contains(this.CssClasses_.TOP_LEFT) || this.element_.classList.contains(this.CssClasses_.TOP_RIGHT)) {\n itemDelay = (height - items[i].offsetTop - items[i].offsetHeight) / height * transitionDuration + 's';\n } else {\n itemDelay = items[i].offsetTop / height * transitionDuration + 's';\n }\n items[i].style.transitionDelay = itemDelay;\n }\n // Apply the initial clip to the text before we start animating.\n this.applyClip_(height, width);\n // Wait for the next frame, turn on animation, and apply the final clip.\n // Also make it visible. This triggers the transitions.\n window.requestAnimationFrame(function () {\n this.element_.classList.add(this.CssClasses_.IS_ANIMATING);\n this.element_.style.clip = 'rect(0 ' + width + 'px ' + height + 'px 0)';\n this.container_.classList.add(this.CssClasses_.IS_VISIBLE);\n }.bind(this));\n // Clean up after the animation is complete.\n this.addAnimationEndListener_();\n // Add a click listener to the document, to close the menu.\n var callback = function (e) {\n // Check to see if the document is processing the same event that\n // displayed the menu in the first place. If so, do nothing.\n // Also check to see if the menu is in the process of closing itself, and\n // do nothing in that case.\n // Also check if the clicked element is a menu item\n // if so, do nothing.\n if (e !== evt && !this.closing_ && e.target.parentNode !== this.element_) {\n document.removeEventListener('click', callback);\n this.hide();\n }\n }.bind(this);\n document.addEventListener('click', callback);\n }\n};\nMaterialMenu.prototype['show'] = MaterialMenu.prototype.show;\n/**\n * Hides the menu.\n *\n * @public\n */\nMaterialMenu.prototype.hide = function () {\n if (this.element_ && this.container_ && this.outline_) {\n var items = this.element_.querySelectorAll('.' + this.CssClasses_.ITEM);\n // Remove all transition delays; menu items fade out concurrently.\n for (var i = 0; i < items.length; i++) {\n items[i].style.transitionDelay = null;\n }\n // Measure the inner element.\n var rect = this.element_.getBoundingClientRect();\n var height = rect.height;\n var width = rect.width;\n // Turn on animation, and apply the final clip. Also make invisible.\n // This triggers the transitions.\n this.element_.classList.add(this.CssClasses_.IS_ANIMATING);\n this.applyClip_(height, width);\n this.container_.classList.remove(this.CssClasses_.IS_VISIBLE);\n // Clean up after the animation is complete.\n this.addAnimationEndListener_();\n }\n};\nMaterialMenu.prototype['hide'] = MaterialMenu.prototype.hide;\n/**\n * Displays or hides the menu, depending on current state.\n *\n * @public\n */\nMaterialMenu.prototype.toggle = function (evt) {\n if (this.container_.classList.contains(this.CssClasses_.IS_VISIBLE)) {\n this.hide();\n } else {\n this.show(evt);\n }\n};\nMaterialMenu.prototype['toggle'] = MaterialMenu.prototype.toggle;\n/**\n * Downgrade the component.\n *\n * @private\n */\nMaterialMenu.prototype.mdlDowngrade_ = function () {\n var items = this.element_.querySelectorAll('.' + this.CssClasses_.ITEM);\n for (var i = 0; i < items.length; i++) {\n items[i].removeEventListener('click', this.boundItemClick_);\n items[i].removeEventListener('keydown', this.boundItemKeydown_);\n }\n};\n/**\n * Public alias for the downgrade method.\n *\n * @public\n */\nMaterialMenu.prototype.mdlDowngrade = MaterialMenu.prototype.mdlDowngrade_;\nMaterialMenu.prototype['mdlDowngrade'] = MaterialMenu.prototype.mdlDowngrade;\n// The component registers itself. It can assume componentHandler is available\n// in the global scope.\ncomponentHandler.register({\n constructor: MaterialMenu,\n classAsString: 'MaterialMenu',\n cssClass: 'mdl-js-menu',\n widget: true\n});\n/**\n * @license\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Class constructor for Progress MDL component.\n * Implements MDL component design pattern defined at:\n * https://github.com/jasonmayes/mdl-component-design-pattern\n *\n * @constructor\n * @param {HTMLElement} element The element that will be upgraded.\n */\nvar MaterialProgress = function MaterialProgress(element) {\n this.element_ = element;\n // Initialize instance.\n this.init();\n};\nwindow['MaterialProgress'] = MaterialProgress;\n/**\n * Store constants in one place so they can be updated easily.\n *\n * @enum {string | number}\n * @private\n */\nMaterialProgress.prototype.Constant_ = {};\n/**\n * Store strings for class names defined by this component that are used in\n * JavaScript. This allows us to simply change it in one place should we\n * decide to modify at a later date.\n *\n * @enum {string}\n * @private\n */\nMaterialProgress.prototype.CssClasses_ = { INDETERMINATE_CLASS: 'mdl-progress__indeterminate' };\n/**\n * Set the current progress of the progressbar.\n *\n * @param {number} p Percentage of the progress (0-100)\n * @public\n */\nMaterialProgress.prototype.setProgress = function (p) {\n if (this.element_.classList.contains(this.CssClasses_.INDETERMINATE_CLASS)) {\n return;\n }\n this.progressbar_.style.width = p + '%';\n};\nMaterialProgress.prototype['setProgress'] = MaterialProgress.prototype.setProgress;\n/**\n * Set the current progress of the buffer.\n *\n * @param {number} p Percentage of the buffer (0-100)\n * @public\n */\nMaterialProgress.prototype.setBuffer = function (p) {\n this.bufferbar_.style.width = p + '%';\n this.auxbar_.style.width = 100 - p + '%';\n};\nMaterialProgress.prototype['setBuffer'] = MaterialProgress.prototype.setBuffer;\n/**\n * Initialize element.\n */\nMaterialProgress.prototype.init = function () {\n if (this.element_) {\n var el = document.createElement('div');\n el.className = 'progressbar bar bar1';\n this.element_.appendChild(el);\n this.progressbar_ = el;\n el = document.createElement('div');\n el.className = 'bufferbar bar bar2';\n this.element_.appendChild(el);\n this.bufferbar_ = el;\n el = document.createElement('div');\n el.className = 'auxbar bar bar3';\n this.element_.appendChild(el);\n this.auxbar_ = el;\n this.progressbar_.style.width = '0%';\n this.bufferbar_.style.width = '100%';\n this.auxbar_.style.width = '0%';\n this.element_.classList.add('is-upgraded');\n }\n};\n/**\n * Downgrade the component\n *\n * @private\n */\nMaterialProgress.prototype.mdlDowngrade_ = function () {\n while (this.element_.firstChild) {\n this.element_.removeChild(this.element_.firstChild);\n }\n};\n/**\n * Public alias for the downgrade method.\n *\n * @public\n */\nMaterialProgress.prototype.mdlDowngrade = MaterialProgress.prototype.mdlDowngrade_;\nMaterialProgress.prototype['mdlDowngrade'] = MaterialProgress.prototype.mdlDowngrade;\n// The component registers itself. It can assume componentHandler is available\n// in the global scope.\ncomponentHandler.register({\n constructor: MaterialProgress,\n classAsString: 'MaterialProgress',\n cssClass: 'mdl-js-progress',\n widget: true\n});\n/**\n * @license\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Class constructor for Radio MDL component.\n * Implements MDL component design pattern defined at:\n * https://github.com/jasonmayes/mdl-component-design-pattern\n *\n * @constructor\n * @param {HTMLElement} element The element that will be upgraded.\n */\nvar MaterialRadio = function MaterialRadio(element) {\n this.element_ = element;\n // Initialize instance.\n this.init();\n};\nwindow['MaterialRadio'] = MaterialRadio;\n/**\n * Store constants in one place so they can be updated easily.\n *\n * @enum {string | number}\n * @private\n */\nMaterialRadio.prototype.Constant_ = { TINY_TIMEOUT: 0.001 };\n/**\n * Store strings for class names defined by this component that are used in\n * JavaScript. This allows us to simply change it in one place should we\n * decide to modify at a later date.\n *\n * @enum {string}\n * @private\n */\nMaterialRadio.prototype.CssClasses_ = {\n IS_FOCUSED: 'is-focused',\n IS_DISABLED: 'is-disabled',\n IS_CHECKED: 'is-checked',\n IS_UPGRADED: 'is-upgraded',\n JS_RADIO: 'mdl-js-radio',\n RADIO_BTN: 'mdl-radio__button',\n RADIO_OUTER_CIRCLE: 'mdl-radio__outer-circle',\n RADIO_INNER_CIRCLE: 'mdl-radio__inner-circle',\n RIPPLE_EFFECT: 'mdl-js-ripple-effect',\n RIPPLE_IGNORE_EVENTS: 'mdl-js-ripple-effect--ignore-events',\n RIPPLE_CONTAINER: 'mdl-radio__ripple-container',\n RIPPLE_CENTER: 'mdl-ripple--center',\n RIPPLE: 'mdl-ripple'\n};\n/**\n * Handle change of state.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialRadio.prototype.onChange_ = function (event) {\n // Since other radio buttons don't get change events, we need to look for\n // them to update their classes.\n var radios = document.getElementsByClassName(this.CssClasses_.JS_RADIO);\n for (var i = 0; i < radios.length; i++) {\n var button = radios[i].querySelector('.' + this.CssClasses_.RADIO_BTN);\n // Different name == different group, so no point updating those.\n if (button.getAttribute('name') === this.btnElement_.getAttribute('name')) {\n radios[i]['MaterialRadio'].updateClasses_();\n }\n }\n};\n/**\n * Handle focus.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialRadio.prototype.onFocus_ = function (event) {\n this.element_.classList.add(this.CssClasses_.IS_FOCUSED);\n};\n/**\n * Handle lost focus.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialRadio.prototype.onBlur_ = function (event) {\n this.element_.classList.remove(this.CssClasses_.IS_FOCUSED);\n};\n/**\n * Handle mouseup.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialRadio.prototype.onMouseup_ = function (event) {\n this.blur_();\n};\n/**\n * Update classes.\n *\n * @private\n */\nMaterialRadio.prototype.updateClasses_ = function () {\n this.checkDisabled();\n this.checkToggleState();\n};\n/**\n * Add blur.\n *\n * @private\n */\nMaterialRadio.prototype.blur_ = function () {\n // TODO: figure out why there's a focus event being fired after our blur,\n // so that we can avoid this hack.\n window.setTimeout(function () {\n this.btnElement_.blur();\n }.bind(this), this.Constant_.TINY_TIMEOUT);\n};\n// Public methods.\n/**\n * Check the components disabled state.\n *\n * @public\n */\nMaterialRadio.prototype.checkDisabled = function () {\n if (this.btnElement_.disabled) {\n this.element_.classList.add(this.CssClasses_.IS_DISABLED);\n } else {\n this.element_.classList.remove(this.CssClasses_.IS_DISABLED);\n }\n};\nMaterialRadio.prototype['checkDisabled'] = MaterialRadio.prototype.checkDisabled;\n/**\n * Check the components toggled state.\n *\n * @public\n */\nMaterialRadio.prototype.checkToggleState = function () {\n if (this.btnElement_.checked) {\n this.element_.classList.add(this.CssClasses_.IS_CHECKED);\n } else {\n this.element_.classList.remove(this.CssClasses_.IS_CHECKED);\n }\n};\nMaterialRadio.prototype['checkToggleState'] = MaterialRadio.prototype.checkToggleState;\n/**\n * Disable radio.\n *\n * @public\n */\nMaterialRadio.prototype.disable = function () {\n this.btnElement_.disabled = true;\n this.updateClasses_();\n};\nMaterialRadio.prototype['disable'] = MaterialRadio.prototype.disable;\n/**\n * Enable radio.\n *\n * @public\n */\nMaterialRadio.prototype.enable = function () {\n this.btnElement_.disabled = false;\n this.updateClasses_();\n};\nMaterialRadio.prototype['enable'] = MaterialRadio.prototype.enable;\n/**\n * Check radio.\n *\n * @public\n */\nMaterialRadio.prototype.check = function () {\n this.btnElement_.checked = true;\n this.updateClasses_();\n};\nMaterialRadio.prototype['check'] = MaterialRadio.prototype.check;\n/**\n * Uncheck radio.\n *\n * @public\n */\nMaterialRadio.prototype.uncheck = function () {\n this.btnElement_.checked = false;\n this.updateClasses_();\n};\nMaterialRadio.prototype['uncheck'] = MaterialRadio.prototype.uncheck;\n/**\n * Initialize element.\n */\nMaterialRadio.prototype.init = function () {\n if (this.element_) {\n this.btnElement_ = this.element_.querySelector('.' + this.CssClasses_.RADIO_BTN);\n this.boundChangeHandler_ = this.onChange_.bind(this);\n this.boundFocusHandler_ = this.onChange_.bind(this);\n this.boundBlurHandler_ = this.onBlur_.bind(this);\n this.boundMouseUpHandler_ = this.onMouseup_.bind(this);\n var outerCircle = document.createElement('span');\n outerCircle.classList.add(this.CssClasses_.RADIO_OUTER_CIRCLE);\n var innerCircle = document.createElement('span');\n innerCircle.classList.add(this.CssClasses_.RADIO_INNER_CIRCLE);\n this.element_.appendChild(outerCircle);\n this.element_.appendChild(innerCircle);\n var rippleContainer;\n if (this.element_.classList.contains(this.CssClasses_.RIPPLE_EFFECT)) {\n this.element_.classList.add(this.CssClasses_.RIPPLE_IGNORE_EVENTS);\n rippleContainer = document.createElement('span');\n rippleContainer.classList.add(this.CssClasses_.RIPPLE_CONTAINER);\n rippleContainer.classList.add(this.CssClasses_.RIPPLE_EFFECT);\n rippleContainer.classList.add(this.CssClasses_.RIPPLE_CENTER);\n rippleContainer.addEventListener('mouseup', this.boundMouseUpHandler_);\n var ripple = document.createElement('span');\n ripple.classList.add(this.CssClasses_.RIPPLE);\n rippleContainer.appendChild(ripple);\n this.element_.appendChild(rippleContainer);\n }\n this.btnElement_.addEventListener('change', this.boundChangeHandler_);\n this.btnElement_.addEventListener('focus', this.boundFocusHandler_);\n this.btnElement_.addEventListener('blur', this.boundBlurHandler_);\n this.element_.addEventListener('mouseup', this.boundMouseUpHandler_);\n this.updateClasses_();\n this.element_.classList.add(this.CssClasses_.IS_UPGRADED);\n }\n};\n/**\n * Downgrade the element.\n *\n * @private\n */\nMaterialRadio.prototype.mdlDowngrade_ = function () {\n var rippleContainer = this.element_.querySelector('.' + this.CssClasses_.RIPPLE_CONTAINER);\n this.btnElement_.removeEventListener('change', this.boundChangeHandler_);\n this.btnElement_.removeEventListener('focus', this.boundFocusHandler_);\n this.btnElement_.removeEventListener('blur', this.boundBlurHandler_);\n this.element_.removeEventListener('mouseup', this.boundMouseUpHandler_);\n if (rippleContainer) {\n rippleContainer.removeEventListener('mouseup', this.boundMouseUpHandler_);\n this.element_.removeChild(rippleContainer);\n }\n};\n/**\n * Public alias for the downgrade method.\n *\n * @public\n */\nMaterialRadio.prototype.mdlDowngrade = MaterialRadio.prototype.mdlDowngrade_;\nMaterialRadio.prototype['mdlDowngrade'] = MaterialRadio.prototype.mdlDowngrade;\n// The component registers itself. It can assume componentHandler is available\n// in the global scope.\ncomponentHandler.register({\n constructor: MaterialRadio,\n classAsString: 'MaterialRadio',\n cssClass: 'mdl-js-radio',\n widget: true\n});\n/**\n * @license\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Class constructor for Slider MDL component.\n * Implements MDL component design pattern defined at:\n * https://github.com/jasonmayes/mdl-component-design-pattern\n *\n * @constructor\n * @param {HTMLElement} element The element that will be upgraded.\n */\nvar MaterialSlider = function MaterialSlider(element) {\n this.element_ = element;\n // Browser feature detection.\n this.isIE_ = window.navigator.msPointerEnabled;\n // Initialize instance.\n this.init();\n};\nwindow['MaterialSlider'] = MaterialSlider;\n/**\n * Store constants in one place so they can be updated easily.\n *\n * @enum {string | number}\n * @private\n */\nMaterialSlider.prototype.Constant_ = {};\n/**\n * Store strings for class names defined by this component that are used in\n * JavaScript. This allows us to simply change it in one place should we\n * decide to modify at a later date.\n *\n * @enum {string}\n * @private\n */\nMaterialSlider.prototype.CssClasses_ = {\n IE_CONTAINER: 'mdl-slider__ie-container',\n SLIDER_CONTAINER: 'mdl-slider__container',\n BACKGROUND_FLEX: 'mdl-slider__background-flex',\n BACKGROUND_LOWER: 'mdl-slider__background-lower',\n BACKGROUND_UPPER: 'mdl-slider__background-upper',\n IS_LOWEST_VALUE: 'is-lowest-value',\n IS_UPGRADED: 'is-upgraded'\n};\n/**\n * Handle input on element.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialSlider.prototype.onInput_ = function (event) {\n this.updateValueStyles_();\n};\n/**\n * Handle change on element.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialSlider.prototype.onChange_ = function (event) {\n this.updateValueStyles_();\n};\n/**\n * Handle mouseup on element.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialSlider.prototype.onMouseUp_ = function (event) {\n event.target.blur();\n};\n/**\n * Handle mousedown on container element.\n * This handler is purpose is to not require the use to click\n * exactly on the 2px slider element, as FireFox seems to be very\n * strict about this.\n *\n * @param {Event} event The event that fired.\n * @private\n * @suppress {missingProperties}\n */\nMaterialSlider.prototype.onContainerMouseDown_ = function (event) {\n // If this click is not on the parent element (but rather some child)\n // ignore. It may still bubble up.\n if (event.target !== this.element_.parentElement) {\n return;\n }\n // Discard the original event and create a new event that\n // is on the slider element.\n event.preventDefault();\n var newEvent = new MouseEvent('mousedown', {\n target: event.target,\n buttons: event.buttons,\n clientX: event.clientX,\n clientY: this.element_.getBoundingClientRect().y\n });\n this.element_.dispatchEvent(newEvent);\n};\n/**\n * Handle updating of values.\n *\n * @private\n */\nMaterialSlider.prototype.updateValueStyles_ = function () {\n // Calculate and apply percentages to div structure behind slider.\n var fraction = (this.element_.value - this.element_.min) / (this.element_.max - this.element_.min);\n if (fraction === 0) {\n this.element_.classList.add(this.CssClasses_.IS_LOWEST_VALUE);\n } else {\n this.element_.classList.remove(this.CssClasses_.IS_LOWEST_VALUE);\n }\n if (!this.isIE_) {\n this.backgroundLower_.style.flex = fraction;\n this.backgroundLower_.style.webkitFlex = fraction;\n this.backgroundUpper_.style.flex = 1 - fraction;\n this.backgroundUpper_.style.webkitFlex = 1 - fraction;\n }\n};\n// Public methods.\n/**\n * Disable slider.\n *\n * @public\n */\nMaterialSlider.prototype.disable = function () {\n this.element_.disabled = true;\n};\nMaterialSlider.prototype['disable'] = MaterialSlider.prototype.disable;\n/**\n * Enable slider.\n *\n * @public\n */\nMaterialSlider.prototype.enable = function () {\n this.element_.disabled = false;\n};\nMaterialSlider.prototype['enable'] = MaterialSlider.prototype.enable;\n/**\n * Update slider value.\n *\n * @param {number} value The value to which to set the control (optional).\n * @public\n */\nMaterialSlider.prototype.change = function (value) {\n if (typeof value !== 'undefined') {\n this.element_.value = value;\n }\n this.updateValueStyles_();\n};\nMaterialSlider.prototype['change'] = MaterialSlider.prototype.change;\n/**\n * Initialize element.\n */\nMaterialSlider.prototype.init = function () {\n if (this.element_) {\n if (this.isIE_) {\n // Since we need to specify a very large height in IE due to\n // implementation limitations, we add a parent here that trims it down to\n // a reasonable size.\n var containerIE = document.createElement('div');\n containerIE.classList.add(this.CssClasses_.IE_CONTAINER);\n this.element_.parentElement.insertBefore(containerIE, this.element_);\n this.element_.parentElement.removeChild(this.element_);\n containerIE.appendChild(this.element_);\n } else {\n // For non-IE browsers, we need a div structure that sits behind the\n // slider and allows us to style the left and right sides of it with\n // different colors.\n var container = document.createElement('div');\n container.classList.add(this.CssClasses_.SLIDER_CONTAINER);\n this.element_.parentElement.insertBefore(container, this.element_);\n this.element_.parentElement.removeChild(this.element_);\n container.appendChild(this.element_);\n var backgroundFlex = document.createElement('div');\n backgroundFlex.classList.add(this.CssClasses_.BACKGROUND_FLEX);\n container.appendChild(backgroundFlex);\n this.backgroundLower_ = document.createElement('div');\n this.backgroundLower_.classList.add(this.CssClasses_.BACKGROUND_LOWER);\n backgroundFlex.appendChild(this.backgroundLower_);\n this.backgroundUpper_ = document.createElement('div');\n this.backgroundUpper_.classList.add(this.CssClasses_.BACKGROUND_UPPER);\n backgroundFlex.appendChild(this.backgroundUpper_);\n }\n this.boundInputHandler = this.onInput_.bind(this);\n this.boundChangeHandler = this.onChange_.bind(this);\n this.boundMouseUpHandler = this.onMouseUp_.bind(this);\n this.boundContainerMouseDownHandler = this.onContainerMouseDown_.bind(this);\n this.element_.addEventListener('input', this.boundInputHandler);\n this.element_.addEventListener('change', this.boundChangeHandler);\n this.element_.addEventListener('mouseup', this.boundMouseUpHandler);\n this.element_.parentElement.addEventListener('mousedown', this.boundContainerMouseDownHandler);\n this.updateValueStyles_();\n this.element_.classList.add(this.CssClasses_.IS_UPGRADED);\n }\n};\n/**\n * Downgrade the component\n *\n * @private\n */\nMaterialSlider.prototype.mdlDowngrade_ = function () {\n this.element_.removeEventListener('input', this.boundInputHandler);\n this.element_.removeEventListener('change', this.boundChangeHandler);\n this.element_.removeEventListener('mouseup', this.boundMouseUpHandler);\n this.element_.parentElement.removeEventListener('mousedown', this.boundContainerMouseDownHandler);\n};\n/**\n * Public alias for the downgrade method.\n *\n * @public\n */\nMaterialSlider.prototype.mdlDowngrade = MaterialSlider.prototype.mdlDowngrade_;\nMaterialSlider.prototype['mdlDowngrade'] = MaterialSlider.prototype.mdlDowngrade;\n// The component registers itself. It can assume componentHandler is available\n// in the global scope.\ncomponentHandler.register({\n constructor: MaterialSlider,\n classAsString: 'MaterialSlider',\n cssClass: 'mdl-js-slider',\n widget: true\n});\n/**\n * @license\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Class constructor for Spinner MDL component.\n * Implements MDL component design pattern defined at:\n * https://github.com/jasonmayes/mdl-component-design-pattern\n *\n * @param {HTMLElement} element The element that will be upgraded.\n * @constructor\n */\nvar MaterialSpinner = function MaterialSpinner(element) {\n this.element_ = element;\n // Initialize instance.\n this.init();\n};\nwindow['MaterialSpinner'] = MaterialSpinner;\n/**\n * Store constants in one place so they can be updated easily.\n *\n * @enum {string | number}\n * @private\n */\nMaterialSpinner.prototype.Constant_ = { MDL_SPINNER_LAYER_COUNT: 4 };\n/**\n * Store strings for class names defined by this component that are used in\n * JavaScript. This allows us to simply change it in one place should we\n * decide to modify at a later date.\n *\n * @enum {string}\n * @private\n */\nMaterialSpinner.prototype.CssClasses_ = {\n MDL_SPINNER_LAYER: 'mdl-spinner__layer',\n MDL_SPINNER_CIRCLE_CLIPPER: 'mdl-spinner__circle-clipper',\n MDL_SPINNER_CIRCLE: 'mdl-spinner__circle',\n MDL_SPINNER_GAP_PATCH: 'mdl-spinner__gap-patch',\n MDL_SPINNER_LEFT: 'mdl-spinner__left',\n MDL_SPINNER_RIGHT: 'mdl-spinner__right'\n};\n/**\n * Auxiliary method to create a spinner layer.\n *\n * @param {number} index Index of the layer to be created.\n * @public\n */\nMaterialSpinner.prototype.createLayer = function (index) {\n var layer = document.createElement('div');\n layer.classList.add(this.CssClasses_.MDL_SPINNER_LAYER);\n layer.classList.add(this.CssClasses_.MDL_SPINNER_LAYER + '-' + index);\n var leftClipper = document.createElement('div');\n leftClipper.classList.add(this.CssClasses_.MDL_SPINNER_CIRCLE_CLIPPER);\n leftClipper.classList.add(this.CssClasses_.MDL_SPINNER_LEFT);\n var gapPatch = document.createElement('div');\n gapPatch.classList.add(this.CssClasses_.MDL_SPINNER_GAP_PATCH);\n var rightClipper = document.createElement('div');\n rightClipper.classList.add(this.CssClasses_.MDL_SPINNER_CIRCLE_CLIPPER);\n rightClipper.classList.add(this.CssClasses_.MDL_SPINNER_RIGHT);\n var circleOwners = [\n leftClipper,\n gapPatch,\n rightClipper\n ];\n for (var i = 0; i < circleOwners.length; i++) {\n var circle = document.createElement('div');\n circle.classList.add(this.CssClasses_.MDL_SPINNER_CIRCLE);\n circleOwners[i].appendChild(circle);\n }\n layer.appendChild(leftClipper);\n layer.appendChild(gapPatch);\n layer.appendChild(rightClipper);\n this.element_.appendChild(layer);\n};\nMaterialSpinner.prototype['createLayer'] = MaterialSpinner.prototype.createLayer;\n/**\n * Stops the spinner animation.\n * Public method for users who need to stop the spinner for any reason.\n *\n * @public\n */\nMaterialSpinner.prototype.stop = function () {\n this.element_.classList.remove('is-active');\n};\nMaterialSpinner.prototype['stop'] = MaterialSpinner.prototype.stop;\n/**\n * Starts the spinner animation.\n * Public method for users who need to manually start the spinner for any reason\n * (instead of just adding the 'is-active' class to their markup).\n *\n * @public\n */\nMaterialSpinner.prototype.start = function () {\n this.element_.classList.add('is-active');\n};\nMaterialSpinner.prototype['start'] = MaterialSpinner.prototype.start;\n/**\n * Initialize element.\n */\nMaterialSpinner.prototype.init = function () {\n if (this.element_) {\n for (var i = 1; i <= this.Constant_.MDL_SPINNER_LAYER_COUNT; i++) {\n this.createLayer(i);\n }\n this.element_.classList.add('is-upgraded');\n }\n};\n// The component registers itself. It can assume componentHandler is available\n// in the global scope.\ncomponentHandler.register({\n constructor: MaterialSpinner,\n classAsString: 'MaterialSpinner',\n cssClass: 'mdl-js-spinner',\n widget: true\n});\n/**\n * @license\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Class constructor for Checkbox MDL component.\n * Implements MDL component design pattern defined at:\n * https://github.com/jasonmayes/mdl-component-design-pattern\n *\n * @constructor\n * @param {HTMLElement} element The element that will be upgraded.\n */\nvar MaterialSwitch = function MaterialSwitch(element) {\n this.element_ = element;\n // Initialize instance.\n this.init();\n};\nwindow['MaterialSwitch'] = MaterialSwitch;\n/**\n * Store constants in one place so they can be updated easily.\n *\n * @enum {string | number}\n * @private\n */\nMaterialSwitch.prototype.Constant_ = { TINY_TIMEOUT: 0.001 };\n/**\n * Store strings for class names defined by this component that are used in\n * JavaScript. This allows us to simply change it in one place should we\n * decide to modify at a later date.\n *\n * @enum {string}\n * @private\n */\nMaterialSwitch.prototype.CssClasses_ = {\n INPUT: 'mdl-switch__input',\n TRACK: 'mdl-switch__track',\n THUMB: 'mdl-switch__thumb',\n FOCUS_HELPER: 'mdl-switch__focus-helper',\n RIPPLE_EFFECT: 'mdl-js-ripple-effect',\n RIPPLE_IGNORE_EVENTS: 'mdl-js-ripple-effect--ignore-events',\n RIPPLE_CONTAINER: 'mdl-switch__ripple-container',\n RIPPLE_CENTER: 'mdl-ripple--center',\n RIPPLE: 'mdl-ripple',\n IS_FOCUSED: 'is-focused',\n IS_DISABLED: 'is-disabled',\n IS_CHECKED: 'is-checked'\n};\n/**\n * Handle change of state.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialSwitch.prototype.onChange_ = function (event) {\n this.updateClasses_();\n};\n/**\n * Handle focus of element.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialSwitch.prototype.onFocus_ = function (event) {\n this.element_.classList.add(this.CssClasses_.IS_FOCUSED);\n};\n/**\n * Handle lost focus of element.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialSwitch.prototype.onBlur_ = function (event) {\n this.element_.classList.remove(this.CssClasses_.IS_FOCUSED);\n};\n/**\n * Handle mouseup.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialSwitch.prototype.onMouseUp_ = function (event) {\n this.blur_();\n};\n/**\n * Handle class updates.\n *\n * @private\n */\nMaterialSwitch.prototype.updateClasses_ = function () {\n this.checkDisabled();\n this.checkToggleState();\n};\n/**\n * Add blur.\n *\n * @private\n */\nMaterialSwitch.prototype.blur_ = function () {\n // TODO: figure out why there's a focus event being fired after our blur,\n // so that we can avoid this hack.\n window.setTimeout(function () {\n this.inputElement_.blur();\n }.bind(this), this.Constant_.TINY_TIMEOUT);\n};\n// Public methods.\n/**\n * Check the components disabled state.\n *\n * @public\n */\nMaterialSwitch.prototype.checkDisabled = function () {\n if (this.inputElement_.disabled) {\n this.element_.classList.add(this.CssClasses_.IS_DISABLED);\n } else {\n this.element_.classList.remove(this.CssClasses_.IS_DISABLED);\n }\n};\nMaterialSwitch.prototype['checkDisabled'] = MaterialSwitch.prototype.checkDisabled;\n/**\n * Check the components toggled state.\n *\n * @public\n */\nMaterialSwitch.prototype.checkToggleState = function () {\n if (this.inputElement_.checked) {\n this.element_.classList.add(this.CssClasses_.IS_CHECKED);\n } else {\n this.element_.classList.remove(this.CssClasses_.IS_CHECKED);\n }\n};\nMaterialSwitch.prototype['checkToggleState'] = MaterialSwitch.prototype.checkToggleState;\n/**\n * Disable switch.\n *\n * @public\n */\nMaterialSwitch.prototype.disable = function () {\n this.inputElement_.disabled = true;\n this.updateClasses_();\n};\nMaterialSwitch.prototype['disable'] = MaterialSwitch.prototype.disable;\n/**\n * Enable switch.\n *\n * @public\n */\nMaterialSwitch.prototype.enable = function () {\n this.inputElement_.disabled = false;\n this.updateClasses_();\n};\nMaterialSwitch.prototype['enable'] = MaterialSwitch.prototype.enable;\n/**\n * Activate switch.\n *\n * @public\n */\nMaterialSwitch.prototype.on = function () {\n this.inputElement_.checked = true;\n this.updateClasses_();\n};\nMaterialSwitch.prototype['on'] = MaterialSwitch.prototype.on;\n/**\n * Deactivate switch.\n *\n * @public\n */\nMaterialSwitch.prototype.off = function () {\n this.inputElement_.checked = false;\n this.updateClasses_();\n};\nMaterialSwitch.prototype['off'] = MaterialSwitch.prototype.off;\n/**\n * Initialize element.\n */\nMaterialSwitch.prototype.init = function () {\n if (this.element_) {\n this.inputElement_ = this.element_.querySelector('.' + this.CssClasses_.INPUT);\n var track = document.createElement('div');\n track.classList.add(this.CssClasses_.TRACK);\n var thumb = document.createElement('div');\n thumb.classList.add(this.CssClasses_.THUMB);\n var focusHelper = document.createElement('span');\n focusHelper.classList.add(this.CssClasses_.FOCUS_HELPER);\n thumb.appendChild(focusHelper);\n this.element_.appendChild(track);\n this.element_.appendChild(thumb);\n this.boundMouseUpHandler = this.onMouseUp_.bind(this);\n if (this.element_.classList.contains(this.CssClasses_.RIPPLE_EFFECT)) {\n this.element_.classList.add(this.CssClasses_.RIPPLE_IGNORE_EVENTS);\n this.rippleContainerElement_ = document.createElement('span');\n this.rippleContainerElement_.classList.add(this.CssClasses_.RIPPLE_CONTAINER);\n this.rippleContainerElement_.classList.add(this.CssClasses_.RIPPLE_EFFECT);\n this.rippleContainerElement_.classList.add(this.CssClasses_.RIPPLE_CENTER);\n this.rippleContainerElement_.addEventListener('mouseup', this.boundMouseUpHandler);\n var ripple = document.createElement('span');\n ripple.classList.add(this.CssClasses_.RIPPLE);\n this.rippleContainerElement_.appendChild(ripple);\n this.element_.appendChild(this.rippleContainerElement_);\n }\n this.boundChangeHandler = this.onChange_.bind(this);\n this.boundFocusHandler = this.onFocus_.bind(this);\n this.boundBlurHandler = this.onBlur_.bind(this);\n this.inputElement_.addEventListener('change', this.boundChangeHandler);\n this.inputElement_.addEventListener('focus', this.boundFocusHandler);\n this.inputElement_.addEventListener('blur', this.boundBlurHandler);\n this.element_.addEventListener('mouseup', this.boundMouseUpHandler);\n this.updateClasses_();\n this.element_.classList.add('is-upgraded');\n }\n};\n/**\n * Downgrade the component.\n *\n * @private\n */\nMaterialSwitch.prototype.mdlDowngrade_ = function () {\n if (this.rippleContainerElement_) {\n this.rippleContainerElement_.removeEventListener('mouseup', this.boundMouseUpHandler);\n }\n this.inputElement_.removeEventListener('change', this.boundChangeHandler);\n this.inputElement_.removeEventListener('focus', this.boundFocusHandler);\n this.inputElement_.removeEventListener('blur', this.boundBlurHandler);\n this.element_.removeEventListener('mouseup', this.boundMouseUpHandler);\n};\n/**\n * Public alias for the downgrade method.\n *\n * @public\n */\nMaterialSwitch.prototype.mdlDowngrade = MaterialSwitch.prototype.mdlDowngrade_;\nMaterialSwitch.prototype['mdlDowngrade'] = MaterialSwitch.prototype.mdlDowngrade;\n// The component registers itself. It can assume componentHandler is available\n// in the global scope.\ncomponentHandler.register({\n constructor: MaterialSwitch,\n classAsString: 'MaterialSwitch',\n cssClass: 'mdl-js-switch',\n widget: true\n});\n/**\n * @license\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Class constructor for Tabs MDL component.\n * Implements MDL component design pattern defined at:\n * https://github.com/jasonmayes/mdl-component-design-pattern\n *\n * @constructor\n * @param {HTMLElement} element The element that will be upgraded.\n */\nvar MaterialTabs = function MaterialTabs(element) {\n // Stores the HTML element.\n this.element_ = element;\n // Initialize instance.\n this.init();\n};\nwindow['MaterialTabs'] = MaterialTabs;\n/**\n * Store constants in one place so they can be updated easily.\n *\n * @enum {string}\n * @private\n */\nMaterialTabs.prototype.Constant_ = {};\n/**\n * Store strings for class names defined by this component that are used in\n * JavaScript. This allows us to simply change it in one place should we\n * decide to modify at a later date.\n *\n * @enum {string}\n * @private\n */\nMaterialTabs.prototype.CssClasses_ = {\n TAB_CLASS: 'mdl-tabs__tab',\n PANEL_CLASS: 'mdl-tabs__panel',\n ACTIVE_CLASS: 'is-active',\n UPGRADED_CLASS: 'is-upgraded',\n MDL_JS_RIPPLE_EFFECT: 'mdl-js-ripple-effect',\n MDL_RIPPLE_CONTAINER: 'mdl-tabs__ripple-container',\n MDL_RIPPLE: 'mdl-ripple',\n MDL_JS_RIPPLE_EFFECT_IGNORE_EVENTS: 'mdl-js-ripple-effect--ignore-events'\n};\n/**\n * Handle clicks to a tabs component\n *\n * @private\n */\nMaterialTabs.prototype.initTabs_ = function () {\n if (this.element_.classList.contains(this.CssClasses_.MDL_JS_RIPPLE_EFFECT)) {\n this.element_.classList.add(this.CssClasses_.MDL_JS_RIPPLE_EFFECT_IGNORE_EVENTS);\n }\n // Select element tabs, document panels\n this.tabs_ = this.element_.querySelectorAll('.' + this.CssClasses_.TAB_CLASS);\n this.panels_ = this.element_.querySelectorAll('.' + this.CssClasses_.PANEL_CLASS);\n // Create new tabs for each tab element\n for (var i = 0; i < this.tabs_.length; i++) {\n new MaterialTab(this.tabs_[i], this);\n }\n this.element_.classList.add(this.CssClasses_.UPGRADED_CLASS);\n};\n/**\n * Reset tab state, dropping active classes\n *\n * @private\n */\nMaterialTabs.prototype.resetTabState_ = function () {\n for (var k = 0; k < this.tabs_.length; k++) {\n this.tabs_[k].classList.remove(this.CssClasses_.ACTIVE_CLASS);\n }\n};\n/**\n * Reset panel state, droping active classes\n *\n * @private\n */\nMaterialTabs.prototype.resetPanelState_ = function () {\n for (var j = 0; j < this.panels_.length; j++) {\n this.panels_[j].classList.remove(this.CssClasses_.ACTIVE_CLASS);\n }\n};\n/**\n * Initialize element.\n */\nMaterialTabs.prototype.init = function () {\n if (this.element_) {\n this.initTabs_();\n }\n};\n/**\n * Constructor for an individual tab.\n *\n * @constructor\n * @param {HTMLElement} tab The HTML element for the tab.\n * @param {MaterialTabs} ctx The MaterialTabs object that owns the tab.\n */\nfunction MaterialTab(tab, ctx) {\n if (tab) {\n if (ctx.element_.classList.contains(ctx.CssClasses_.MDL_JS_RIPPLE_EFFECT)) {\n var rippleContainer = document.createElement('span');\n rippleContainer.classList.add(ctx.CssClasses_.MDL_RIPPLE_CONTAINER);\n rippleContainer.classList.add(ctx.CssClasses_.MDL_JS_RIPPLE_EFFECT);\n var ripple = document.createElement('span');\n ripple.classList.add(ctx.CssClasses_.MDL_RIPPLE);\n rippleContainer.appendChild(ripple);\n tab.appendChild(rippleContainer);\n }\n tab.addEventListener('click', function (e) {\n e.preventDefault();\n var href = tab.href.split('#')[1];\n var panel = ctx.element_.querySelector('#' + href);\n ctx.resetTabState_();\n ctx.resetPanelState_();\n tab.classList.add(ctx.CssClasses_.ACTIVE_CLASS);\n panel.classList.add(ctx.CssClasses_.ACTIVE_CLASS);\n });\n }\n}\n// The component registers itself. It can assume componentHandler is available\n// in the global scope.\ncomponentHandler.register({\n constructor: MaterialTabs,\n classAsString: 'MaterialTabs',\n cssClass: 'mdl-js-tabs'\n});\n/**\n * @license\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Class constructor for Textfield MDL component.\n * Implements MDL component design pattern defined at:\n * https://github.com/jasonmayes/mdl-component-design-pattern\n *\n * @constructor\n * @param {HTMLElement} element The element that will be upgraded.\n */\nvar MaterialTextfield = function MaterialTextfield(element) {\n this.element_ = element;\n this.maxRows = this.Constant_.NO_MAX_ROWS;\n // Initialize instance.\n this.init();\n};\nwindow['MaterialTextfield'] = MaterialTextfield;\n/**\n * Store constants in one place so they can be updated easily.\n *\n * @enum {string | number}\n * @private\n */\nMaterialTextfield.prototype.Constant_ = {\n NO_MAX_ROWS: -1,\n MAX_ROWS_ATTRIBUTE: 'maxrows'\n};\n/**\n * Store strings for class names defined by this component that are used in\n * JavaScript. This allows us to simply change it in one place should we\n * decide to modify at a later date.\n *\n * @enum {string}\n * @private\n */\nMaterialTextfield.prototype.CssClasses_ = {\n LABEL: 'mdl-textfield__label',\n INPUT: 'mdl-textfield__input',\n IS_DIRTY: 'is-dirty',\n IS_FOCUSED: 'is-focused',\n IS_DISABLED: 'is-disabled',\n IS_INVALID: 'is-invalid',\n IS_UPGRADED: 'is-upgraded'\n};\n/**\n * Handle input being entered.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialTextfield.prototype.onKeyDown_ = function (event) {\n var currentRowCount = event.target.value.split('\\n').length;\n if (event.keyCode === 13) {\n if (currentRowCount >= this.maxRows) {\n event.preventDefault();\n }\n }\n};\n/**\n * Handle focus.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialTextfield.prototype.onFocus_ = function (event) {\n this.element_.classList.add(this.CssClasses_.IS_FOCUSED);\n};\n/**\n * Handle lost focus.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialTextfield.prototype.onBlur_ = function (event) {\n this.element_.classList.remove(this.CssClasses_.IS_FOCUSED);\n};\n/**\n * Handle class updates.\n *\n * @private\n */\nMaterialTextfield.prototype.updateClasses_ = function () {\n this.checkDisabled();\n this.checkValidity();\n this.checkDirty();\n};\n// Public methods.\n/**\n * Check the disabled state and update field accordingly.\n *\n * @public\n */\nMaterialTextfield.prototype.checkDisabled = function () {\n if (this.input_.disabled) {\n this.element_.classList.add(this.CssClasses_.IS_DISABLED);\n } else {\n this.element_.classList.remove(this.CssClasses_.IS_DISABLED);\n }\n};\nMaterialTextfield.prototype['checkDisabled'] = MaterialTextfield.prototype.checkDisabled;\n/**\n * Check the validity state and update field accordingly.\n *\n * @public\n */\nMaterialTextfield.prototype.checkValidity = function () {\n if (this.input_.validity) {\n if (this.input_.validity.valid) {\n this.element_.classList.remove(this.CssClasses_.IS_INVALID);\n } else {\n this.element_.classList.add(this.CssClasses_.IS_INVALID);\n }\n }\n};\nMaterialTextfield.prototype['checkValidity'] = MaterialTextfield.prototype.checkValidity;\n/**\n * Check the dirty state and update field accordingly.\n *\n * @public\n */\nMaterialTextfield.prototype.checkDirty = function () {\n if (this.input_.value && this.input_.value.length > 0) {\n this.element_.classList.add(this.CssClasses_.IS_DIRTY);\n } else {\n this.element_.classList.remove(this.CssClasses_.IS_DIRTY);\n }\n};\nMaterialTextfield.prototype['checkDirty'] = MaterialTextfield.prototype.checkDirty;\n/**\n * Disable text field.\n *\n * @public\n */\nMaterialTextfield.prototype.disable = function () {\n this.input_.disabled = true;\n this.updateClasses_();\n};\nMaterialTextfield.prototype['disable'] = MaterialTextfield.prototype.disable;\n/**\n * Enable text field.\n *\n * @public\n */\nMaterialTextfield.prototype.enable = function () {\n this.input_.disabled = false;\n this.updateClasses_();\n};\nMaterialTextfield.prototype['enable'] = MaterialTextfield.prototype.enable;\n/**\n * Update text field value.\n *\n * @param {string} value The value to which to set the control (optional).\n * @public\n */\nMaterialTextfield.prototype.change = function (value) {\n this.input_.value = value || '';\n this.updateClasses_();\n};\nMaterialTextfield.prototype['change'] = MaterialTextfield.prototype.change;\n/**\n * Initialize element.\n */\nMaterialTextfield.prototype.init = function () {\n if (this.element_) {\n this.label_ = this.element_.querySelector('.' + this.CssClasses_.LABEL);\n this.input_ = this.element_.querySelector('.' + this.CssClasses_.INPUT);\n if (this.input_) {\n if (this.input_.hasAttribute(this.Constant_.MAX_ROWS_ATTRIBUTE)) {\n this.maxRows = parseInt(this.input_.getAttribute(this.Constant_.MAX_ROWS_ATTRIBUTE), 10);\n if (isNaN(this.maxRows)) {\n this.maxRows = this.Constant_.NO_MAX_ROWS;\n }\n }\n this.boundUpdateClassesHandler = this.updateClasses_.bind(this);\n this.boundFocusHandler = this.onFocus_.bind(this);\n this.boundBlurHandler = this.onBlur_.bind(this);\n this.input_.addEventListener('input', this.boundUpdateClassesHandler);\n this.input_.addEventListener('focus', this.boundFocusHandler);\n this.input_.addEventListener('blur', this.boundBlurHandler);\n if (this.maxRows !== this.Constant_.NO_MAX_ROWS) {\n // TODO: This should handle pasting multi line text.\n // Currently doesn't.\n this.boundKeyDownHandler = this.onKeyDown_.bind(this);\n this.input_.addEventListener('keydown', this.boundKeyDownHandler);\n }\n var invalid = this.element_.classList.contains(this.CssClasses_.IS_INVALID);\n this.updateClasses_();\n this.element_.classList.add(this.CssClasses_.IS_UPGRADED);\n if (invalid) {\n this.element_.classList.add(this.CssClasses_.IS_INVALID);\n }\n }\n }\n};\n/**\n * Downgrade the component\n *\n * @private\n */\nMaterialTextfield.prototype.mdlDowngrade_ = function () {\n this.input_.removeEventListener('input', this.boundUpdateClassesHandler);\n this.input_.removeEventListener('focus', this.boundFocusHandler);\n this.input_.removeEventListener('blur', this.boundBlurHandler);\n if (this.boundKeyDownHandler) {\n this.input_.removeEventListener('keydown', this.boundKeyDownHandler);\n }\n};\n/**\n * Public alias for the downgrade method.\n *\n * @public\n */\nMaterialTextfield.prototype.mdlDowngrade = MaterialTextfield.prototype.mdlDowngrade_;\nMaterialTextfield.prototype['mdlDowngrade'] = MaterialTextfield.prototype.mdlDowngrade;\n// The component registers itself. It can assume componentHandler is available\n// in the global scope.\ncomponentHandler.register({\n constructor: MaterialTextfield,\n classAsString: 'MaterialTextfield',\n cssClass: 'mdl-js-textfield',\n widget: true\n});\n/**\n * @license\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Class constructor for Tooltip MDL component.\n * Implements MDL component design pattern defined at:\n * https://github.com/jasonmayes/mdl-component-design-pattern\n *\n * @constructor\n * @param {HTMLElement} element The element that will be upgraded.\n */\nvar MaterialTooltip = function MaterialTooltip(element) {\n this.element_ = element;\n // Initialize instance.\n this.init();\n};\nwindow['MaterialTooltip'] = MaterialTooltip;\n/**\n * Store constants in one place so they can be updated easily.\n *\n * @enum {string | number}\n * @private\n */\nMaterialTooltip.prototype.Constant_ = {};\n/**\n * Store strings for class names defined by this component that are used in\n * JavaScript. This allows us to simply change it in one place should we\n * decide to modify at a later date.\n *\n * @enum {string}\n * @private\n */\nMaterialTooltip.prototype.CssClasses_ = { IS_ACTIVE: 'is-active' };\n/**\n * Handle mouseenter for tooltip.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialTooltip.prototype.handleMouseEnter_ = function (event) {\n event.stopPropagation();\n var props = event.target.getBoundingClientRect();\n var left = props.left + props.width / 2;\n var marginLeft = -1 * (this.element_.offsetWidth / 2);\n if (left + marginLeft < 0) {\n this.element_.style.left = 0;\n this.element_.style.marginLeft = 0;\n } else {\n this.element_.style.left = left + 'px';\n this.element_.style.marginLeft = marginLeft + 'px';\n }\n this.element_.style.top = props.top + props.height + 10 + 'px';\n this.element_.classList.add(this.CssClasses_.IS_ACTIVE);\n window.addEventListener('scroll', this.boundMouseLeaveHandler, false);\n window.addEventListener('touchmove', this.boundMouseLeaveHandler, false);\n};\n/**\n * Handle mouseleave for tooltip.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialTooltip.prototype.handleMouseLeave_ = function (event) {\n event.stopPropagation();\n this.element_.classList.remove(this.CssClasses_.IS_ACTIVE);\n window.removeEventListener('scroll', this.boundMouseLeaveHandler);\n window.removeEventListener('touchmove', this.boundMouseLeaveHandler, false);\n};\n/**\n * Initialize element.\n */\nMaterialTooltip.prototype.init = function () {\n if (this.element_) {\n var forElId = this.element_.getAttribute('for');\n if (forElId) {\n this.forElement_ = document.getElementById(forElId);\n }\n if (this.forElement_) {\n // Tabindex needs to be set for `blur` events to be emitted\n if (!this.forElement_.hasAttribute('tabindex')) {\n this.forElement_.setAttribute('tabindex', '0');\n }\n this.boundMouseEnterHandler = this.handleMouseEnter_.bind(this);\n this.boundMouseLeaveHandler = this.handleMouseLeave_.bind(this);\n this.forElement_.addEventListener('mouseenter', this.boundMouseEnterHandler, false);\n this.forElement_.addEventListener('click', this.boundMouseEnterHandler, false);\n this.forElement_.addEventListener('blur', this.boundMouseLeaveHandler);\n this.forElement_.addEventListener('touchstart', this.boundMouseEnterHandler, false);\n this.forElement_.addEventListener('mouseleave', this.boundMouseLeaveHandler);\n }\n }\n};\n/**\n * Downgrade the component\n *\n * @private\n */\nMaterialTooltip.prototype.mdlDowngrade_ = function () {\n if (this.forElement_) {\n this.forElement_.removeEventListener('mouseenter', this.boundMouseEnterHandler, false);\n this.forElement_.removeEventListener('click', this.boundMouseEnterHandler, false);\n this.forElement_.removeEventListener('touchstart', this.boundMouseEnterHandler, false);\n this.forElement_.removeEventListener('mouseleave', this.boundMouseLeaveHandler);\n }\n};\n/**\n * Public alias for the downgrade method.\n *\n * @public\n */\nMaterialTooltip.prototype.mdlDowngrade = MaterialTooltip.prototype.mdlDowngrade_;\nMaterialTooltip.prototype['mdlDowngrade'] = MaterialTooltip.prototype.mdlDowngrade;\n// The component registers itself. It can assume componentHandler is available\n// in the global scope.\ncomponentHandler.register({\n constructor: MaterialTooltip,\n classAsString: 'MaterialTooltip',\n cssClass: 'mdl-tooltip'\n});\n/**\n * @license\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Class constructor for Layout MDL component.\n * Implements MDL component design pattern defined at:\n * https://github.com/jasonmayes/mdl-component-design-pattern\n *\n * @constructor\n * @param {HTMLElement} element The element that will be upgraded.\n */\nvar MaterialLayout = function MaterialLayout(element) {\n this.element_ = element;\n // Initialize instance.\n this.init();\n};\nwindow['MaterialLayout'] = MaterialLayout;\n/**\n * Store constants in one place so they can be updated easily.\n *\n * @enum {string | number}\n * @private\n */\nMaterialLayout.prototype.Constant_ = {\n MAX_WIDTH: '(max-width: 1024px)',\n TAB_SCROLL_PIXELS: 100,\n MENU_ICON: 'menu',\n CHEVRON_LEFT: 'chevron_left',\n CHEVRON_RIGHT: 'chevron_right'\n};\n/**\n * Modes.\n *\n * @enum {number}\n * @private\n */\nMaterialLayout.prototype.Mode_ = {\n STANDARD: 0,\n SEAMED: 1,\n WATERFALL: 2,\n SCROLL: 3\n};\n/**\n * Store strings for class names defined by this component that are used in\n * JavaScript. This allows us to simply change it in one place should we\n * decide to modify at a later date.\n *\n * @enum {string}\n * @private\n */\nMaterialLayout.prototype.CssClasses_ = {\n CONTAINER: 'mdl-layout__container',\n HEADER: 'mdl-layout__header',\n DRAWER: 'mdl-layout__drawer',\n CONTENT: 'mdl-layout__content',\n DRAWER_BTN: 'mdl-layout__drawer-button',\n ICON: 'material-icons',\n JS_RIPPLE_EFFECT: 'mdl-js-ripple-effect',\n RIPPLE_CONTAINER: 'mdl-layout__tab-ripple-container',\n RIPPLE: 'mdl-ripple',\n RIPPLE_IGNORE_EVENTS: 'mdl-js-ripple-effect--ignore-events',\n HEADER_SEAMED: 'mdl-layout__header--seamed',\n HEADER_WATERFALL: 'mdl-layout__header--waterfall',\n HEADER_SCROLL: 'mdl-layout__header--scroll',\n FIXED_HEADER: 'mdl-layout--fixed-header',\n OBFUSCATOR: 'mdl-layout__obfuscator',\n TAB_BAR: 'mdl-layout__tab-bar',\n TAB_CONTAINER: 'mdl-layout__tab-bar-container',\n TAB: 'mdl-layout__tab',\n TAB_BAR_BUTTON: 'mdl-layout__tab-bar-button',\n TAB_BAR_LEFT_BUTTON: 'mdl-layout__tab-bar-left-button',\n TAB_BAR_RIGHT_BUTTON: 'mdl-layout__tab-bar-right-button',\n PANEL: 'mdl-layout__tab-panel',\n HAS_DRAWER: 'has-drawer',\n HAS_TABS: 'has-tabs',\n HAS_SCROLLING_HEADER: 'has-scrolling-header',\n CASTING_SHADOW: 'is-casting-shadow',\n IS_COMPACT: 'is-compact',\n IS_SMALL_SCREEN: 'is-small-screen',\n IS_DRAWER_OPEN: 'is-visible',\n IS_ACTIVE: 'is-active',\n IS_UPGRADED: 'is-upgraded',\n IS_ANIMATING: 'is-animating',\n ON_LARGE_SCREEN: 'mdl-layout--large-screen-only',\n ON_SMALL_SCREEN: 'mdl-layout--small-screen-only'\n};\n/**\n * Handles scrolling on the content.\n *\n * @private\n */\nMaterialLayout.prototype.contentScrollHandler_ = function () {\n if (this.header_.classList.contains(this.CssClasses_.IS_ANIMATING)) {\n return;\n }\n if (this.content_.scrollTop > 0 && !this.header_.classList.contains(this.CssClasses_.IS_COMPACT)) {\n this.header_.classList.add(this.CssClasses_.CASTING_SHADOW);\n this.header_.classList.add(this.CssClasses_.IS_COMPACT);\n this.header_.classList.add(this.CssClasses_.IS_ANIMATING);\n } else if (this.content_.scrollTop <= 0 && this.header_.classList.contains(this.CssClasses_.IS_COMPACT)) {\n this.header_.classList.remove(this.CssClasses_.CASTING_SHADOW);\n this.header_.classList.remove(this.CssClasses_.IS_COMPACT);\n this.header_.classList.add(this.CssClasses_.IS_ANIMATING);\n }\n};\n/**\n * Handles changes in screen size.\n *\n * @private\n */\nMaterialLayout.prototype.screenSizeHandler_ = function () {\n if (this.screenSizeMediaQuery_.matches) {\n this.element_.classList.add(this.CssClasses_.IS_SMALL_SCREEN);\n } else {\n this.element_.classList.remove(this.CssClasses_.IS_SMALL_SCREEN);\n // Collapse drawer (if any) when moving to a large screen size.\n if (this.drawer_) {\n this.drawer_.classList.remove(this.CssClasses_.IS_DRAWER_OPEN);\n this.obfuscator_.classList.remove(this.CssClasses_.IS_DRAWER_OPEN);\n }\n }\n};\n/**\n * Handles toggling of the drawer.\n *\n * @private\n */\nMaterialLayout.prototype.drawerToggleHandler_ = function () {\n this.drawer_.classList.toggle(this.CssClasses_.IS_DRAWER_OPEN);\n this.obfuscator_.classList.toggle(this.CssClasses_.IS_DRAWER_OPEN);\n};\n/**\n * Handles (un)setting the `is-animating` class\n *\n * @private\n */\nMaterialLayout.prototype.headerTransitionEndHandler_ = function () {\n this.header_.classList.remove(this.CssClasses_.IS_ANIMATING);\n};\n/**\n * Handles expanding the header on click\n *\n * @private\n */\nMaterialLayout.prototype.headerClickHandler_ = function () {\n if (this.header_.classList.contains(this.CssClasses_.IS_COMPACT)) {\n this.header_.classList.remove(this.CssClasses_.IS_COMPACT);\n this.header_.classList.add(this.CssClasses_.IS_ANIMATING);\n }\n};\n/**\n * Reset tab state, dropping active classes\n *\n * @private\n */\nMaterialLayout.prototype.resetTabState_ = function (tabBar) {\n for (var k = 0; k < tabBar.length; k++) {\n tabBar[k].classList.remove(this.CssClasses_.IS_ACTIVE);\n }\n};\n/**\n * Reset panel state, droping active classes\n *\n * @private\n */\nMaterialLayout.prototype.resetPanelState_ = function (panels) {\n for (var j = 0; j < panels.length; j++) {\n panels[j].classList.remove(this.CssClasses_.IS_ACTIVE);\n }\n};\n/**\n * Initialize element.\n */\nMaterialLayout.prototype.init = function () {\n if (this.element_) {\n var container = document.createElement('div');\n container.classList.add(this.CssClasses_.CONTAINER);\n this.element_.parentElement.insertBefore(container, this.element_);\n this.element_.parentElement.removeChild(this.element_);\n container.appendChild(this.element_);\n var directChildren = this.element_.childNodes;\n var numChildren = directChildren.length;\n for (var c = 0; c < numChildren; c++) {\n var child = directChildren[c];\n if (child.classList && child.classList.contains(this.CssClasses_.HEADER)) {\n this.header_ = child;\n }\n if (child.classList && child.classList.contains(this.CssClasses_.DRAWER)) {\n this.drawer_ = child;\n }\n if (child.classList && child.classList.contains(this.CssClasses_.CONTENT)) {\n this.content_ = child;\n }\n }\n if (this.header_) {\n this.tabBar_ = this.header_.querySelector('.' + this.CssClasses_.TAB_BAR);\n }\n var mode = this.Mode_.STANDARD;\n if (this.header_) {\n if (this.header_.classList.contains(this.CssClasses_.HEADER_SEAMED)) {\n mode = this.Mode_.SEAMED;\n } else if (this.header_.classList.contains(this.CssClasses_.HEADER_WATERFALL)) {\n mode = this.Mode_.WATERFALL;\n this.header_.addEventListener('transitionend', this.headerTransitionEndHandler_.bind(this));\n this.header_.addEventListener('click', this.headerClickHandler_.bind(this));\n } else if (this.header_.classList.contains(this.CssClasses_.HEADER_SCROLL)) {\n mode = this.Mode_.SCROLL;\n container.classList.add(this.CssClasses_.HAS_SCROLLING_HEADER);\n }\n if (mode === this.Mode_.STANDARD) {\n this.header_.classList.add(this.CssClasses_.CASTING_SHADOW);\n if (this.tabBar_) {\n this.tabBar_.classList.add(this.CssClasses_.CASTING_SHADOW);\n }\n } else if (mode === this.Mode_.SEAMED || mode === this.Mode_.SCROLL) {\n this.header_.classList.remove(this.CssClasses_.CASTING_SHADOW);\n if (this.tabBar_) {\n this.tabBar_.classList.remove(this.CssClasses_.CASTING_SHADOW);\n }\n } else if (mode === this.Mode_.WATERFALL) {\n // Add and remove shadows depending on scroll position.\n // Also add/remove auxiliary class for styling of the compact version of\n // the header.\n this.content_.addEventListener('scroll', this.contentScrollHandler_.bind(this));\n this.contentScrollHandler_();\n }\n }\n // Add drawer toggling button to our layout, if we have an openable drawer.\n if (this.drawer_) {\n var drawerButton = this.element_.querySelector('.' + this.CssClasses_.DRAWER_BTN);\n if (!drawerButton) {\n drawerButton = document.createElement('div');\n drawerButton.classList.add(this.CssClasses_.DRAWER_BTN);\n var drawerButtonIcon = document.createElement('i');\n drawerButtonIcon.classList.add(this.CssClasses_.ICON);\n drawerButtonIcon.textContent = this.Constant_.MENU_ICON;\n drawerButton.appendChild(drawerButtonIcon);\n }\n if (this.drawer_.classList.contains(this.CssClasses_.ON_LARGE_SCREEN)) {\n //If drawer has ON_LARGE_SCREEN class then add it to the drawer toggle button as well.\n drawerButton.classList.add(this.CssClasses_.ON_LARGE_SCREEN);\n } else if (this.drawer_.classList.contains(this.CssClasses_.ON_SMALL_SCREEN)) {\n //If drawer has ON_SMALL_SCREEN class then add it to the drawer toggle button as well.\n drawerButton.classList.add(this.CssClasses_.ON_SMALL_SCREEN);\n }\n drawerButton.addEventListener('click', this.drawerToggleHandler_.bind(this));\n // Add a class if the layout has a drawer, for altering the left padding.\n // Adds the HAS_DRAWER to the elements since this.header_ may or may\n // not be present.\n this.element_.classList.add(this.CssClasses_.HAS_DRAWER);\n // If we have a fixed header, add the button to the header rather than\n // the layout.\n if (this.element_.classList.contains(this.CssClasses_.FIXED_HEADER)) {\n this.header_.insertBefore(drawerButton, this.header_.firstChild);\n } else {\n this.element_.insertBefore(drawerButton, this.content_);\n }\n var obfuscator = document.createElement('div');\n obfuscator.classList.add(this.CssClasses_.OBFUSCATOR);\n this.element_.appendChild(obfuscator);\n obfuscator.addEventListener('click', this.drawerToggleHandler_.bind(this));\n this.obfuscator_ = obfuscator;\n }\n // Keep an eye on screen size, and add/remove auxiliary class for styling\n // of small screens.\n this.screenSizeMediaQuery_ = window.matchMedia(this.Constant_.MAX_WIDTH);\n this.screenSizeMediaQuery_.addListener(this.screenSizeHandler_.bind(this));\n this.screenSizeHandler_();\n // Initialize tabs, if any.\n if (this.header_ && this.tabBar_) {\n this.element_.classList.add(this.CssClasses_.HAS_TABS);\n var tabContainer = document.createElement('div');\n tabContainer.classList.add(this.CssClasses_.TAB_CONTAINER);\n this.header_.insertBefore(tabContainer, this.tabBar_);\n this.header_.removeChild(this.tabBar_);\n var leftButton = document.createElement('div');\n leftButton.classList.add(this.CssClasses_.TAB_BAR_BUTTON);\n leftButton.classList.add(this.CssClasses_.TAB_BAR_LEFT_BUTTON);\n var leftButtonIcon = document.createElement('i');\n leftButtonIcon.classList.add(this.CssClasses_.ICON);\n leftButtonIcon.textContent = this.Constant_.CHEVRON_LEFT;\n leftButton.appendChild(leftButtonIcon);\n leftButton.addEventListener('click', function () {\n this.tabBar_.scrollLeft -= this.Constant_.TAB_SCROLL_PIXELS;\n }.bind(this));\n var rightButton = document.createElement('div');\n rightButton.classList.add(this.CssClasses_.TAB_BAR_BUTTON);\n rightButton.classList.add(this.CssClasses_.TAB_BAR_RIGHT_BUTTON);\n var rightButtonIcon = document.createElement('i');\n rightButtonIcon.classList.add(this.CssClasses_.ICON);\n rightButtonIcon.textContent = this.Constant_.CHEVRON_RIGHT;\n rightButton.appendChild(rightButtonIcon);\n rightButton.addEventListener('click', function () {\n this.tabBar_.scrollLeft += this.Constant_.TAB_SCROLL_PIXELS;\n }.bind(this));\n tabContainer.appendChild(leftButton);\n tabContainer.appendChild(this.tabBar_);\n tabContainer.appendChild(rightButton);\n // Add and remove buttons depending on scroll position.\n var tabScrollHandler = function () {\n if (this.tabBar_.scrollLeft > 0) {\n leftButton.classList.add(this.CssClasses_.IS_ACTIVE);\n } else {\n leftButton.classList.remove(this.CssClasses_.IS_ACTIVE);\n }\n if (this.tabBar_.scrollLeft < this.tabBar_.scrollWidth - this.tabBar_.offsetWidth) {\n rightButton.classList.add(this.CssClasses_.IS_ACTIVE);\n } else {\n rightButton.classList.remove(this.CssClasses_.IS_ACTIVE);\n }\n }.bind(this);\n this.tabBar_.addEventListener('scroll', tabScrollHandler);\n tabScrollHandler();\n if (this.tabBar_.classList.contains(this.CssClasses_.JS_RIPPLE_EFFECT)) {\n this.tabBar_.classList.add(this.CssClasses_.RIPPLE_IGNORE_EVENTS);\n }\n // Select element tabs, document panels\n var tabs = this.tabBar_.querySelectorAll('.' + this.CssClasses_.TAB);\n var panels = this.content_.querySelectorAll('.' + this.CssClasses_.PANEL);\n // Create new tabs for each tab element\n for (var i = 0; i < tabs.length; i++) {\n new MaterialLayoutTab(tabs[i], tabs, panels, this);\n }\n }\n this.element_.classList.add(this.CssClasses_.IS_UPGRADED);\n }\n};\n/**\n * Constructor for an individual tab.\n *\n * @constructor\n * @param {HTMLElement} tab The HTML element for the tab.\n * @param {!Array} tabs Array with HTML elements for all tabs.\n * @param {!Array} panels Array with HTML elements for all panels.\n * @param {MaterialLayout} layout The MaterialLayout object that owns the tab.\n */\nfunction MaterialLayoutTab(tab, tabs, panels, layout) {\n if (layout.tabBar_.classList.contains(layout.CssClasses_.JS_RIPPLE_EFFECT)) {\n var rippleContainer = document.createElement('span');\n rippleContainer.classList.add(layout.CssClasses_.RIPPLE_CONTAINER);\n rippleContainer.classList.add(layout.CssClasses_.JS_RIPPLE_EFFECT);\n var ripple = document.createElement('span');\n ripple.classList.add(layout.CssClasses_.RIPPLE);\n rippleContainer.appendChild(ripple);\n tab.appendChild(rippleContainer);\n }\n tab.addEventListener('click', function (e) {\n e.preventDefault();\n var href = tab.href.split('#')[1];\n var panel = layout.content_.querySelector('#' + href);\n layout.resetTabState_(tabs);\n layout.resetPanelState_(panels);\n tab.classList.add(layout.CssClasses_.IS_ACTIVE);\n panel.classList.add(layout.CssClasses_.IS_ACTIVE);\n });\n}\n// The component registers itself. It can assume componentHandler is available\n// in the global scope.\ncomponentHandler.register({\n constructor: MaterialLayout,\n classAsString: 'MaterialLayout',\n cssClass: 'mdl-js-layout'\n});\n/**\n * @license\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Class constructor for Data Table Card MDL component.\n * Implements MDL component design pattern defined at:\n * https://github.com/jasonmayes/mdl-component-design-pattern\n *\n * @constructor\n * @param {HTMLElement} element The element that will be upgraded.\n */\nvar MaterialDataTable = function MaterialDataTable(element) {\n this.element_ = element;\n // Initialize instance.\n this.init();\n};\nwindow['MaterialDataTable'] = MaterialDataTable;\n/**\n * Store constants in one place so they can be updated easily.\n *\n * @enum {string | number}\n * @private\n */\nMaterialDataTable.prototype.Constant_ = {};\n/**\n * Store strings for class names defined by this component that are used in\n * JavaScript. This allows us to simply change it in one place should we\n * decide to modify at a later date.\n *\n * @enum {string}\n * @private\n */\nMaterialDataTable.prototype.CssClasses_ = {\n DATA_TABLE: 'mdl-data-table',\n SELECTABLE: 'mdl-data-table--selectable',\n SELECT_ELEMENT: 'mdl-data-table__select',\n IS_SELECTED: 'is-selected',\n IS_UPGRADED: 'is-upgraded'\n};\n/**\n * Generates and returns a function that toggles the selection state of a\n * single row (or multiple rows).\n *\n * @param {Element} checkbox Checkbox that toggles the selection state.\n * @param {HTMLElement} row Row to toggle when checkbox changes.\n * @param {(Array|NodeList)=} opt_rows Rows to toggle when checkbox changes.\n * @private\n */\nMaterialDataTable.prototype.selectRow_ = function (checkbox, row, opt_rows) {\n if (row) {\n return function () {\n if (checkbox.checked) {\n row.classList.add(this.CssClasses_.IS_SELECTED);\n } else {\n row.classList.remove(this.CssClasses_.IS_SELECTED);\n }\n }.bind(this);\n }\n if (opt_rows) {\n return function () {\n var i;\n var el;\n if (checkbox.checked) {\n for (i = 0; i < opt_rows.length; i++) {\n el = opt_rows[i].querySelector('td').querySelector('.mdl-checkbox');\n el['MaterialCheckbox'].check();\n opt_rows[i].classList.add(this.CssClasses_.IS_SELECTED);\n }\n } else {\n for (i = 0; i < opt_rows.length; i++) {\n el = opt_rows[i].querySelector('td').querySelector('.mdl-checkbox');\n el['MaterialCheckbox'].uncheck();\n opt_rows[i].classList.remove(this.CssClasses_.IS_SELECTED);\n }\n }\n }.bind(this);\n }\n};\n/**\n * Creates a checkbox for a single or or multiple rows and hooks up the\n * event handling.\n *\n * @param {HTMLElement} row Row to toggle when checkbox changes.\n * @param {(Array|NodeList)=} opt_rows Rows to toggle when checkbox changes.\n * @private\n */\nMaterialDataTable.prototype.createCheckbox_ = function (row, opt_rows) {\n var label = document.createElement('label');\n var labelClasses = [\n 'mdl-checkbox',\n 'mdl-js-checkbox',\n 'mdl-js-ripple-effect',\n this.CssClasses_.SELECT_ELEMENT\n ];\n label.className = labelClasses.join(' ');\n var checkbox = document.createElement('input');\n checkbox.type = 'checkbox';\n checkbox.classList.add('mdl-checkbox__input');\n checkbox.addEventListener('change', this.selectRow_(checkbox, row, opt_rows));\n label.appendChild(checkbox);\n componentHandler.upgradeElement(label, 'MaterialCheckbox');\n return label;\n};\n/**\n * Initialize element.\n */\nMaterialDataTable.prototype.init = function () {\n if (this.element_) {\n var firstHeader = this.element_.querySelector('th');\n var rows = this.element_.querySelector('tbody').querySelectorAll('tr');\n if (this.element_.classList.contains(this.CssClasses_.SELECTABLE)) {\n var th = document.createElement('th');\n var headerCheckbox = this.createCheckbox_(null, rows);\n th.appendChild(headerCheckbox);\n firstHeader.parentElement.insertBefore(th, firstHeader);\n for (var i = 0; i < rows.length; i++) {\n var firstCell = rows[i].querySelector('td');\n if (firstCell) {\n var td = document.createElement('td');\n var rowCheckbox = this.createCheckbox_(rows[i]);\n td.appendChild(rowCheckbox);\n rows[i].insertBefore(td, firstCell);\n }\n }\n }\n this.element_.classList.add(this.CssClasses_.IS_UPGRADED);\n }\n};\n// The component registers itself. It can assume componentHandler is available\n// in the global scope.\ncomponentHandler.register({\n constructor: MaterialDataTable,\n classAsString: 'MaterialDataTable',\n cssClass: 'mdl-js-data-table'\n});\n/**\n * @license\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Class constructor for Ripple MDL component.\n * Implements MDL component design pattern defined at:\n * https://github.com/jasonmayes/mdl-component-design-pattern\n *\n * @constructor\n * @param {HTMLElement} element The element that will be upgraded.\n */\nvar MaterialRipple = function MaterialRipple(element) {\n this.element_ = element;\n // Initialize instance.\n this.init();\n};\nwindow['MaterialRipple'] = MaterialRipple;\n/**\n * Store constants in one place so they can be updated easily.\n *\n * @enum {string | number}\n * @private\n */\nMaterialRipple.prototype.Constant_ = {\n INITIAL_SCALE: 'scale(0.0001, 0.0001)',\n INITIAL_SIZE: '1px',\n INITIAL_OPACITY: '0.4',\n FINAL_OPACITY: '0',\n FINAL_SCALE: ''\n};\n/**\n * Store strings for class names defined by this component that are used in\n * JavaScript. This allows us to simply change it in one place should we\n * decide to modify at a later date.\n *\n * @enum {string}\n * @private\n */\nMaterialRipple.prototype.CssClasses_ = {\n RIPPLE_CENTER: 'mdl-ripple--center',\n RIPPLE_EFFECT_IGNORE_EVENTS: 'mdl-js-ripple-effect--ignore-events',\n RIPPLE: 'mdl-ripple',\n IS_ANIMATING: 'is-animating',\n IS_VISIBLE: 'is-visible'\n};\n/**\n * Handle mouse / finger down on element.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialRipple.prototype.downHandler_ = function (event) {\n if (!this.rippleElement_.style.width && !this.rippleElement_.style.height) {\n var rect = this.element_.getBoundingClientRect();\n this.boundHeight = rect.height;\n this.boundWidth = rect.width;\n this.rippleSize_ = Math.sqrt(rect.width * rect.width + rect.height * rect.height) * 2 + 2;\n this.rippleElement_.style.width = this.rippleSize_ + 'px';\n this.rippleElement_.style.height = this.rippleSize_ + 'px';\n }\n this.rippleElement_.classList.add(this.CssClasses_.IS_VISIBLE);\n if (event.type === 'mousedown' && this.ignoringMouseDown_) {\n this.ignoringMouseDown_ = false;\n } else {\n if (event.type === 'touchstart') {\n this.ignoringMouseDown_ = true;\n }\n var frameCount = this.getFrameCount();\n if (frameCount > 0) {\n return;\n }\n this.setFrameCount(1);\n var bound = event.currentTarget.getBoundingClientRect();\n var x;\n var y;\n // Check if we are handling a keyboard click.\n if (event.clientX === 0 && event.clientY === 0) {\n x = Math.round(bound.width / 2);\n y = Math.round(bound.height / 2);\n } else {\n var clientX = event.clientX ? event.clientX : event.touches[0].clientX;\n var clientY = event.clientY ? event.clientY : event.touches[0].clientY;\n x = Math.round(clientX - bound.left);\n y = Math.round(clientY - bound.top);\n }\n this.setRippleXY(x, y);\n this.setRippleStyles(true);\n window.requestAnimationFrame(this.animFrameHandler.bind(this));\n }\n};\n/**\n * Handle mouse / finger up on element.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialRipple.prototype.upHandler_ = function (event) {\n // Don't fire for the artificial \"mouseup\" generated by a double-click.\n if (event && event.detail !== 2) {\n this.rippleElement_.classList.remove(this.CssClasses_.IS_VISIBLE);\n }\n // Allow a repaint to occur before removing this class, so the animation\n // shows for tap events, which seem to trigger a mouseup too soon after\n // mousedown.\n window.setTimeout(function () {\n this.rippleElement_.classList.remove(this.CssClasses_.IS_VISIBLE);\n }.bind(this), 0);\n};\n/**\n * Initialize element.\n */\nMaterialRipple.prototype.init = function () {\n if (this.element_) {\n var recentering = this.element_.classList.contains(this.CssClasses_.RIPPLE_CENTER);\n if (!this.element_.classList.contains(this.CssClasses_.RIPPLE_EFFECT_IGNORE_EVENTS)) {\n this.rippleElement_ = this.element_.querySelector('.' + this.CssClasses_.RIPPLE);\n this.frameCount_ = 0;\n this.rippleSize_ = 0;\n this.x_ = 0;\n this.y_ = 0;\n // Touch start produces a compat mouse down event, which would cause a\n // second ripples. To avoid that, we use this property to ignore the first\n // mouse down after a touch start.\n this.ignoringMouseDown_ = false;\n this.boundDownHandler = this.downHandler_.bind(this);\n this.element_.addEventListener('mousedown', this.boundDownHandler);\n this.element_.addEventListener('touchstart', this.boundDownHandler);\n this.boundUpHandler = this.upHandler_.bind(this);\n this.element_.addEventListener('mouseup', this.boundUpHandler);\n this.element_.addEventListener('mouseleave', this.boundUpHandler);\n this.element_.addEventListener('touchend', this.boundUpHandler);\n this.element_.addEventListener('blur', this.boundUpHandler);\n /**\n * Getter for frameCount_.\n * @return {number} the frame count.\n */\n this.getFrameCount = function () {\n return this.frameCount_;\n };\n /**\n * Setter for frameCount_.\n * @param {number} fC the frame count.\n */\n this.setFrameCount = function (fC) {\n this.frameCount_ = fC;\n };\n /**\n * Getter for rippleElement_.\n * @return {Element} the ripple element.\n */\n this.getRippleElement = function () {\n return this.rippleElement_;\n };\n /**\n * Sets the ripple X and Y coordinates.\n * @param {number} newX the new X coordinate\n * @param {number} newY the new Y coordinate\n */\n this.setRippleXY = function (newX, newY) {\n this.x_ = newX;\n this.y_ = newY;\n };\n /**\n * Sets the ripple styles.\n * @param {boolean} start whether or not this is the start frame.\n */\n this.setRippleStyles = function (start) {\n if (this.rippleElement_ !== null) {\n var transformString;\n var scale;\n var size;\n var offset = 'translate(' + this.x_ + 'px, ' + this.y_ + 'px)';\n if (start) {\n scale = this.Constant_.INITIAL_SCALE;\n size = this.Constant_.INITIAL_SIZE;\n } else {\n scale = this.Constant_.FINAL_SCALE;\n size = this.rippleSize_ + 'px';\n if (recentering) {\n offset = 'translate(' + this.boundWidth / 2 + 'px, ' + this.boundHeight / 2 + 'px)';\n }\n }\n transformString = 'translate(-50%, -50%) ' + offset + scale;\n this.rippleElement_.style.webkitTransform = transformString;\n this.rippleElement_.style.msTransform = transformString;\n this.rippleElement_.style.transform = transformString;\n if (start) {\n this.rippleElement_.classList.remove(this.CssClasses_.IS_ANIMATING);\n } else {\n this.rippleElement_.classList.add(this.CssClasses_.IS_ANIMATING);\n }\n }\n };\n /**\n * Handles an animation frame.\n */\n this.animFrameHandler = function () {\n if (this.frameCount_-- > 0) {\n window.requestAnimationFrame(this.animFrameHandler.bind(this));\n } else {\n this.setRippleStyles(false);\n }\n };\n }\n }\n};\n/**\n * Downgrade the component\n *\n * @private\n */\nMaterialRipple.prototype.mdlDowngrade_ = function () {\n this.element_.removeEventListener('mousedown', this.boundDownHandler);\n this.element_.removeEventListener('touchstart', this.boundDownHandler);\n this.element_.removeEventListener('mouseup', this.boundUpHandler);\n this.element_.removeEventListener('mouseleave', this.boundUpHandler);\n this.element_.removeEventListener('touchend', this.boundUpHandler);\n this.element_.removeEventListener('blur', this.boundUpHandler);\n};\n/**\n * Public alias for the downgrade method.\n *\n * @public\n */\nMaterialRipple.prototype.mdlDowngrade = MaterialRipple.prototype.mdlDowngrade_;\nMaterialRipple.prototype['mdlDowngrade'] = MaterialRipple.prototype.mdlDowngrade;\n// The component registers itself. It can assume componentHandler is available\n// in the global scope.\ncomponentHandler.register({\n constructor: MaterialRipple,\n classAsString: 'MaterialRipple',\n cssClass: 'mdl-js-ripple-effect',\n widget: false\n});\n}());\n","/**\n * @license\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * A component handler interface using the revealing module design pattern.\n * More details on this design pattern here:\n * https://github.com/jasonmayes/mdl-component-design-pattern\n *\n * @author Jason Mayes.\n */\n/* exported componentHandler */\n\n// Pre-defining the componentHandler interface, for closure documentation and\n// static verification.\nvar componentHandler = {\n /**\n * Searches existing DOM for elements of our component type and upgrades them\n * if they have not already been upgraded.\n *\n * @param {string=} optJsClass the programatic name of the element class we\n * need to create a new instance of.\n * @param {string=} optCssClass the name of the CSS class elements of this\n * type will have.\n */\n upgradeDom: function(optJsClass, optCssClass) {},\n /**\n * Upgrades a specific element rather than all in the DOM.\n *\n * @param {!Element} element The element we wish to upgrade.\n * @param {string=} optJsClass Optional name of the class we want to upgrade\n * the element to.\n */\n upgradeElement: function(element, optJsClass) {},\n /**\n * Upgrades a specific list of elements rather than all in the DOM.\n *\n * @param {!Element|!Array|!NodeList|!HTMLCollection} elements\n * The elements we wish to upgrade.\n */\n upgradeElements: function(elements) {},\n /**\n * Upgrades all registered components found in the current DOM. This is\n * automatically called on window load.\n */\n upgradeAllRegistered: function() {},\n /**\n * Allows user to be alerted to any upgrades that are performed for a given\n * component type\n *\n * @param {string} jsClass The class name of the MDL component we wish\n * to hook into for any upgrades performed.\n * @param {function(!HTMLElement)} callback The function to call upon an\n * upgrade. This function should expect 1 parameter - the HTMLElement which\n * got upgraded.\n */\n registerUpgradedCallback: function(jsClass, callback) {},\n /**\n * Registers a class for future use and attempts to upgrade existing DOM.\n *\n * @param {componentHandler.ComponentConfigPublic} config the registration configuration\n */\n register: function(config) {},\n /**\n * Downgrade either a given node, an array of nodes, or a NodeList.\n *\n * @param {!Node|!Array|!NodeList} nodes\n */\n downgradeElements: function(nodes) {}\n};\n\ncomponentHandler = (function() {\n 'use strict';\n\n /** @type {!Array} */\n var registeredComponents_ = [];\n\n /** @type {!Array} */\n var createdComponents_ = [];\n\n var downgradeMethod_ = 'mdlDowngrade';\n var componentConfigProperty_ = 'mdlComponentConfigInternal_';\n\n /**\n * Searches registered components for a class we are interested in using.\n * Optionally replaces a match with passed object if specified.\n *\n * @param {string} name The name of a class we want to use.\n * @param {componentHandler.ComponentConfig=} optReplace Optional object to replace match with.\n * @return {!Object|boolean}\n * @private\n */\n function findRegisteredClass_(name, optReplace) {\n for (var i = 0; i < registeredComponents_.length; i++) {\n if (registeredComponents_[i].className === name) {\n if (typeof optReplace !== 'undefined') {\n registeredComponents_[i] = optReplace;\n }\n return registeredComponents_[i];\n }\n }\n return false;\n }\n\n /**\n * Returns an array of the classNames of the upgraded classes on the element.\n *\n * @param {!Element} element The element to fetch data from.\n * @return {!Array}\n * @private\n */\n function getUpgradedListOfElement_(element) {\n var dataUpgraded = element.getAttribute('data-upgraded');\n // Use `['']` as default value to conform the `,name,name...` style.\n return dataUpgraded === null ? [''] : dataUpgraded.split(',');\n }\n\n /**\n * Returns true if the given element has already been upgraded for the given\n * class.\n *\n * @param {!Element} element The element we want to check.\n * @param {string} jsClass The class to check for.\n * @returns {boolean}\n * @private\n */\n function isElementUpgraded_(element, jsClass) {\n var upgradedList = getUpgradedListOfElement_(element);\n return upgradedList.indexOf(jsClass) !== -1;\n }\n\n /**\n * Searches existing DOM for elements of our component type and upgrades them\n * if they have not already been upgraded.\n *\n * @param {string=} optJsClass the programatic name of the element class we\n * need to create a new instance of.\n * @param {string=} optCssClass the name of the CSS class elements of this\n * type will have.\n */\n function upgradeDomInternal(optJsClass, optCssClass) {\n if (typeof optJsClass === 'undefined' &&\n typeof optCssClass === 'undefined') {\n for (var i = 0; i < registeredComponents_.length; i++) {\n upgradeDomInternal(registeredComponents_[i].className,\n registeredComponents_[i].cssClass);\n }\n } else {\n var jsClass = /** @type {string} */ (optJsClass);\n if (typeof optCssClass === 'undefined') {\n var registeredClass = findRegisteredClass_(jsClass);\n if (registeredClass) {\n optCssClass = registeredClass.cssClass;\n }\n }\n\n var elements = document.querySelectorAll('.' + optCssClass);\n for (var n = 0; n < elements.length; n++) {\n upgradeElementInternal(elements[n], jsClass);\n }\n }\n }\n\n /**\n * Upgrades a specific element rather than all in the DOM.\n *\n * @param {!Element} element The element we wish to upgrade.\n * @param {string=} optJsClass Optional name of the class we want to upgrade\n * the element to.\n */\n function upgradeElementInternal(element, optJsClass) {\n // Verify argument type.\n if (!(typeof element === 'object' && element instanceof Element)) {\n throw new Error('Invalid argument provided to upgrade MDL element.');\n }\n var upgradedList = getUpgradedListOfElement_(element);\n var classesToUpgrade = [];\n // If jsClass is not provided scan the registered components to find the\n // ones matching the element's CSS classList.\n if (!optJsClass) {\n var classList = element.classList;\n registeredComponents_.forEach(function(component) {\n // Match CSS & Not to be upgraded & Not upgraded.\n if (classList.contains(component.cssClass) &&\n classesToUpgrade.indexOf(component) === -1 &&\n !isElementUpgraded_(element, component.className)) {\n classesToUpgrade.push(component);\n }\n });\n } else if (!isElementUpgraded_(element, optJsClass)) {\n classesToUpgrade.push(findRegisteredClass_(optJsClass));\n }\n\n // Upgrade the element for each classes.\n for (var i = 0, n = classesToUpgrade.length, registeredClass; i < n; i++) {\n registeredClass = classesToUpgrade[i];\n if (registeredClass) {\n // Mark element as upgraded.\n upgradedList.push(registeredClass.className);\n element.setAttribute('data-upgraded', upgradedList.join(','));\n var instance = new registeredClass.classConstructor(element);\n instance[componentConfigProperty_] = registeredClass;\n createdComponents_.push(instance);\n // Call any callbacks the user has registered with this component type.\n for (var j = 0, m = registeredClass.callbacks.length; j < m; j++) {\n registeredClass.callbacks[j](element);\n }\n\n if (registeredClass.widget) {\n // Assign per element instance for control over API\n element[registeredClass.className] = instance;\n }\n } else {\n throw new Error(\n 'Unable to find a registered component for the given class.');\n }\n\n var ev = document.createEvent('Events');\n ev.initEvent('mdl-componentupgraded', true, true);\n element.dispatchEvent(ev);\n }\n }\n\n /**\n * Upgrades a specific list of elements rather than all in the DOM.\n *\n * @param {!Element|!Array|!NodeList|!HTMLCollection} elements\n * The elements we wish to upgrade.\n */\n function upgradeElementsInternal(elements) {\n if (!Array.isArray(elements)) {\n if (typeof elements.item === 'function') {\n elements = Array.prototype.slice.call(/** @type {Array} */ (elements));\n } else {\n elements = [elements];\n }\n }\n for (var i = 0, n = elements.length, element; i < n; i++) {\n element = elements[i];\n if (element instanceof HTMLElement) {\n upgradeElementInternal(element);\n if (element.children.length > 0) {\n upgradeElementsInternal(element.children);\n }\n }\n }\n }\n\n /**\n * Registers a class for future use and attempts to upgrade existing DOM.\n *\n * @param {componentHandler.ComponentConfigPublic} config\n */\n function registerInternal(config) {\n // In order to support both Closure-compiled and uncompiled code accessing\n // this method, we need to allow for both the dot and array syntax for\n // property access. You'll therefore see the `foo.bar || foo['bar']`\n // pattern repeated across this method.\n var widgetMissing = (typeof config.widget === 'undefined' &&\n typeof config['widget'] === 'undefined');\n var widget = true;\n\n if (!widgetMissing) {\n widget = config.widget || config['widget'];\n }\n\n var newConfig = /** @type {componentHandler.ComponentConfig} */ ({\n classConstructor: config.constructor || config['constructor'],\n className: config.classAsString || config['classAsString'],\n cssClass: config.cssClass || config['cssClass'],\n widget: widget,\n callbacks: []\n });\n\n registeredComponents_.forEach(function(item) {\n if (item.cssClass === newConfig.cssClass) {\n throw new Error('The provided cssClass has already been registered: ' + item.cssClass);\n }\n if (item.className === newConfig.className) {\n throw new Error('The provided className has already been registered');\n }\n });\n\n if (config.constructor.prototype\n .hasOwnProperty(componentConfigProperty_)) {\n throw new Error(\n 'MDL component classes must not have ' + componentConfigProperty_ +\n ' defined as a property.');\n }\n\n var found = findRegisteredClass_(config.classAsString, newConfig);\n\n if (!found) {\n registeredComponents_.push(newConfig);\n }\n }\n\n /**\n * Allows user to be alerted to any upgrades that are performed for a given\n * component type\n *\n * @param {string} jsClass The class name of the MDL component we wish\n * to hook into for any upgrades performed.\n * @param {function(!HTMLElement)} callback The function to call upon an\n * upgrade. This function should expect 1 parameter - the HTMLElement which\n * got upgraded.\n */\n function registerUpgradedCallbackInternal(jsClass, callback) {\n var regClass = findRegisteredClass_(jsClass);\n if (regClass) {\n regClass.callbacks.push(callback);\n }\n }\n\n /**\n * Upgrades all registered components found in the current DOM. This is\n * automatically called on window load.\n */\n function upgradeAllRegisteredInternal() {\n for (var n = 0; n < registeredComponents_.length; n++) {\n upgradeDomInternal(registeredComponents_[n].className);\n }\n }\n\n /**\n * Finds a created component by a given DOM node.\n *\n * @param {!Node} node\n * @return {*}\n */\n function findCreatedComponentByNodeInternal(node) {\n for (var n = 0; n < createdComponents_.length; n++) {\n var component = createdComponents_[n];\n if (component.element_ === node) {\n return component;\n }\n }\n }\n\n /**\n * Check the component for the downgrade method.\n * Execute if found.\n * Remove component from createdComponents list.\n *\n * @param {*} component\n */\n function deconstructComponentInternal(component) {\n if (component &&\n component[componentConfigProperty_]\n .classConstructor.prototype\n .hasOwnProperty(downgradeMethod_)) {\n component[downgradeMethod_]();\n var componentIndex = createdComponents_.indexOf(component);\n createdComponents_.splice(componentIndex, 1);\n\n var upgrades = component.element_.getAttribute('data-upgraded').split(',');\n var componentPlace = upgrades.indexOf(\n component[componentConfigProperty_].classAsString);\n upgrades.splice(componentPlace, 1);\n component.element_.setAttribute('data-upgraded', upgrades.join(','));\n\n var ev = document.createEvent('Events');\n ev.initEvent('mdl-componentdowngraded', true, true);\n component.element_.dispatchEvent(ev);\n }\n }\n\n /**\n * Downgrade either a given node, an array of nodes, or a NodeList.\n *\n * @param {!Node|!Array|!NodeList} nodes\n */\n function downgradeNodesInternal(nodes) {\n /**\n * Auxiliary function to downgrade a single node.\n * @param {!Node} node the node to be downgraded\n */\n var downgradeNode = function(node) {\n deconstructComponentInternal(findCreatedComponentByNodeInternal(node));\n };\n if (nodes instanceof Array || nodes instanceof NodeList) {\n for (var n = 0; n < nodes.length; n++) {\n downgradeNode(nodes[n]);\n }\n } else if (nodes instanceof Node) {\n downgradeNode(nodes);\n } else {\n throw new Error('Invalid argument provided to downgrade MDL nodes.');\n }\n }\n\n // Now return the functions that should be made public with their publicly\n // facing names...\n return {\n upgradeDom: upgradeDomInternal,\n upgradeElement: upgradeElementInternal,\n upgradeElements: upgradeElementsInternal,\n upgradeAllRegistered: upgradeAllRegisteredInternal,\n registerUpgradedCallback: registerUpgradedCallbackInternal,\n register: registerInternal,\n downgradeElements: downgradeNodesInternal\n };\n})();\n\n/**\n * Describes the type of a registered component type managed by\n * componentHandler. Provided for benefit of the Closure compiler.\n *\n * @typedef {{\n * constructor: Function,\n * classAsString: string,\n * cssClass: string,\n * widget: (string|boolean|undefined)\n * }}\n */\ncomponentHandler.ComponentConfigPublic; // jshint ignore:line\n\n/**\n * Describes the type of a registered component type managed by\n * componentHandler. Provided for benefit of the Closure compiler.\n *\n * @typedef {{\n * constructor: !Function,\n * className: string,\n * cssClass: string,\n * widget: (string|boolean),\n * callbacks: !Array\n * }}\n */\ncomponentHandler.ComponentConfig; // jshint ignore:line\n\n/**\n * Created component (i.e., upgraded element) type as managed by\n * componentHandler. Provided for benefit of the Closure compiler.\n *\n * @typedef {{\n * element_: !HTMLElement,\n * className: string,\n * classAsString: string,\n * cssClass: string,\n * widget: string\n * }}\n */\ncomponentHandler.Component; // jshint ignore:line\n\n// Export all symbols, for the benefit of Closure compiler.\n// No effect on uncompiled code.\ncomponentHandler['upgradeDom'] = componentHandler.upgradeDom;\ncomponentHandler['upgradeElement'] = componentHandler.upgradeElement;\ncomponentHandler['upgradeElements'] = componentHandler.upgradeElements;\ncomponentHandler['upgradeAllRegistered'] =\n componentHandler.upgradeAllRegistered;\ncomponentHandler['registerUpgradedCallback'] =\n componentHandler.registerUpgradedCallback;\ncomponentHandler['register'] = componentHandler.register;\ncomponentHandler['downgradeElements'] = componentHandler.downgradeElements;\nwindow.componentHandler = componentHandler;\nwindow['componentHandler'] = componentHandler;\n\nwindow.addEventListener('load', function() {\n 'use strict';\n\n /**\n * Performs a \"Cutting the mustard\" test. If the browser supports the features\n * tested, adds a mdl-js class to the element. It then upgrades all MDL\n * components requiring JavaScript.\n */\n if ('classList' in document.createElement('div') &&\n 'querySelector' in document &&\n 'addEventListener' in window && Array.prototype.forEach) {\n document.documentElement.classList.add('mdl-js');\n componentHandler.upgradeAllRegistered();\n } else {\n /**\n * Dummy function to avoid JS errors.\n */\n componentHandler.upgradeElement = function() {};\n /**\n * Dummy function to avoid JS errors.\n */\n componentHandler.register = function() {};\n }\n});\n","/**\n * @license\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Class constructor for Tabs MDL component.\n * Implements MDL component design pattern defined at:\n * https://github.com/jasonmayes/mdl-component-design-pattern\n *\n * @constructor\n * @param {HTMLElement} element The element that will be upgraded.\n */\nvar MaterialTabs = function MaterialTabs(element) {\n // Stores the HTML element.\n this.element_ = element;\n // Initialize instance.\n this.init();\n};\nwindow['MaterialTabs'] = MaterialTabs;\n/**\n * Store constants in one place so they can be updated easily.\n *\n * @enum {string}\n * @private\n */\nMaterialTabs.prototype.Constant_ = {};\n/**\n * Store strings for class names defined by this component that are used in\n * JavaScript. This allows us to simply change it in one place should we\n * decide to modify at a later date.\n *\n * @enum {string}\n * @private\n */\nMaterialTabs.prototype.CssClasses_ = {\n TAB_CLASS: 'mdl-tabs__tab',\n PANEL_CLASS: 'mdl-tabs__panel',\n ACTIVE_CLASS: 'is-active',\n UPGRADED_CLASS: 'is-upgraded',\n MDL_JS_RIPPLE_EFFECT: 'mdl-js-ripple-effect',\n MDL_RIPPLE_CONTAINER: 'mdl-tabs__ripple-container',\n MDL_RIPPLE: 'mdl-ripple',\n MDL_JS_RIPPLE_EFFECT_IGNORE_EVENTS: 'mdl-js-ripple-effect--ignore-events'\n};\n/**\n * Handle clicks to a tabs component\n *\n * @private\n */\nMaterialTabs.prototype.initTabs_ = function () {\n if (this.element_.classList.contains(this.CssClasses_.MDL_JS_RIPPLE_EFFECT)) {\n this.element_.classList.add(this.CssClasses_.MDL_JS_RIPPLE_EFFECT_IGNORE_EVENTS);\n }\n // Select element tabs, document panels\n this.tabs_ = this.element_.querySelectorAll('.' + this.CssClasses_.TAB_CLASS);\n this.panels_ = this.element_.querySelectorAll('.' + this.CssClasses_.PANEL_CLASS);\n // Create new tabs for each tab element\n for (var i = 0; i < this.tabs_.length; i++) {\n new MaterialTab(this.tabs_[i], this);\n }\n this.element_.classList.add(this.CssClasses_.UPGRADED_CLASS);\n};\n/**\n * Reset tab state, dropping active classes\n *\n * @private\n */\nMaterialTabs.prototype.resetTabState_ = function () {\n for (var k = 0; k < this.tabs_.length; k++) {\n this.tabs_[k].classList.remove(this.CssClasses_.ACTIVE_CLASS);\n }\n};\n/**\n * Reset panel state, droping active classes\n *\n * @private\n */\nMaterialTabs.prototype.resetPanelState_ = function () {\n for (var j = 0; j < this.panels_.length; j++) {\n this.panels_[j].classList.remove(this.CssClasses_.ACTIVE_CLASS);\n }\n};\n/**\n * Initialize element.\n */\nMaterialTabs.prototype.init = function () {\n if (this.element_) {\n this.initTabs_();\n }\n};\n/**\n * Constructor for an individual tab.\n *\n * @constructor\n * @param {HTMLElement} tab The HTML element for the tab.\n * @param {MaterialTabs} ctx The MaterialTabs object that owns the tab.\n */\nfunction MaterialTab(tab, ctx) {\n if (tab) {\n if (ctx.element_.classList.contains(ctx.CssClasses_.MDL_JS_RIPPLE_EFFECT)) {\n var rippleContainer = document.createElement('span');\n rippleContainer.classList.add(ctx.CssClasses_.MDL_RIPPLE_CONTAINER);\n rippleContainer.classList.add(ctx.CssClasses_.MDL_JS_RIPPLE_EFFECT);\n var ripple = document.createElement('span');\n ripple.classList.add(ctx.CssClasses_.MDL_RIPPLE);\n rippleContainer.appendChild(ripple);\n tab.appendChild(rippleContainer);\n }\n tab.addEventListener('click', function (e) {\n e.preventDefault();\n var href = tab.href.split('#')[1];\n var panel = ctx.element_.querySelector('#' + href);\n ctx.resetTabState_();\n ctx.resetPanelState_();\n tab.classList.add(ctx.CssClasses_.ACTIVE_CLASS);\n panel.classList.add(ctx.CssClasses_.ACTIVE_CLASS);\n });\n }\n}\n// The component registers itself. It can assume componentHandler is available\n// in the global scope.\ncomponentHandler.register({\n constructor: MaterialTabs,\n classAsString: 'MaterialTabs',\n cssClass: 'mdl-js-tabs'\n});","/**\n * @license\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Class constructor for Layout MDL component.\n * Implements MDL component design pattern defined at:\n * https://github.com/jasonmayes/mdl-component-design-pattern\n *\n * @constructor\n * @param {HTMLElement} element The element that will be upgraded.\n */\nvar MaterialLayout = function MaterialLayout(element) {\n this.element_ = element;\n // Initialize instance.\n this.init();\n};\nwindow['MaterialLayout'] = MaterialLayout;\n/**\n * Store constants in one place so they can be updated easily.\n *\n * @enum {string | number}\n * @private\n */\nMaterialLayout.prototype.Constant_ = {\n MAX_WIDTH: '(max-width: 1024px)',\n TAB_SCROLL_PIXELS: 100,\n MENU_ICON: 'menu',\n CHEVRON_LEFT: 'chevron_left',\n CHEVRON_RIGHT: 'chevron_right'\n};\n/**\n * Modes.\n *\n * @enum {number}\n * @private\n */\nMaterialLayout.prototype.Mode_ = {\n STANDARD: 0,\n SEAMED: 1,\n WATERFALL: 2,\n SCROLL: 3\n};\n/**\n * Store strings for class names defined by this component that are used in\n * JavaScript. This allows us to simply change it in one place should we\n * decide to modify at a later date.\n *\n * @enum {string}\n * @private\n */\nMaterialLayout.prototype.CssClasses_ = {\n CONTAINER: 'mdl-layout__container',\n HEADER: 'mdl-layout__header',\n DRAWER: 'mdl-layout__drawer',\n CONTENT: 'mdl-layout__content',\n DRAWER_BTN: 'mdl-layout__drawer-button',\n ICON: 'material-icons',\n JS_RIPPLE_EFFECT: 'mdl-js-ripple-effect',\n RIPPLE_CONTAINER: 'mdl-layout__tab-ripple-container',\n RIPPLE: 'mdl-ripple',\n RIPPLE_IGNORE_EVENTS: 'mdl-js-ripple-effect--ignore-events',\n HEADER_SEAMED: 'mdl-layout__header--seamed',\n HEADER_WATERFALL: 'mdl-layout__header--waterfall',\n HEADER_SCROLL: 'mdl-layout__header--scroll',\n FIXED_HEADER: 'mdl-layout--fixed-header',\n OBFUSCATOR: 'mdl-layout__obfuscator',\n TAB_BAR: 'mdl-layout__tab-bar',\n TAB_CONTAINER: 'mdl-layout__tab-bar-container',\n TAB: 'mdl-layout__tab',\n TAB_BAR_BUTTON: 'mdl-layout__tab-bar-button',\n TAB_BAR_LEFT_BUTTON: 'mdl-layout__tab-bar-left-button',\n TAB_BAR_RIGHT_BUTTON: 'mdl-layout__tab-bar-right-button',\n PANEL: 'mdl-layout__tab-panel',\n HAS_DRAWER: 'has-drawer',\n HAS_TABS: 'has-tabs',\n HAS_SCROLLING_HEADER: 'has-scrolling-header',\n CASTING_SHADOW: 'is-casting-shadow',\n IS_COMPACT: 'is-compact',\n IS_SMALL_SCREEN: 'is-small-screen',\n IS_DRAWER_OPEN: 'is-visible',\n IS_ACTIVE: 'is-active',\n IS_UPGRADED: 'is-upgraded',\n IS_ANIMATING: 'is-animating',\n ON_LARGE_SCREEN: 'mdl-layout--large-screen-only',\n ON_SMALL_SCREEN: 'mdl-layout--small-screen-only'\n};\n/**\n * Handles scrolling on the content.\n *\n * @private\n */\nMaterialLayout.prototype.contentScrollHandler_ = function () {\n if (this.header_.classList.contains(this.CssClasses_.IS_ANIMATING)) {\n return;\n }\n if (this.content_.scrollTop > 0 && !this.header_.classList.contains(this.CssClasses_.IS_COMPACT)) {\n this.header_.classList.add(this.CssClasses_.CASTING_SHADOW);\n this.header_.classList.add(this.CssClasses_.IS_COMPACT);\n this.header_.classList.add(this.CssClasses_.IS_ANIMATING);\n } else if (this.content_.scrollTop <= 0 && this.header_.classList.contains(this.CssClasses_.IS_COMPACT)) {\n this.header_.classList.remove(this.CssClasses_.CASTING_SHADOW);\n this.header_.classList.remove(this.CssClasses_.IS_COMPACT);\n this.header_.classList.add(this.CssClasses_.IS_ANIMATING);\n }\n};\n/**\n * Handles changes in screen size.\n *\n * @private\n */\nMaterialLayout.prototype.screenSizeHandler_ = function () {\n if (this.screenSizeMediaQuery_.matches) {\n this.element_.classList.add(this.CssClasses_.IS_SMALL_SCREEN);\n } else {\n this.element_.classList.remove(this.CssClasses_.IS_SMALL_SCREEN);\n // Collapse drawer (if any) when moving to a large screen size.\n if (this.drawer_) {\n this.drawer_.classList.remove(this.CssClasses_.IS_DRAWER_OPEN);\n this.obfuscator_.classList.remove(this.CssClasses_.IS_DRAWER_OPEN);\n }\n }\n};\n/**\n * Handles toggling of the drawer.\n *\n * @private\n */\nMaterialLayout.prototype.drawerToggleHandler_ = function () {\n this.drawer_.classList.toggle(this.CssClasses_.IS_DRAWER_OPEN);\n this.obfuscator_.classList.toggle(this.CssClasses_.IS_DRAWER_OPEN);\n};\n/**\n * Handles (un)setting the `is-animating` class\n *\n * @private\n */\nMaterialLayout.prototype.headerTransitionEndHandler_ = function () {\n this.header_.classList.remove(this.CssClasses_.IS_ANIMATING);\n};\n/**\n * Handles expanding the header on click\n *\n * @private\n */\nMaterialLayout.prototype.headerClickHandler_ = function () {\n if (this.header_.classList.contains(this.CssClasses_.IS_COMPACT)) {\n this.header_.classList.remove(this.CssClasses_.IS_COMPACT);\n this.header_.classList.add(this.CssClasses_.IS_ANIMATING);\n }\n};\n/**\n * Reset tab state, dropping active classes\n *\n * @private\n */\nMaterialLayout.prototype.resetTabState_ = function (tabBar) {\n for (var k = 0; k < tabBar.length; k++) {\n tabBar[k].classList.remove(this.CssClasses_.IS_ACTIVE);\n }\n};\n/**\n * Reset panel state, droping active classes\n *\n * @private\n */\nMaterialLayout.prototype.resetPanelState_ = function (panels) {\n for (var j = 0; j < panels.length; j++) {\n panels[j].classList.remove(this.CssClasses_.IS_ACTIVE);\n }\n};\n/**\n * Initialize element.\n */\nMaterialLayout.prototype.init = function () {\n if (this.element_) {\n var container = document.createElement('div');\n container.classList.add(this.CssClasses_.CONTAINER);\n this.element_.parentElement.insertBefore(container, this.element_);\n this.element_.parentElement.removeChild(this.element_);\n container.appendChild(this.element_);\n var directChildren = this.element_.childNodes;\n var numChildren = directChildren.length;\n for (var c = 0; c < numChildren; c++) {\n var child = directChildren[c];\n if (child.classList && child.classList.contains(this.CssClasses_.HEADER)) {\n this.header_ = child;\n }\n if (child.classList && child.classList.contains(this.CssClasses_.DRAWER)) {\n this.drawer_ = child;\n }\n if (child.classList && child.classList.contains(this.CssClasses_.CONTENT)) {\n this.content_ = child;\n }\n }\n if (this.header_) {\n this.tabBar_ = this.header_.querySelector('.' + this.CssClasses_.TAB_BAR);\n }\n var mode = this.Mode_.STANDARD;\n if (this.header_) {\n if (this.header_.classList.contains(this.CssClasses_.HEADER_SEAMED)) {\n mode = this.Mode_.SEAMED;\n } else if (this.header_.classList.contains(this.CssClasses_.HEADER_WATERFALL)) {\n mode = this.Mode_.WATERFALL;\n this.header_.addEventListener('transitionend', this.headerTransitionEndHandler_.bind(this));\n this.header_.addEventListener('click', this.headerClickHandler_.bind(this));\n } else if (this.header_.classList.contains(this.CssClasses_.HEADER_SCROLL)) {\n mode = this.Mode_.SCROLL;\n container.classList.add(this.CssClasses_.HAS_SCROLLING_HEADER);\n }\n if (mode === this.Mode_.STANDARD) {\n this.header_.classList.add(this.CssClasses_.CASTING_SHADOW);\n if (this.tabBar_) {\n this.tabBar_.classList.add(this.CssClasses_.CASTING_SHADOW);\n }\n } else if (mode === this.Mode_.SEAMED || mode === this.Mode_.SCROLL) {\n this.header_.classList.remove(this.CssClasses_.CASTING_SHADOW);\n if (this.tabBar_) {\n this.tabBar_.classList.remove(this.CssClasses_.CASTING_SHADOW);\n }\n } else if (mode === this.Mode_.WATERFALL) {\n // Add and remove shadows depending on scroll position.\n // Also add/remove auxiliary class for styling of the compact version of\n // the header.\n this.content_.addEventListener('scroll', this.contentScrollHandler_.bind(this));\n this.contentScrollHandler_();\n }\n }\n // Add drawer toggling button to our layout, if we have an openable drawer.\n if (this.drawer_) {\n var drawerButton = this.element_.querySelector('.' + this.CssClasses_.DRAWER_BTN);\n if (!drawerButton) {\n drawerButton = document.createElement('div');\n drawerButton.classList.add(this.CssClasses_.DRAWER_BTN);\n var drawerButtonIcon = document.createElement('i');\n drawerButtonIcon.classList.add(this.CssClasses_.ICON);\n drawerButtonIcon.textContent = this.Constant_.MENU_ICON;\n drawerButton.appendChild(drawerButtonIcon);\n }\n if (this.drawer_.classList.contains(this.CssClasses_.ON_LARGE_SCREEN)) {\n //If drawer has ON_LARGE_SCREEN class then add it to the drawer toggle button as well.\n drawerButton.classList.add(this.CssClasses_.ON_LARGE_SCREEN);\n } else if (this.drawer_.classList.contains(this.CssClasses_.ON_SMALL_SCREEN)) {\n //If drawer has ON_SMALL_SCREEN class then add it to the drawer toggle button as well.\n drawerButton.classList.add(this.CssClasses_.ON_SMALL_SCREEN);\n }\n drawerButton.addEventListener('click', this.drawerToggleHandler_.bind(this));\n // Add a class if the layout has a drawer, for altering the left padding.\n // Adds the HAS_DRAWER to the elements since this.header_ may or may\n // not be present.\n this.element_.classList.add(this.CssClasses_.HAS_DRAWER);\n // If we have a fixed header, add the button to the header rather than\n // the layout.\n if (this.element_.classList.contains(this.CssClasses_.FIXED_HEADER)) {\n this.header_.insertBefore(drawerButton, this.header_.firstChild);\n } else {\n this.element_.insertBefore(drawerButton, this.content_);\n }\n var obfuscator = document.createElement('div');\n obfuscator.classList.add(this.CssClasses_.OBFUSCATOR);\n this.element_.appendChild(obfuscator);\n obfuscator.addEventListener('click', this.drawerToggleHandler_.bind(this));\n this.obfuscator_ = obfuscator;\n }\n // Keep an eye on screen size, and add/remove auxiliary class for styling\n // of small screens.\n this.screenSizeMediaQuery_ = window.matchMedia(this.Constant_.MAX_WIDTH);\n this.screenSizeMediaQuery_.addListener(this.screenSizeHandler_.bind(this));\n this.screenSizeHandler_();\n // Initialize tabs, if any.\n if (this.header_ && this.tabBar_) {\n this.element_.classList.add(this.CssClasses_.HAS_TABS);\n var tabContainer = document.createElement('div');\n tabContainer.classList.add(this.CssClasses_.TAB_CONTAINER);\n this.header_.insertBefore(tabContainer, this.tabBar_);\n this.header_.removeChild(this.tabBar_);\n var leftButton = document.createElement('div');\n leftButton.classList.add(this.CssClasses_.TAB_BAR_BUTTON);\n leftButton.classList.add(this.CssClasses_.TAB_BAR_LEFT_BUTTON);\n var leftButtonIcon = document.createElement('i');\n leftButtonIcon.classList.add(this.CssClasses_.ICON);\n leftButtonIcon.textContent = this.Constant_.CHEVRON_LEFT;\n leftButton.appendChild(leftButtonIcon);\n leftButton.addEventListener('click', function () {\n this.tabBar_.scrollLeft -= this.Constant_.TAB_SCROLL_PIXELS;\n }.bind(this));\n var rightButton = document.createElement('div');\n rightButton.classList.add(this.CssClasses_.TAB_BAR_BUTTON);\n rightButton.classList.add(this.CssClasses_.TAB_BAR_RIGHT_BUTTON);\n var rightButtonIcon = document.createElement('i');\n rightButtonIcon.classList.add(this.CssClasses_.ICON);\n rightButtonIcon.textContent = this.Constant_.CHEVRON_RIGHT;\n rightButton.appendChild(rightButtonIcon);\n rightButton.addEventListener('click', function () {\n this.tabBar_.scrollLeft += this.Constant_.TAB_SCROLL_PIXELS;\n }.bind(this));\n tabContainer.appendChild(leftButton);\n tabContainer.appendChild(this.tabBar_);\n tabContainer.appendChild(rightButton);\n // Add and remove buttons depending on scroll position.\n var tabScrollHandler = function () {\n if (this.tabBar_.scrollLeft > 0) {\n leftButton.classList.add(this.CssClasses_.IS_ACTIVE);\n } else {\n leftButton.classList.remove(this.CssClasses_.IS_ACTIVE);\n }\n if (this.tabBar_.scrollLeft < this.tabBar_.scrollWidth - this.tabBar_.offsetWidth) {\n rightButton.classList.add(this.CssClasses_.IS_ACTIVE);\n } else {\n rightButton.classList.remove(this.CssClasses_.IS_ACTIVE);\n }\n }.bind(this);\n this.tabBar_.addEventListener('scroll', tabScrollHandler);\n tabScrollHandler();\n if (this.tabBar_.classList.contains(this.CssClasses_.JS_RIPPLE_EFFECT)) {\n this.tabBar_.classList.add(this.CssClasses_.RIPPLE_IGNORE_EVENTS);\n }\n // Select element tabs, document panels\n var tabs = this.tabBar_.querySelectorAll('.' + this.CssClasses_.TAB);\n var panels = this.content_.querySelectorAll('.' + this.CssClasses_.PANEL);\n // Create new tabs for each tab element\n for (var i = 0; i < tabs.length; i++) {\n new MaterialLayoutTab(tabs[i], tabs, panels, this);\n }\n }\n this.element_.classList.add(this.CssClasses_.IS_UPGRADED);\n }\n};\n/**\n * Constructor for an individual tab.\n *\n * @constructor\n * @param {HTMLElement} tab The HTML element for the tab.\n * @param {!Array} tabs Array with HTML elements for all tabs.\n * @param {!Array} panels Array with HTML elements for all panels.\n * @param {MaterialLayout} layout The MaterialLayout object that owns the tab.\n */\nfunction MaterialLayoutTab(tab, tabs, panels, layout) {\n if (layout.tabBar_.classList.contains(layout.CssClasses_.JS_RIPPLE_EFFECT)) {\n var rippleContainer = document.createElement('span');\n rippleContainer.classList.add(layout.CssClasses_.RIPPLE_CONTAINER);\n rippleContainer.classList.add(layout.CssClasses_.JS_RIPPLE_EFFECT);\n var ripple = document.createElement('span');\n ripple.classList.add(layout.CssClasses_.RIPPLE);\n rippleContainer.appendChild(ripple);\n tab.appendChild(rippleContainer);\n }\n tab.addEventListener('click', function (e) {\n e.preventDefault();\n var href = tab.href.split('#')[1];\n var panel = layout.content_.querySelector('#' + href);\n layout.resetTabState_(tabs);\n layout.resetPanelState_(panels);\n tab.classList.add(layout.CssClasses_.IS_ACTIVE);\n panel.classList.add(layout.CssClasses_.IS_ACTIVE);\n });\n}\n// The component registers itself. It can assume componentHandler is available\n// in the global scope.\ncomponentHandler.register({\n constructor: MaterialLayout,\n classAsString: 'MaterialLayout',\n cssClass: 'mdl-js-layout'\n});","// Source: https://github.com/darius/requestAnimationFrame/blob/master/requestAnimationFrame.js\n// Adapted from https://gist.github.com/paulirish/1579671 which derived from\n// http://paulirish.com/2011/requestanimationframe-for-smart-animating/\n// http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating\n// requestAnimationFrame polyfill by Erik Möller.\n// Fixes from Paul Irish, Tino Zijdel, Andrew Mao, Klemen Slavič, Darius Bacon\n// MIT license\nif (!Date.now) {\n /**\n * Date.now polyfill.\n * @return {number} the current Date\n */\n Date.now = function () {\n return new Date().getTime();\n };\n Date['now'] = Date.now;\n}\nvar vendors = [\n 'webkit',\n 'moz'\n];\nfor (var i = 0; i < vendors.length && !window.requestAnimationFrame; ++i) {\n var vp = vendors[i];\n window.requestAnimationFrame = window[vp + 'RequestAnimationFrame'];\n window.cancelAnimationFrame = window[vp + 'CancelAnimationFrame'] || window[vp + 'CancelRequestAnimationFrame'];\n window['requestAnimationFrame'] = window.requestAnimationFrame;\n window['cancelAnimationFrame'] = window.cancelAnimationFrame;\n}\nif (/iP(ad|hone|od).*OS 6/.test(window.navigator.userAgent) || !window.requestAnimationFrame || !window.cancelAnimationFrame) {\n var lastTime = 0;\n /**\n * requestAnimationFrame polyfill.\n * @param {!Function} callback the callback function.\n */\n window.requestAnimationFrame = function (callback) {\n var now = Date.now();\n var nextTime = Math.max(lastTime + 16, now);\n return setTimeout(function () {\n callback(lastTime = nextTime);\n }, nextTime - now);\n };\n window.cancelAnimationFrame = clearTimeout;\n window['requestAnimationFrame'] = window.requestAnimationFrame;\n window['cancelAnimationFrame'] = window.cancelAnimationFrame;\n}","/**\n * @license\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Class constructor for Button MDL component.\n * Implements MDL component design pattern defined at:\n * https://github.com/jasonmayes/mdl-component-design-pattern\n *\n * @param {HTMLElement} element The element that will be upgraded.\n */\nvar MaterialButton = function MaterialButton(element) {\n this.element_ = element;\n // Initialize instance.\n this.init();\n};\nwindow['MaterialButton'] = MaterialButton;\n/**\n * Store constants in one place so they can be updated easily.\n *\n * @enum {string | number}\n * @private\n */\nMaterialButton.prototype.Constant_ = {};\n/**\n * Store strings for class names defined by this component that are used in\n * JavaScript. This allows us to simply change it in one place should we\n * decide to modify at a later date.\n *\n * @enum {string}\n * @private\n */\nMaterialButton.prototype.CssClasses_ = {\n RIPPLE_EFFECT: 'mdl-js-ripple-effect',\n RIPPLE_CONTAINER: 'mdl-button__ripple-container',\n RIPPLE: 'mdl-ripple'\n};\n/**\n * Handle blur of element.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialButton.prototype.blurHandler_ = function (event) {\n if (event) {\n this.element_.blur();\n }\n};\n// Public methods.\n/**\n * Disable button.\n *\n * @public\n */\nMaterialButton.prototype.disable = function () {\n this.element_.disabled = true;\n};\nMaterialButton.prototype['disable'] = MaterialButton.prototype.disable;\n/**\n * Enable button.\n *\n * @public\n */\nMaterialButton.prototype.enable = function () {\n this.element_.disabled = false;\n};\nMaterialButton.prototype['enable'] = MaterialButton.prototype.enable;\n/**\n * Initialize element.\n */\nMaterialButton.prototype.init = function () {\n if (this.element_) {\n if (this.element_.classList.contains(this.CssClasses_.RIPPLE_EFFECT)) {\n var rippleContainer = document.createElement('span');\n rippleContainer.classList.add(this.CssClasses_.RIPPLE_CONTAINER);\n this.rippleElement_ = document.createElement('span');\n this.rippleElement_.classList.add(this.CssClasses_.RIPPLE);\n rippleContainer.appendChild(this.rippleElement_);\n this.boundRippleBlurHandler = this.blurHandler_.bind(this);\n this.rippleElement_.addEventListener('mouseup', this.boundRippleBlurHandler);\n this.element_.appendChild(rippleContainer);\n }\n this.boundButtonBlurHandler = this.blurHandler_.bind(this);\n this.element_.addEventListener('mouseup', this.boundButtonBlurHandler);\n this.element_.addEventListener('mouseleave', this.boundButtonBlurHandler);\n }\n};\n/**\n * Downgrade the element.\n *\n * @private\n */\nMaterialButton.prototype.mdlDowngrade_ = function () {\n if (this.rippleElement_) {\n this.rippleElement_.removeEventListener('mouseup', this.boundRippleBlurHandler);\n }\n this.element_.removeEventListener('mouseup', this.boundButtonBlurHandler);\n this.element_.removeEventListener('mouseleave', this.boundButtonBlurHandler);\n};\n/**\n * Public alias for the downgrade method.\n *\n * @public\n */\nMaterialButton.prototype.mdlDowngrade = MaterialButton.prototype.mdlDowngrade_;\nMaterialButton.prototype['mdlDowngrade'] = MaterialButton.prototype.mdlDowngrade;\n// The component registers itself. It can assume componentHandler is available\n// in the global scope.\ncomponentHandler.register({\n constructor: MaterialButton,\n classAsString: 'MaterialButton',\n cssClass: 'mdl-js-button',\n widget: true\n});","/**\n * @license\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Class constructor for Checkbox MDL component.\n * Implements MDL component design pattern defined at:\n * https://github.com/jasonmayes/mdl-component-design-pattern\n *\n * @constructor\n * @param {HTMLElement} element The element that will be upgraded.\n */\nvar MaterialCheckbox = function MaterialCheckbox(element) {\n this.element_ = element;\n // Initialize instance.\n this.init();\n};\nwindow['MaterialCheckbox'] = MaterialCheckbox;\n/**\n * Store constants in one place so they can be updated easily.\n *\n * @enum {string | number}\n * @private\n */\nMaterialCheckbox.prototype.Constant_ = { TINY_TIMEOUT: 0.001 };\n/**\n * Store strings for class names defined by this component that are used in\n * JavaScript. This allows us to simply change it in one place should we\n * decide to modify at a later date.\n *\n * @enum {string}\n * @private\n */\nMaterialCheckbox.prototype.CssClasses_ = {\n INPUT: 'mdl-checkbox__input',\n BOX_OUTLINE: 'mdl-checkbox__box-outline',\n FOCUS_HELPER: 'mdl-checkbox__focus-helper',\n TICK_OUTLINE: 'mdl-checkbox__tick-outline',\n RIPPLE_EFFECT: 'mdl-js-ripple-effect',\n RIPPLE_IGNORE_EVENTS: 'mdl-js-ripple-effect--ignore-events',\n RIPPLE_CONTAINER: 'mdl-checkbox__ripple-container',\n RIPPLE_CENTER: 'mdl-ripple--center',\n RIPPLE: 'mdl-ripple',\n IS_FOCUSED: 'is-focused',\n IS_DISABLED: 'is-disabled',\n IS_CHECKED: 'is-checked',\n IS_UPGRADED: 'is-upgraded'\n};\n/**\n * Handle change of state.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialCheckbox.prototype.onChange_ = function (event) {\n this.updateClasses_();\n};\n/**\n * Handle focus of element.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialCheckbox.prototype.onFocus_ = function (event) {\n this.element_.classList.add(this.CssClasses_.IS_FOCUSED);\n};\n/**\n * Handle lost focus of element.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialCheckbox.prototype.onBlur_ = function (event) {\n this.element_.classList.remove(this.CssClasses_.IS_FOCUSED);\n};\n/**\n * Handle mouseup.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialCheckbox.prototype.onMouseUp_ = function (event) {\n this.blur_();\n};\n/**\n * Handle class updates.\n *\n * @private\n */\nMaterialCheckbox.prototype.updateClasses_ = function () {\n this.checkDisabled();\n this.checkToggleState();\n};\n/**\n * Add blur.\n *\n * @private\n */\nMaterialCheckbox.prototype.blur_ = function () {\n // TODO: figure out why there's a focus event being fired after our blur,\n // so that we can avoid this hack.\n window.setTimeout(function () {\n this.inputElement_.blur();\n }.bind(this), this.Constant_.TINY_TIMEOUT);\n};\n// Public methods.\n/**\n * Check the inputs toggle state and update display.\n *\n * @public\n */\nMaterialCheckbox.prototype.checkToggleState = function () {\n if (this.inputElement_.checked) {\n this.element_.classList.add(this.CssClasses_.IS_CHECKED);\n } else {\n this.element_.classList.remove(this.CssClasses_.IS_CHECKED);\n }\n};\nMaterialCheckbox.prototype['checkToggleState'] = MaterialCheckbox.prototype.checkToggleState;\n/**\n * Check the inputs disabled state and update display.\n *\n * @public\n */\nMaterialCheckbox.prototype.checkDisabled = function () {\n if (this.inputElement_.disabled) {\n this.element_.classList.add(this.CssClasses_.IS_DISABLED);\n } else {\n this.element_.classList.remove(this.CssClasses_.IS_DISABLED);\n }\n};\nMaterialCheckbox.prototype['checkDisabled'] = MaterialCheckbox.prototype.checkDisabled;\n/**\n * Disable checkbox.\n *\n * @public\n */\nMaterialCheckbox.prototype.disable = function () {\n this.inputElement_.disabled = true;\n this.updateClasses_();\n};\nMaterialCheckbox.prototype['disable'] = MaterialCheckbox.prototype.disable;\n/**\n * Enable checkbox.\n *\n * @public\n */\nMaterialCheckbox.prototype.enable = function () {\n this.inputElement_.disabled = false;\n this.updateClasses_();\n};\nMaterialCheckbox.prototype['enable'] = MaterialCheckbox.prototype.enable;\n/**\n * Check checkbox.\n *\n * @public\n */\nMaterialCheckbox.prototype.check = function () {\n this.inputElement_.checked = true;\n this.updateClasses_();\n};\nMaterialCheckbox.prototype['check'] = MaterialCheckbox.prototype.check;\n/**\n * Uncheck checkbox.\n *\n * @public\n */\nMaterialCheckbox.prototype.uncheck = function () {\n this.inputElement_.checked = false;\n this.updateClasses_();\n};\nMaterialCheckbox.prototype['uncheck'] = MaterialCheckbox.prototype.uncheck;\n/**\n * Initialize element.\n */\nMaterialCheckbox.prototype.init = function () {\n if (this.element_) {\n this.inputElement_ = this.element_.querySelector('.' + this.CssClasses_.INPUT);\n var boxOutline = document.createElement('span');\n boxOutline.classList.add(this.CssClasses_.BOX_OUTLINE);\n var tickContainer = document.createElement('span');\n tickContainer.classList.add(this.CssClasses_.FOCUS_HELPER);\n var tickOutline = document.createElement('span');\n tickOutline.classList.add(this.CssClasses_.TICK_OUTLINE);\n boxOutline.appendChild(tickOutline);\n this.element_.appendChild(tickContainer);\n this.element_.appendChild(boxOutline);\n if (this.element_.classList.contains(this.CssClasses_.RIPPLE_EFFECT)) {\n this.element_.classList.add(this.CssClasses_.RIPPLE_IGNORE_EVENTS);\n this.rippleContainerElement_ = document.createElement('span');\n this.rippleContainerElement_.classList.add(this.CssClasses_.RIPPLE_CONTAINER);\n this.rippleContainerElement_.classList.add(this.CssClasses_.RIPPLE_EFFECT);\n this.rippleContainerElement_.classList.add(this.CssClasses_.RIPPLE_CENTER);\n this.boundRippleMouseUp = this.onMouseUp_.bind(this);\n this.rippleContainerElement_.addEventListener('mouseup', this.boundRippleMouseUp);\n var ripple = document.createElement('span');\n ripple.classList.add(this.CssClasses_.RIPPLE);\n this.rippleContainerElement_.appendChild(ripple);\n this.element_.appendChild(this.rippleContainerElement_);\n }\n this.boundInputOnChange = this.onChange_.bind(this);\n this.boundInputOnFocus = this.onFocus_.bind(this);\n this.boundInputOnBlur = this.onBlur_.bind(this);\n this.boundElementMouseUp = this.onMouseUp_.bind(this);\n this.inputElement_.addEventListener('change', this.boundInputOnChange);\n this.inputElement_.addEventListener('focus', this.boundInputOnFocus);\n this.inputElement_.addEventListener('blur', this.boundInputOnBlur);\n this.element_.addEventListener('mouseup', this.boundElementMouseUp);\n this.updateClasses_();\n this.element_.classList.add(this.CssClasses_.IS_UPGRADED);\n }\n};\n/**\n * Downgrade the component.\n *\n * @private\n */\nMaterialCheckbox.prototype.mdlDowngrade_ = function () {\n if (this.rippleContainerElement_) {\n this.rippleContainerElement_.removeEventListener('mouseup', this.boundRippleMouseUp);\n }\n this.inputElement_.removeEventListener('change', this.boundInputOnChange);\n this.inputElement_.removeEventListener('focus', this.boundInputOnFocus);\n this.inputElement_.removeEventListener('blur', this.boundInputOnBlur);\n this.element_.removeEventListener('mouseup', this.boundElementMouseUp);\n};\n/**\n * Public alias for the downgrade method.\n *\n * @public\n */\nMaterialCheckbox.prototype.mdlDowngrade = MaterialCheckbox.prototype.mdlDowngrade_;\nMaterialCheckbox.prototype['mdlDowngrade'] = MaterialCheckbox.prototype.mdlDowngrade;\n// The component registers itself. It can assume componentHandler is available\n// in the global scope.\ncomponentHandler.register({\n constructor: MaterialCheckbox,\n classAsString: 'MaterialCheckbox',\n cssClass: 'mdl-js-checkbox',\n widget: true\n});","/**\n * @license\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Class constructor for icon toggle MDL component.\n * Implements MDL component design pattern defined at:\n * https://github.com/jasonmayes/mdl-component-design-pattern\n *\n * @constructor\n * @param {HTMLElement} element The element that will be upgraded.\n */\nvar MaterialIconToggle = function MaterialIconToggle(element) {\n this.element_ = element;\n // Initialize instance.\n this.init();\n};\nwindow['MaterialIconToggle'] = MaterialIconToggle;\n/**\n * Store constants in one place so they can be updated easily.\n *\n * @enum {string | number}\n * @private\n */\nMaterialIconToggle.prototype.Constant_ = { TINY_TIMEOUT: 0.001 };\n/**\n * Store strings for class names defined by this component that are used in\n * JavaScript. This allows us to simply change it in one place should we\n * decide to modify at a later date.\n *\n * @enum {string}\n * @private\n */\nMaterialIconToggle.prototype.CssClasses_ = {\n INPUT: 'mdl-icon-toggle__input',\n JS_RIPPLE_EFFECT: 'mdl-js-ripple-effect',\n RIPPLE_IGNORE_EVENTS: 'mdl-js-ripple-effect--ignore-events',\n RIPPLE_CONTAINER: 'mdl-icon-toggle__ripple-container',\n RIPPLE_CENTER: 'mdl-ripple--center',\n RIPPLE: 'mdl-ripple',\n IS_FOCUSED: 'is-focused',\n IS_DISABLED: 'is-disabled',\n IS_CHECKED: 'is-checked'\n};\n/**\n * Handle change of state.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialIconToggle.prototype.onChange_ = function (event) {\n this.updateClasses_();\n};\n/**\n * Handle focus of element.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialIconToggle.prototype.onFocus_ = function (event) {\n this.element_.classList.add(this.CssClasses_.IS_FOCUSED);\n};\n/**\n * Handle lost focus of element.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialIconToggle.prototype.onBlur_ = function (event) {\n this.element_.classList.remove(this.CssClasses_.IS_FOCUSED);\n};\n/**\n * Handle mouseup.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialIconToggle.prototype.onMouseUp_ = function (event) {\n this.blur_();\n};\n/**\n * Handle class updates.\n *\n * @private\n */\nMaterialIconToggle.prototype.updateClasses_ = function () {\n this.checkDisabled();\n this.checkToggleState();\n};\n/**\n * Add blur.\n *\n * @private\n */\nMaterialIconToggle.prototype.blur_ = function () {\n // TODO: figure out why there's a focus event being fired after our blur,\n // so that we can avoid this hack.\n window.setTimeout(function () {\n this.inputElement_.blur();\n }.bind(this), this.Constant_.TINY_TIMEOUT);\n};\n// Public methods.\n/**\n * Check the inputs toggle state and update display.\n *\n * @public\n */\nMaterialIconToggle.prototype.checkToggleState = function () {\n if (this.inputElement_.checked) {\n this.element_.classList.add(this.CssClasses_.IS_CHECKED);\n } else {\n this.element_.classList.remove(this.CssClasses_.IS_CHECKED);\n }\n};\nMaterialIconToggle.prototype['checkToggleState'] = MaterialIconToggle.prototype.checkToggleState;\n/**\n * Check the inputs disabled state and update display.\n *\n * @public\n */\nMaterialIconToggle.prototype.checkDisabled = function () {\n if (this.inputElement_.disabled) {\n this.element_.classList.add(this.CssClasses_.IS_DISABLED);\n } else {\n this.element_.classList.remove(this.CssClasses_.IS_DISABLED);\n }\n};\nMaterialIconToggle.prototype['checkDisabled'] = MaterialIconToggle.prototype.checkDisabled;\n/**\n * Disable icon toggle.\n *\n * @public\n */\nMaterialIconToggle.prototype.disable = function () {\n this.inputElement_.disabled = true;\n this.updateClasses_();\n};\nMaterialIconToggle.prototype['disable'] = MaterialIconToggle.prototype.disable;\n/**\n * Enable icon toggle.\n *\n * @public\n */\nMaterialIconToggle.prototype.enable = function () {\n this.inputElement_.disabled = false;\n this.updateClasses_();\n};\nMaterialIconToggle.prototype['enable'] = MaterialIconToggle.prototype.enable;\n/**\n * Check icon toggle.\n *\n * @public\n */\nMaterialIconToggle.prototype.check = function () {\n this.inputElement_.checked = true;\n this.updateClasses_();\n};\nMaterialIconToggle.prototype['check'] = MaterialIconToggle.prototype.check;\n/**\n * Uncheck icon toggle.\n *\n * @public\n */\nMaterialIconToggle.prototype.uncheck = function () {\n this.inputElement_.checked = false;\n this.updateClasses_();\n};\nMaterialIconToggle.prototype['uncheck'] = MaterialIconToggle.prototype.uncheck;\n/**\n * Initialize element.\n */\nMaterialIconToggle.prototype.init = function () {\n if (this.element_) {\n this.inputElement_ = this.element_.querySelector('.' + this.CssClasses_.INPUT);\n if (this.element_.classList.contains(this.CssClasses_.JS_RIPPLE_EFFECT)) {\n this.element_.classList.add(this.CssClasses_.RIPPLE_IGNORE_EVENTS);\n this.rippleContainerElement_ = document.createElement('span');\n this.rippleContainerElement_.classList.add(this.CssClasses_.RIPPLE_CONTAINER);\n this.rippleContainerElement_.classList.add(this.CssClasses_.JS_RIPPLE_EFFECT);\n this.rippleContainerElement_.classList.add(this.CssClasses_.RIPPLE_CENTER);\n this.boundRippleMouseUp = this.onMouseUp_.bind(this);\n this.rippleContainerElement_.addEventListener('mouseup', this.boundRippleMouseUp);\n var ripple = document.createElement('span');\n ripple.classList.add(this.CssClasses_.RIPPLE);\n this.rippleContainerElement_.appendChild(ripple);\n this.element_.appendChild(this.rippleContainerElement_);\n }\n this.boundInputOnChange = this.onChange_.bind(this);\n this.boundInputOnFocus = this.onFocus_.bind(this);\n this.boundInputOnBlur = this.onBlur_.bind(this);\n this.boundElementOnMouseUp = this.onMouseUp_.bind(this);\n this.inputElement_.addEventListener('change', this.boundInputOnChange);\n this.inputElement_.addEventListener('focus', this.boundInputOnFocus);\n this.inputElement_.addEventListener('blur', this.boundInputOnBlur);\n this.element_.addEventListener('mouseup', this.boundElementOnMouseUp);\n this.updateClasses_();\n this.element_.classList.add('is-upgraded');\n }\n};\n/**\n * Downgrade the component\n *\n * @private\n */\nMaterialIconToggle.prototype.mdlDowngrade_ = function () {\n if (this.rippleContainerElement_) {\n this.rippleContainerElement_.removeEventListener('mouseup', this.boundRippleMouseUp);\n }\n this.inputElement_.removeEventListener('change', this.boundInputOnChange);\n this.inputElement_.removeEventListener('focus', this.boundInputOnFocus);\n this.inputElement_.removeEventListener('blur', this.boundInputOnBlur);\n this.element_.removeEventListener('mouseup', this.boundElementOnMouseUp);\n};\n/**\n * Public alias for the downgrade method.\n *\n * @public\n */\nMaterialIconToggle.prototype.mdlDowngrade = MaterialIconToggle.prototype.mdlDowngrade_;\nMaterialIconToggle.prototype['mdlDowngrade'] = MaterialIconToggle.prototype.mdlDowngrade;\n// The component registers itself. It can assume componentHandler is available\n// in the global scope.\ncomponentHandler.register({\n constructor: MaterialIconToggle,\n classAsString: 'MaterialIconToggle',\n cssClass: 'mdl-js-icon-toggle',\n widget: true\n});","/**\n * @license\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Class constructor for dropdown MDL component.\n * Implements MDL component design pattern defined at:\n * https://github.com/jasonmayes/mdl-component-design-pattern\n *\n * @constructor\n * @param {HTMLElement} element The element that will be upgraded.\n */\nvar MaterialMenu = function MaterialMenu(element) {\n this.element_ = element;\n // Initialize instance.\n this.init();\n};\nwindow['MaterialMenu'] = MaterialMenu;\n/**\n * Store constants in one place so they can be updated easily.\n *\n * @enum {string | number}\n * @private\n */\nMaterialMenu.prototype.Constant_ = {\n // Total duration of the menu animation.\n TRANSITION_DURATION_SECONDS: 0.3,\n // The fraction of the total duration we want to use for menu item animations.\n TRANSITION_DURATION_FRACTION: 0.8,\n // How long the menu stays open after choosing an option (so the user can see\n // the ripple).\n CLOSE_TIMEOUT: 150\n};\n/**\n * Keycodes, for code readability.\n *\n * @enum {number}\n * @private\n */\nMaterialMenu.prototype.Keycodes_ = {\n ENTER: 13,\n ESCAPE: 27,\n SPACE: 32,\n UP_ARROW: 38,\n DOWN_ARROW: 40\n};\n/**\n * Store strings for class names defined by this component that are used in\n * JavaScript. This allows us to simply change it in one place should we\n * decide to modify at a later date.\n *\n * @enum {string}\n * @private\n */\nMaterialMenu.prototype.CssClasses_ = {\n CONTAINER: 'mdl-menu__container',\n OUTLINE: 'mdl-menu__outline',\n ITEM: 'mdl-menu__item',\n ITEM_RIPPLE_CONTAINER: 'mdl-menu__item-ripple-container',\n RIPPLE_EFFECT: 'mdl-js-ripple-effect',\n RIPPLE_IGNORE_EVENTS: 'mdl-js-ripple-effect--ignore-events',\n RIPPLE: 'mdl-ripple',\n // Statuses\n IS_UPGRADED: 'is-upgraded',\n IS_VISIBLE: 'is-visible',\n IS_ANIMATING: 'is-animating',\n // Alignment options\n BOTTOM_LEFT: 'mdl-menu--bottom-left',\n // This is the default.\n BOTTOM_RIGHT: 'mdl-menu--bottom-right',\n TOP_LEFT: 'mdl-menu--top-left',\n TOP_RIGHT: 'mdl-menu--top-right',\n UNALIGNED: 'mdl-menu--unaligned'\n};\n/**\n * Initialize element.\n */\nMaterialMenu.prototype.init = function () {\n if (this.element_) {\n // Create container for the menu.\n var container = document.createElement('div');\n container.classList.add(this.CssClasses_.CONTAINER);\n this.element_.parentElement.insertBefore(container, this.element_);\n this.element_.parentElement.removeChild(this.element_);\n container.appendChild(this.element_);\n this.container_ = container;\n // Create outline for the menu (shadow and background).\n var outline = document.createElement('div');\n outline.classList.add(this.CssClasses_.OUTLINE);\n this.outline_ = outline;\n container.insertBefore(outline, this.element_);\n // Find the \"for\" element and bind events to it.\n var forElId = this.element_.getAttribute('for');\n var forEl = null;\n if (forElId) {\n forEl = document.getElementById(forElId);\n if (forEl) {\n this.forElement_ = forEl;\n forEl.addEventListener('click', this.handleForClick_.bind(this));\n forEl.addEventListener('keydown', this.handleForKeyboardEvent_.bind(this));\n }\n }\n var items = this.element_.querySelectorAll('.' + this.CssClasses_.ITEM);\n this.boundItemKeydown_ = this.handleItemKeyboardEvent_.bind(this);\n this.boundItemClick_ = this.handleItemClick_.bind(this);\n for (var i = 0; i < items.length; i++) {\n // Add a listener to each menu item.\n items[i].addEventListener('click', this.boundItemClick_);\n // Add a tab index to each menu item.\n items[i].tabIndex = '-1';\n // Add a keyboard listener to each menu item.\n items[i].addEventListener('keydown', this.boundItemKeydown_);\n }\n // Add ripple classes to each item, if the user has enabled ripples.\n if (this.element_.classList.contains(this.CssClasses_.RIPPLE_EFFECT)) {\n this.element_.classList.add(this.CssClasses_.RIPPLE_IGNORE_EVENTS);\n for (i = 0; i < items.length; i++) {\n var item = items[i];\n var rippleContainer = document.createElement('span');\n rippleContainer.classList.add(this.CssClasses_.ITEM_RIPPLE_CONTAINER);\n var ripple = document.createElement('span');\n ripple.classList.add(this.CssClasses_.RIPPLE);\n rippleContainer.appendChild(ripple);\n item.appendChild(rippleContainer);\n item.classList.add(this.CssClasses_.RIPPLE_EFFECT);\n }\n }\n // Copy alignment classes to the container, so the outline can use them.\n if (this.element_.classList.contains(this.CssClasses_.BOTTOM_LEFT)) {\n this.outline_.classList.add(this.CssClasses_.BOTTOM_LEFT);\n }\n if (this.element_.classList.contains(this.CssClasses_.BOTTOM_RIGHT)) {\n this.outline_.classList.add(this.CssClasses_.BOTTOM_RIGHT);\n }\n if (this.element_.classList.contains(this.CssClasses_.TOP_LEFT)) {\n this.outline_.classList.add(this.CssClasses_.TOP_LEFT);\n }\n if (this.element_.classList.contains(this.CssClasses_.TOP_RIGHT)) {\n this.outline_.classList.add(this.CssClasses_.TOP_RIGHT);\n }\n if (this.element_.classList.contains(this.CssClasses_.UNALIGNED)) {\n this.outline_.classList.add(this.CssClasses_.UNALIGNED);\n }\n container.classList.add(this.CssClasses_.IS_UPGRADED);\n }\n};\n/**\n * Handles a click on the \"for\" element, by positioning the menu and then\n * toggling it.\n *\n * @param {Event} evt The event that fired.\n * @private\n */\nMaterialMenu.prototype.handleForClick_ = function (evt) {\n if (this.element_ && this.forElement_) {\n var rect = this.forElement_.getBoundingClientRect();\n var forRect = this.forElement_.parentElement.getBoundingClientRect();\n if (this.element_.classList.contains(this.CssClasses_.UNALIGNED)) {\n } else if (this.element_.classList.contains(this.CssClasses_.BOTTOM_RIGHT)) {\n // Position below the \"for\" element, aligned to its right.\n this.container_.style.right = forRect.right - rect.right + 'px';\n this.container_.style.top = this.forElement_.offsetTop + this.forElement_.offsetHeight + 'px';\n } else if (this.element_.classList.contains(this.CssClasses_.TOP_LEFT)) {\n // Position above the \"for\" element, aligned to its left.\n this.container_.style.left = this.forElement_.offsetLeft + 'px';\n this.container_.style.bottom = forRect.bottom - rect.top + 'px';\n } else if (this.element_.classList.contains(this.CssClasses_.TOP_RIGHT)) {\n // Position above the \"for\" element, aligned to its right.\n this.container_.style.right = forRect.right - rect.right + 'px';\n this.container_.style.bottom = forRect.bottom - rect.top + 'px';\n } else {\n // Default: position below the \"for\" element, aligned to its left.\n this.container_.style.left = this.forElement_.offsetLeft + 'px';\n this.container_.style.top = this.forElement_.offsetTop + this.forElement_.offsetHeight + 'px';\n }\n }\n this.toggle(evt);\n};\n/**\n * Handles a keyboard event on the \"for\" element.\n *\n * @param {Event} evt The event that fired.\n * @private\n */\nMaterialMenu.prototype.handleForKeyboardEvent_ = function (evt) {\n if (this.element_ && this.container_ && this.forElement_) {\n var items = this.element_.querySelectorAll('.' + this.CssClasses_.ITEM + ':not([disabled])');\n if (items && items.length > 0 && this.container_.classList.contains(this.CssClasses_.IS_VISIBLE)) {\n if (evt.keyCode === this.Keycodes_.UP_ARROW) {\n evt.preventDefault();\n items[items.length - 1].focus();\n } else if (evt.keyCode === this.Keycodes_.DOWN_ARROW) {\n evt.preventDefault();\n items[0].focus();\n }\n }\n }\n};\n/**\n * Handles a keyboard event on an item.\n *\n * @param {Event} evt The event that fired.\n * @private\n */\nMaterialMenu.prototype.handleItemKeyboardEvent_ = function (evt) {\n if (this.element_ && this.container_) {\n var items = this.element_.querySelectorAll('.' + this.CssClasses_.ITEM + ':not([disabled])');\n if (items && items.length > 0 && this.container_.classList.contains(this.CssClasses_.IS_VISIBLE)) {\n var currentIndex = Array.prototype.slice.call(items).indexOf(evt.target);\n if (evt.keyCode === this.Keycodes_.UP_ARROW) {\n evt.preventDefault();\n if (currentIndex > 0) {\n items[currentIndex - 1].focus();\n } else {\n items[items.length - 1].focus();\n }\n } else if (evt.keyCode === this.Keycodes_.DOWN_ARROW) {\n evt.preventDefault();\n if (items.length > currentIndex + 1) {\n items[currentIndex + 1].focus();\n } else {\n items[0].focus();\n }\n } else if (evt.keyCode === this.Keycodes_.SPACE || evt.keyCode === this.Keycodes_.ENTER) {\n evt.preventDefault();\n // Send mousedown and mouseup to trigger ripple.\n var e = new MouseEvent('mousedown');\n evt.target.dispatchEvent(e);\n e = new MouseEvent('mouseup');\n evt.target.dispatchEvent(e);\n // Send click.\n evt.target.click();\n } else if (evt.keyCode === this.Keycodes_.ESCAPE) {\n evt.preventDefault();\n this.hide();\n }\n }\n }\n};\n/**\n * Handles a click event on an item.\n *\n * @param {Event} evt The event that fired.\n * @private\n */\nMaterialMenu.prototype.handleItemClick_ = function (evt) {\n if (evt.target.hasAttribute('disabled')) {\n evt.stopPropagation();\n } else {\n // Wait some time before closing menu, so the user can see the ripple.\n this.closing_ = true;\n window.setTimeout(function (evt) {\n this.hide();\n this.closing_ = false;\n }.bind(this), this.Constant_.CLOSE_TIMEOUT);\n }\n};\n/**\n * Calculates the initial clip (for opening the menu) or final clip (for closing\n * it), and applies it. This allows us to animate from or to the correct point,\n * that is, the point it's aligned to in the \"for\" element.\n *\n * @param {number} height Height of the clip rectangle\n * @param {number} width Width of the clip rectangle\n * @private\n */\nMaterialMenu.prototype.applyClip_ = function (height, width) {\n if (this.element_.classList.contains(this.CssClasses_.UNALIGNED)) {\n // Do not clip.\n this.element_.style.clip = '';\n } else if (this.element_.classList.contains(this.CssClasses_.BOTTOM_RIGHT)) {\n // Clip to the top right corner of the menu.\n this.element_.style.clip = 'rect(0 ' + width + 'px ' + '0 ' + width + 'px)';\n } else if (this.element_.classList.contains(this.CssClasses_.TOP_LEFT)) {\n // Clip to the bottom left corner of the menu.\n this.element_.style.clip = 'rect(' + height + 'px 0 ' + height + 'px 0)';\n } else if (this.element_.classList.contains(this.CssClasses_.TOP_RIGHT)) {\n // Clip to the bottom right corner of the menu.\n this.element_.style.clip = 'rect(' + height + 'px ' + width + 'px ' + height + 'px ' + width + 'px)';\n } else {\n // Default: do not clip (same as clipping to the top left corner).\n this.element_.style.clip = '';\n }\n};\n/**\n * Adds an event listener to clean up after the animation ends.\n *\n * @private\n */\nMaterialMenu.prototype.addAnimationEndListener_ = function () {\n var cleanup = function () {\n this.element_.removeEventListener('transitionend', cleanup);\n this.element_.removeEventListener('webkitTransitionEnd', cleanup);\n this.element_.classList.remove(this.CssClasses_.IS_ANIMATING);\n }.bind(this);\n // Remove animation class once the transition is done.\n this.element_.addEventListener('transitionend', cleanup);\n this.element_.addEventListener('webkitTransitionEnd', cleanup);\n};\n/**\n * Displays the menu.\n *\n * @public\n */\nMaterialMenu.prototype.show = function (evt) {\n if (this.element_ && this.container_ && this.outline_) {\n // Measure the inner element.\n var height = this.element_.getBoundingClientRect().height;\n var width = this.element_.getBoundingClientRect().width;\n // Apply the inner element's size to the container and outline.\n this.container_.style.width = width + 'px';\n this.container_.style.height = height + 'px';\n this.outline_.style.width = width + 'px';\n this.outline_.style.height = height + 'px';\n var transitionDuration = this.Constant_.TRANSITION_DURATION_SECONDS * this.Constant_.TRANSITION_DURATION_FRACTION;\n // Calculate transition delays for individual menu items, so that they fade\n // in one at a time.\n var items = this.element_.querySelectorAll('.' + this.CssClasses_.ITEM);\n for (var i = 0; i < items.length; i++) {\n var itemDelay = null;\n if (this.element_.classList.contains(this.CssClasses_.TOP_LEFT) || this.element_.classList.contains(this.CssClasses_.TOP_RIGHT)) {\n itemDelay = (height - items[i].offsetTop - items[i].offsetHeight) / height * transitionDuration + 's';\n } else {\n itemDelay = items[i].offsetTop / height * transitionDuration + 's';\n }\n items[i].style.transitionDelay = itemDelay;\n }\n // Apply the initial clip to the text before we start animating.\n this.applyClip_(height, width);\n // Wait for the next frame, turn on animation, and apply the final clip.\n // Also make it visible. This triggers the transitions.\n window.requestAnimationFrame(function () {\n this.element_.classList.add(this.CssClasses_.IS_ANIMATING);\n this.element_.style.clip = 'rect(0 ' + width + 'px ' + height + 'px 0)';\n this.container_.classList.add(this.CssClasses_.IS_VISIBLE);\n }.bind(this));\n // Clean up after the animation is complete.\n this.addAnimationEndListener_();\n // Add a click listener to the document, to close the menu.\n var callback = function (e) {\n // Check to see if the document is processing the same event that\n // displayed the menu in the first place. If so, do nothing.\n // Also check to see if the menu is in the process of closing itself, and\n // do nothing in that case.\n // Also check if the clicked element is a menu item\n // if so, do nothing.\n if (e !== evt && !this.closing_ && e.target.parentNode !== this.element_) {\n document.removeEventListener('click', callback);\n this.hide();\n }\n }.bind(this);\n document.addEventListener('click', callback);\n }\n};\nMaterialMenu.prototype['show'] = MaterialMenu.prototype.show;\n/**\n * Hides the menu.\n *\n * @public\n */\nMaterialMenu.prototype.hide = function () {\n if (this.element_ && this.container_ && this.outline_) {\n var items = this.element_.querySelectorAll('.' + this.CssClasses_.ITEM);\n // Remove all transition delays; menu items fade out concurrently.\n for (var i = 0; i < items.length; i++) {\n items[i].style.transitionDelay = null;\n }\n // Measure the inner element.\n var rect = this.element_.getBoundingClientRect();\n var height = rect.height;\n var width = rect.width;\n // Turn on animation, and apply the final clip. Also make invisible.\n // This triggers the transitions.\n this.element_.classList.add(this.CssClasses_.IS_ANIMATING);\n this.applyClip_(height, width);\n this.container_.classList.remove(this.CssClasses_.IS_VISIBLE);\n // Clean up after the animation is complete.\n this.addAnimationEndListener_();\n }\n};\nMaterialMenu.prototype['hide'] = MaterialMenu.prototype.hide;\n/**\n * Displays or hides the menu, depending on current state.\n *\n * @public\n */\nMaterialMenu.prototype.toggle = function (evt) {\n if (this.container_.classList.contains(this.CssClasses_.IS_VISIBLE)) {\n this.hide();\n } else {\n this.show(evt);\n }\n};\nMaterialMenu.prototype['toggle'] = MaterialMenu.prototype.toggle;\n/**\n * Downgrade the component.\n *\n * @private\n */\nMaterialMenu.prototype.mdlDowngrade_ = function () {\n var items = this.element_.querySelectorAll('.' + this.CssClasses_.ITEM);\n for (var i = 0; i < items.length; i++) {\n items[i].removeEventListener('click', this.boundItemClick_);\n items[i].removeEventListener('keydown', this.boundItemKeydown_);\n }\n};\n/**\n * Public alias for the downgrade method.\n *\n * @public\n */\nMaterialMenu.prototype.mdlDowngrade = MaterialMenu.prototype.mdlDowngrade_;\nMaterialMenu.prototype['mdlDowngrade'] = MaterialMenu.prototype.mdlDowngrade;\n// The component registers itself. It can assume componentHandler is available\n// in the global scope.\ncomponentHandler.register({\n constructor: MaterialMenu,\n classAsString: 'MaterialMenu',\n cssClass: 'mdl-js-menu',\n widget: true\n});","/**\n * @license\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Class constructor for Progress MDL component.\n * Implements MDL component design pattern defined at:\n * https://github.com/jasonmayes/mdl-component-design-pattern\n *\n * @constructor\n * @param {HTMLElement} element The element that will be upgraded.\n */\nvar MaterialProgress = function MaterialProgress(element) {\n this.element_ = element;\n // Initialize instance.\n this.init();\n};\nwindow['MaterialProgress'] = MaterialProgress;\n/**\n * Store constants in one place so they can be updated easily.\n *\n * @enum {string | number}\n * @private\n */\nMaterialProgress.prototype.Constant_ = {};\n/**\n * Store strings for class names defined by this component that are used in\n * JavaScript. This allows us to simply change it in one place should we\n * decide to modify at a later date.\n *\n * @enum {string}\n * @private\n */\nMaterialProgress.prototype.CssClasses_ = { INDETERMINATE_CLASS: 'mdl-progress__indeterminate' };\n/**\n * Set the current progress of the progressbar.\n *\n * @param {number} p Percentage of the progress (0-100)\n * @public\n */\nMaterialProgress.prototype.setProgress = function (p) {\n if (this.element_.classList.contains(this.CssClasses_.INDETERMINATE_CLASS)) {\n return;\n }\n this.progressbar_.style.width = p + '%';\n};\nMaterialProgress.prototype['setProgress'] = MaterialProgress.prototype.setProgress;\n/**\n * Set the current progress of the buffer.\n *\n * @param {number} p Percentage of the buffer (0-100)\n * @public\n */\nMaterialProgress.prototype.setBuffer = function (p) {\n this.bufferbar_.style.width = p + '%';\n this.auxbar_.style.width = 100 - p + '%';\n};\nMaterialProgress.prototype['setBuffer'] = MaterialProgress.prototype.setBuffer;\n/**\n * Initialize element.\n */\nMaterialProgress.prototype.init = function () {\n if (this.element_) {\n var el = document.createElement('div');\n el.className = 'progressbar bar bar1';\n this.element_.appendChild(el);\n this.progressbar_ = el;\n el = document.createElement('div');\n el.className = 'bufferbar bar bar2';\n this.element_.appendChild(el);\n this.bufferbar_ = el;\n el = document.createElement('div');\n el.className = 'auxbar bar bar3';\n this.element_.appendChild(el);\n this.auxbar_ = el;\n this.progressbar_.style.width = '0%';\n this.bufferbar_.style.width = '100%';\n this.auxbar_.style.width = '0%';\n this.element_.classList.add('is-upgraded');\n }\n};\n/**\n * Downgrade the component\n *\n * @private\n */\nMaterialProgress.prototype.mdlDowngrade_ = function () {\n while (this.element_.firstChild) {\n this.element_.removeChild(this.element_.firstChild);\n }\n};\n/**\n * Public alias for the downgrade method.\n *\n * @public\n */\nMaterialProgress.prototype.mdlDowngrade = MaterialProgress.prototype.mdlDowngrade_;\nMaterialProgress.prototype['mdlDowngrade'] = MaterialProgress.prototype.mdlDowngrade;\n// The component registers itself. It can assume componentHandler is available\n// in the global scope.\ncomponentHandler.register({\n constructor: MaterialProgress,\n classAsString: 'MaterialProgress',\n cssClass: 'mdl-js-progress',\n widget: true\n});","/**\n * @license\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Class constructor for Radio MDL component.\n * Implements MDL component design pattern defined at:\n * https://github.com/jasonmayes/mdl-component-design-pattern\n *\n * @constructor\n * @param {HTMLElement} element The element that will be upgraded.\n */\nvar MaterialRadio = function MaterialRadio(element) {\n this.element_ = element;\n // Initialize instance.\n this.init();\n};\nwindow['MaterialRadio'] = MaterialRadio;\n/**\n * Store constants in one place so they can be updated easily.\n *\n * @enum {string | number}\n * @private\n */\nMaterialRadio.prototype.Constant_ = { TINY_TIMEOUT: 0.001 };\n/**\n * Store strings for class names defined by this component that are used in\n * JavaScript. This allows us to simply change it in one place should we\n * decide to modify at a later date.\n *\n * @enum {string}\n * @private\n */\nMaterialRadio.prototype.CssClasses_ = {\n IS_FOCUSED: 'is-focused',\n IS_DISABLED: 'is-disabled',\n IS_CHECKED: 'is-checked',\n IS_UPGRADED: 'is-upgraded',\n JS_RADIO: 'mdl-js-radio',\n RADIO_BTN: 'mdl-radio__button',\n RADIO_OUTER_CIRCLE: 'mdl-radio__outer-circle',\n RADIO_INNER_CIRCLE: 'mdl-radio__inner-circle',\n RIPPLE_EFFECT: 'mdl-js-ripple-effect',\n RIPPLE_IGNORE_EVENTS: 'mdl-js-ripple-effect--ignore-events',\n RIPPLE_CONTAINER: 'mdl-radio__ripple-container',\n RIPPLE_CENTER: 'mdl-ripple--center',\n RIPPLE: 'mdl-ripple'\n};\n/**\n * Handle change of state.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialRadio.prototype.onChange_ = function (event) {\n // Since other radio buttons don't get change events, we need to look for\n // them to update their classes.\n var radios = document.getElementsByClassName(this.CssClasses_.JS_RADIO);\n for (var i = 0; i < radios.length; i++) {\n var button = radios[i].querySelector('.' + this.CssClasses_.RADIO_BTN);\n // Different name == different group, so no point updating those.\n if (button.getAttribute('name') === this.btnElement_.getAttribute('name')) {\n radios[i]['MaterialRadio'].updateClasses_();\n }\n }\n};\n/**\n * Handle focus.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialRadio.prototype.onFocus_ = function (event) {\n this.element_.classList.add(this.CssClasses_.IS_FOCUSED);\n};\n/**\n * Handle lost focus.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialRadio.prototype.onBlur_ = function (event) {\n this.element_.classList.remove(this.CssClasses_.IS_FOCUSED);\n};\n/**\n * Handle mouseup.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialRadio.prototype.onMouseup_ = function (event) {\n this.blur_();\n};\n/**\n * Update classes.\n *\n * @private\n */\nMaterialRadio.prototype.updateClasses_ = function () {\n this.checkDisabled();\n this.checkToggleState();\n};\n/**\n * Add blur.\n *\n * @private\n */\nMaterialRadio.prototype.blur_ = function () {\n // TODO: figure out why there's a focus event being fired after our blur,\n // so that we can avoid this hack.\n window.setTimeout(function () {\n this.btnElement_.blur();\n }.bind(this), this.Constant_.TINY_TIMEOUT);\n};\n// Public methods.\n/**\n * Check the components disabled state.\n *\n * @public\n */\nMaterialRadio.prototype.checkDisabled = function () {\n if (this.btnElement_.disabled) {\n this.element_.classList.add(this.CssClasses_.IS_DISABLED);\n } else {\n this.element_.classList.remove(this.CssClasses_.IS_DISABLED);\n }\n};\nMaterialRadio.prototype['checkDisabled'] = MaterialRadio.prototype.checkDisabled;\n/**\n * Check the components toggled state.\n *\n * @public\n */\nMaterialRadio.prototype.checkToggleState = function () {\n if (this.btnElement_.checked) {\n this.element_.classList.add(this.CssClasses_.IS_CHECKED);\n } else {\n this.element_.classList.remove(this.CssClasses_.IS_CHECKED);\n }\n};\nMaterialRadio.prototype['checkToggleState'] = MaterialRadio.prototype.checkToggleState;\n/**\n * Disable radio.\n *\n * @public\n */\nMaterialRadio.prototype.disable = function () {\n this.btnElement_.disabled = true;\n this.updateClasses_();\n};\nMaterialRadio.prototype['disable'] = MaterialRadio.prototype.disable;\n/**\n * Enable radio.\n *\n * @public\n */\nMaterialRadio.prototype.enable = function () {\n this.btnElement_.disabled = false;\n this.updateClasses_();\n};\nMaterialRadio.prototype['enable'] = MaterialRadio.prototype.enable;\n/**\n * Check radio.\n *\n * @public\n */\nMaterialRadio.prototype.check = function () {\n this.btnElement_.checked = true;\n this.updateClasses_();\n};\nMaterialRadio.prototype['check'] = MaterialRadio.prototype.check;\n/**\n * Uncheck radio.\n *\n * @public\n */\nMaterialRadio.prototype.uncheck = function () {\n this.btnElement_.checked = false;\n this.updateClasses_();\n};\nMaterialRadio.prototype['uncheck'] = MaterialRadio.prototype.uncheck;\n/**\n * Initialize element.\n */\nMaterialRadio.prototype.init = function () {\n if (this.element_) {\n this.btnElement_ = this.element_.querySelector('.' + this.CssClasses_.RADIO_BTN);\n this.boundChangeHandler_ = this.onChange_.bind(this);\n this.boundFocusHandler_ = this.onChange_.bind(this);\n this.boundBlurHandler_ = this.onBlur_.bind(this);\n this.boundMouseUpHandler_ = this.onMouseup_.bind(this);\n var outerCircle = document.createElement('span');\n outerCircle.classList.add(this.CssClasses_.RADIO_OUTER_CIRCLE);\n var innerCircle = document.createElement('span');\n innerCircle.classList.add(this.CssClasses_.RADIO_INNER_CIRCLE);\n this.element_.appendChild(outerCircle);\n this.element_.appendChild(innerCircle);\n var rippleContainer;\n if (this.element_.classList.contains(this.CssClasses_.RIPPLE_EFFECT)) {\n this.element_.classList.add(this.CssClasses_.RIPPLE_IGNORE_EVENTS);\n rippleContainer = document.createElement('span');\n rippleContainer.classList.add(this.CssClasses_.RIPPLE_CONTAINER);\n rippleContainer.classList.add(this.CssClasses_.RIPPLE_EFFECT);\n rippleContainer.classList.add(this.CssClasses_.RIPPLE_CENTER);\n rippleContainer.addEventListener('mouseup', this.boundMouseUpHandler_);\n var ripple = document.createElement('span');\n ripple.classList.add(this.CssClasses_.RIPPLE);\n rippleContainer.appendChild(ripple);\n this.element_.appendChild(rippleContainer);\n }\n this.btnElement_.addEventListener('change', this.boundChangeHandler_);\n this.btnElement_.addEventListener('focus', this.boundFocusHandler_);\n this.btnElement_.addEventListener('blur', this.boundBlurHandler_);\n this.element_.addEventListener('mouseup', this.boundMouseUpHandler_);\n this.updateClasses_();\n this.element_.classList.add(this.CssClasses_.IS_UPGRADED);\n }\n};\n/**\n * Downgrade the element.\n *\n * @private\n */\nMaterialRadio.prototype.mdlDowngrade_ = function () {\n var rippleContainer = this.element_.querySelector('.' + this.CssClasses_.RIPPLE_CONTAINER);\n this.btnElement_.removeEventListener('change', this.boundChangeHandler_);\n this.btnElement_.removeEventListener('focus', this.boundFocusHandler_);\n this.btnElement_.removeEventListener('blur', this.boundBlurHandler_);\n this.element_.removeEventListener('mouseup', this.boundMouseUpHandler_);\n if (rippleContainer) {\n rippleContainer.removeEventListener('mouseup', this.boundMouseUpHandler_);\n this.element_.removeChild(rippleContainer);\n }\n};\n/**\n * Public alias for the downgrade method.\n *\n * @public\n */\nMaterialRadio.prototype.mdlDowngrade = MaterialRadio.prototype.mdlDowngrade_;\nMaterialRadio.prototype['mdlDowngrade'] = MaterialRadio.prototype.mdlDowngrade;\n// The component registers itself. It can assume componentHandler is available\n// in the global scope.\ncomponentHandler.register({\n constructor: MaterialRadio,\n classAsString: 'MaterialRadio',\n cssClass: 'mdl-js-radio',\n widget: true\n});","/**\n * @license\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Class constructor for Slider MDL component.\n * Implements MDL component design pattern defined at:\n * https://github.com/jasonmayes/mdl-component-design-pattern\n *\n * @constructor\n * @param {HTMLElement} element The element that will be upgraded.\n */\nvar MaterialSlider = function MaterialSlider(element) {\n this.element_ = element;\n // Browser feature detection.\n this.isIE_ = window.navigator.msPointerEnabled;\n // Initialize instance.\n this.init();\n};\nwindow['MaterialSlider'] = MaterialSlider;\n/**\n * Store constants in one place so they can be updated easily.\n *\n * @enum {string | number}\n * @private\n */\nMaterialSlider.prototype.Constant_ = {};\n/**\n * Store strings for class names defined by this component that are used in\n * JavaScript. This allows us to simply change it in one place should we\n * decide to modify at a later date.\n *\n * @enum {string}\n * @private\n */\nMaterialSlider.prototype.CssClasses_ = {\n IE_CONTAINER: 'mdl-slider__ie-container',\n SLIDER_CONTAINER: 'mdl-slider__container',\n BACKGROUND_FLEX: 'mdl-slider__background-flex',\n BACKGROUND_LOWER: 'mdl-slider__background-lower',\n BACKGROUND_UPPER: 'mdl-slider__background-upper',\n IS_LOWEST_VALUE: 'is-lowest-value',\n IS_UPGRADED: 'is-upgraded'\n};\n/**\n * Handle input on element.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialSlider.prototype.onInput_ = function (event) {\n this.updateValueStyles_();\n};\n/**\n * Handle change on element.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialSlider.prototype.onChange_ = function (event) {\n this.updateValueStyles_();\n};\n/**\n * Handle mouseup on element.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialSlider.prototype.onMouseUp_ = function (event) {\n event.target.blur();\n};\n/**\n * Handle mousedown on container element.\n * This handler is purpose is to not require the use to click\n * exactly on the 2px slider element, as FireFox seems to be very\n * strict about this.\n *\n * @param {Event} event The event that fired.\n * @private\n * @suppress {missingProperties}\n */\nMaterialSlider.prototype.onContainerMouseDown_ = function (event) {\n // If this click is not on the parent element (but rather some child)\n // ignore. It may still bubble up.\n if (event.target !== this.element_.parentElement) {\n return;\n }\n // Discard the original event and create a new event that\n // is on the slider element.\n event.preventDefault();\n var newEvent = new MouseEvent('mousedown', {\n target: event.target,\n buttons: event.buttons,\n clientX: event.clientX,\n clientY: this.element_.getBoundingClientRect().y\n });\n this.element_.dispatchEvent(newEvent);\n};\n/**\n * Handle updating of values.\n *\n * @private\n */\nMaterialSlider.prototype.updateValueStyles_ = function () {\n // Calculate and apply percentages to div structure behind slider.\n var fraction = (this.element_.value - this.element_.min) / (this.element_.max - this.element_.min);\n if (fraction === 0) {\n this.element_.classList.add(this.CssClasses_.IS_LOWEST_VALUE);\n } else {\n this.element_.classList.remove(this.CssClasses_.IS_LOWEST_VALUE);\n }\n if (!this.isIE_) {\n this.backgroundLower_.style.flex = fraction;\n this.backgroundLower_.style.webkitFlex = fraction;\n this.backgroundUpper_.style.flex = 1 - fraction;\n this.backgroundUpper_.style.webkitFlex = 1 - fraction;\n }\n};\n// Public methods.\n/**\n * Disable slider.\n *\n * @public\n */\nMaterialSlider.prototype.disable = function () {\n this.element_.disabled = true;\n};\nMaterialSlider.prototype['disable'] = MaterialSlider.prototype.disable;\n/**\n * Enable slider.\n *\n * @public\n */\nMaterialSlider.prototype.enable = function () {\n this.element_.disabled = false;\n};\nMaterialSlider.prototype['enable'] = MaterialSlider.prototype.enable;\n/**\n * Update slider value.\n *\n * @param {number} value The value to which to set the control (optional).\n * @public\n */\nMaterialSlider.prototype.change = function (value) {\n if (typeof value !== 'undefined') {\n this.element_.value = value;\n }\n this.updateValueStyles_();\n};\nMaterialSlider.prototype['change'] = MaterialSlider.prototype.change;\n/**\n * Initialize element.\n */\nMaterialSlider.prototype.init = function () {\n if (this.element_) {\n if (this.isIE_) {\n // Since we need to specify a very large height in IE due to\n // implementation limitations, we add a parent here that trims it down to\n // a reasonable size.\n var containerIE = document.createElement('div');\n containerIE.classList.add(this.CssClasses_.IE_CONTAINER);\n this.element_.parentElement.insertBefore(containerIE, this.element_);\n this.element_.parentElement.removeChild(this.element_);\n containerIE.appendChild(this.element_);\n } else {\n // For non-IE browsers, we need a div structure that sits behind the\n // slider and allows us to style the left and right sides of it with\n // different colors.\n var container = document.createElement('div');\n container.classList.add(this.CssClasses_.SLIDER_CONTAINER);\n this.element_.parentElement.insertBefore(container, this.element_);\n this.element_.parentElement.removeChild(this.element_);\n container.appendChild(this.element_);\n var backgroundFlex = document.createElement('div');\n backgroundFlex.classList.add(this.CssClasses_.BACKGROUND_FLEX);\n container.appendChild(backgroundFlex);\n this.backgroundLower_ = document.createElement('div');\n this.backgroundLower_.classList.add(this.CssClasses_.BACKGROUND_LOWER);\n backgroundFlex.appendChild(this.backgroundLower_);\n this.backgroundUpper_ = document.createElement('div');\n this.backgroundUpper_.classList.add(this.CssClasses_.BACKGROUND_UPPER);\n backgroundFlex.appendChild(this.backgroundUpper_);\n }\n this.boundInputHandler = this.onInput_.bind(this);\n this.boundChangeHandler = this.onChange_.bind(this);\n this.boundMouseUpHandler = this.onMouseUp_.bind(this);\n this.boundContainerMouseDownHandler = this.onContainerMouseDown_.bind(this);\n this.element_.addEventListener('input', this.boundInputHandler);\n this.element_.addEventListener('change', this.boundChangeHandler);\n this.element_.addEventListener('mouseup', this.boundMouseUpHandler);\n this.element_.parentElement.addEventListener('mousedown', this.boundContainerMouseDownHandler);\n this.updateValueStyles_();\n this.element_.classList.add(this.CssClasses_.IS_UPGRADED);\n }\n};\n/**\n * Downgrade the component\n *\n * @private\n */\nMaterialSlider.prototype.mdlDowngrade_ = function () {\n this.element_.removeEventListener('input', this.boundInputHandler);\n this.element_.removeEventListener('change', this.boundChangeHandler);\n this.element_.removeEventListener('mouseup', this.boundMouseUpHandler);\n this.element_.parentElement.removeEventListener('mousedown', this.boundContainerMouseDownHandler);\n};\n/**\n * Public alias for the downgrade method.\n *\n * @public\n */\nMaterialSlider.prototype.mdlDowngrade = MaterialSlider.prototype.mdlDowngrade_;\nMaterialSlider.prototype['mdlDowngrade'] = MaterialSlider.prototype.mdlDowngrade;\n// The component registers itself. It can assume componentHandler is available\n// in the global scope.\ncomponentHandler.register({\n constructor: MaterialSlider,\n classAsString: 'MaterialSlider',\n cssClass: 'mdl-js-slider',\n widget: true\n});","/**\n * @license\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Class constructor for Spinner MDL component.\n * Implements MDL component design pattern defined at:\n * https://github.com/jasonmayes/mdl-component-design-pattern\n *\n * @param {HTMLElement} element The element that will be upgraded.\n * @constructor\n */\nvar MaterialSpinner = function MaterialSpinner(element) {\n this.element_ = element;\n // Initialize instance.\n this.init();\n};\nwindow['MaterialSpinner'] = MaterialSpinner;\n/**\n * Store constants in one place so they can be updated easily.\n *\n * @enum {string | number}\n * @private\n */\nMaterialSpinner.prototype.Constant_ = { MDL_SPINNER_LAYER_COUNT: 4 };\n/**\n * Store strings for class names defined by this component that are used in\n * JavaScript. This allows us to simply change it in one place should we\n * decide to modify at a later date.\n *\n * @enum {string}\n * @private\n */\nMaterialSpinner.prototype.CssClasses_ = {\n MDL_SPINNER_LAYER: 'mdl-spinner__layer',\n MDL_SPINNER_CIRCLE_CLIPPER: 'mdl-spinner__circle-clipper',\n MDL_SPINNER_CIRCLE: 'mdl-spinner__circle',\n MDL_SPINNER_GAP_PATCH: 'mdl-spinner__gap-patch',\n MDL_SPINNER_LEFT: 'mdl-spinner__left',\n MDL_SPINNER_RIGHT: 'mdl-spinner__right'\n};\n/**\n * Auxiliary method to create a spinner layer.\n *\n * @param {number} index Index of the layer to be created.\n * @public\n */\nMaterialSpinner.prototype.createLayer = function (index) {\n var layer = document.createElement('div');\n layer.classList.add(this.CssClasses_.MDL_SPINNER_LAYER);\n layer.classList.add(this.CssClasses_.MDL_SPINNER_LAYER + '-' + index);\n var leftClipper = document.createElement('div');\n leftClipper.classList.add(this.CssClasses_.MDL_SPINNER_CIRCLE_CLIPPER);\n leftClipper.classList.add(this.CssClasses_.MDL_SPINNER_LEFT);\n var gapPatch = document.createElement('div');\n gapPatch.classList.add(this.CssClasses_.MDL_SPINNER_GAP_PATCH);\n var rightClipper = document.createElement('div');\n rightClipper.classList.add(this.CssClasses_.MDL_SPINNER_CIRCLE_CLIPPER);\n rightClipper.classList.add(this.CssClasses_.MDL_SPINNER_RIGHT);\n var circleOwners = [\n leftClipper,\n gapPatch,\n rightClipper\n ];\n for (var i = 0; i < circleOwners.length; i++) {\n var circle = document.createElement('div');\n circle.classList.add(this.CssClasses_.MDL_SPINNER_CIRCLE);\n circleOwners[i].appendChild(circle);\n }\n layer.appendChild(leftClipper);\n layer.appendChild(gapPatch);\n layer.appendChild(rightClipper);\n this.element_.appendChild(layer);\n};\nMaterialSpinner.prototype['createLayer'] = MaterialSpinner.prototype.createLayer;\n/**\n * Stops the spinner animation.\n * Public method for users who need to stop the spinner for any reason.\n *\n * @public\n */\nMaterialSpinner.prototype.stop = function () {\n this.element_.classList.remove('is-active');\n};\nMaterialSpinner.prototype['stop'] = MaterialSpinner.prototype.stop;\n/**\n * Starts the spinner animation.\n * Public method for users who need to manually start the spinner for any reason\n * (instead of just adding the 'is-active' class to their markup).\n *\n * @public\n */\nMaterialSpinner.prototype.start = function () {\n this.element_.classList.add('is-active');\n};\nMaterialSpinner.prototype['start'] = MaterialSpinner.prototype.start;\n/**\n * Initialize element.\n */\nMaterialSpinner.prototype.init = function () {\n if (this.element_) {\n for (var i = 1; i <= this.Constant_.MDL_SPINNER_LAYER_COUNT; i++) {\n this.createLayer(i);\n }\n this.element_.classList.add('is-upgraded');\n }\n};\n// The component registers itself. It can assume componentHandler is available\n// in the global scope.\ncomponentHandler.register({\n constructor: MaterialSpinner,\n classAsString: 'MaterialSpinner',\n cssClass: 'mdl-js-spinner',\n widget: true\n});","/**\n * @license\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Class constructor for Checkbox MDL component.\n * Implements MDL component design pattern defined at:\n * https://github.com/jasonmayes/mdl-component-design-pattern\n *\n * @constructor\n * @param {HTMLElement} element The element that will be upgraded.\n */\nvar MaterialSwitch = function MaterialSwitch(element) {\n this.element_ = element;\n // Initialize instance.\n this.init();\n};\nwindow['MaterialSwitch'] = MaterialSwitch;\n/**\n * Store constants in one place so they can be updated easily.\n *\n * @enum {string | number}\n * @private\n */\nMaterialSwitch.prototype.Constant_ = { TINY_TIMEOUT: 0.001 };\n/**\n * Store strings for class names defined by this component that are used in\n * JavaScript. This allows us to simply change it in one place should we\n * decide to modify at a later date.\n *\n * @enum {string}\n * @private\n */\nMaterialSwitch.prototype.CssClasses_ = {\n INPUT: 'mdl-switch__input',\n TRACK: 'mdl-switch__track',\n THUMB: 'mdl-switch__thumb',\n FOCUS_HELPER: 'mdl-switch__focus-helper',\n RIPPLE_EFFECT: 'mdl-js-ripple-effect',\n RIPPLE_IGNORE_EVENTS: 'mdl-js-ripple-effect--ignore-events',\n RIPPLE_CONTAINER: 'mdl-switch__ripple-container',\n RIPPLE_CENTER: 'mdl-ripple--center',\n RIPPLE: 'mdl-ripple',\n IS_FOCUSED: 'is-focused',\n IS_DISABLED: 'is-disabled',\n IS_CHECKED: 'is-checked'\n};\n/**\n * Handle change of state.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialSwitch.prototype.onChange_ = function (event) {\n this.updateClasses_();\n};\n/**\n * Handle focus of element.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialSwitch.prototype.onFocus_ = function (event) {\n this.element_.classList.add(this.CssClasses_.IS_FOCUSED);\n};\n/**\n * Handle lost focus of element.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialSwitch.prototype.onBlur_ = function (event) {\n this.element_.classList.remove(this.CssClasses_.IS_FOCUSED);\n};\n/**\n * Handle mouseup.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialSwitch.prototype.onMouseUp_ = function (event) {\n this.blur_();\n};\n/**\n * Handle class updates.\n *\n * @private\n */\nMaterialSwitch.prototype.updateClasses_ = function () {\n this.checkDisabled();\n this.checkToggleState();\n};\n/**\n * Add blur.\n *\n * @private\n */\nMaterialSwitch.prototype.blur_ = function () {\n // TODO: figure out why there's a focus event being fired after our blur,\n // so that we can avoid this hack.\n window.setTimeout(function () {\n this.inputElement_.blur();\n }.bind(this), this.Constant_.TINY_TIMEOUT);\n};\n// Public methods.\n/**\n * Check the components disabled state.\n *\n * @public\n */\nMaterialSwitch.prototype.checkDisabled = function () {\n if (this.inputElement_.disabled) {\n this.element_.classList.add(this.CssClasses_.IS_DISABLED);\n } else {\n this.element_.classList.remove(this.CssClasses_.IS_DISABLED);\n }\n};\nMaterialSwitch.prototype['checkDisabled'] = MaterialSwitch.prototype.checkDisabled;\n/**\n * Check the components toggled state.\n *\n * @public\n */\nMaterialSwitch.prototype.checkToggleState = function () {\n if (this.inputElement_.checked) {\n this.element_.classList.add(this.CssClasses_.IS_CHECKED);\n } else {\n this.element_.classList.remove(this.CssClasses_.IS_CHECKED);\n }\n};\nMaterialSwitch.prototype['checkToggleState'] = MaterialSwitch.prototype.checkToggleState;\n/**\n * Disable switch.\n *\n * @public\n */\nMaterialSwitch.prototype.disable = function () {\n this.inputElement_.disabled = true;\n this.updateClasses_();\n};\nMaterialSwitch.prototype['disable'] = MaterialSwitch.prototype.disable;\n/**\n * Enable switch.\n *\n * @public\n */\nMaterialSwitch.prototype.enable = function () {\n this.inputElement_.disabled = false;\n this.updateClasses_();\n};\nMaterialSwitch.prototype['enable'] = MaterialSwitch.prototype.enable;\n/**\n * Activate switch.\n *\n * @public\n */\nMaterialSwitch.prototype.on = function () {\n this.inputElement_.checked = true;\n this.updateClasses_();\n};\nMaterialSwitch.prototype['on'] = MaterialSwitch.prototype.on;\n/**\n * Deactivate switch.\n *\n * @public\n */\nMaterialSwitch.prototype.off = function () {\n this.inputElement_.checked = false;\n this.updateClasses_();\n};\nMaterialSwitch.prototype['off'] = MaterialSwitch.prototype.off;\n/**\n * Initialize element.\n */\nMaterialSwitch.prototype.init = function () {\n if (this.element_) {\n this.inputElement_ = this.element_.querySelector('.' + this.CssClasses_.INPUT);\n var track = document.createElement('div');\n track.classList.add(this.CssClasses_.TRACK);\n var thumb = document.createElement('div');\n thumb.classList.add(this.CssClasses_.THUMB);\n var focusHelper = document.createElement('span');\n focusHelper.classList.add(this.CssClasses_.FOCUS_HELPER);\n thumb.appendChild(focusHelper);\n this.element_.appendChild(track);\n this.element_.appendChild(thumb);\n this.boundMouseUpHandler = this.onMouseUp_.bind(this);\n if (this.element_.classList.contains(this.CssClasses_.RIPPLE_EFFECT)) {\n this.element_.classList.add(this.CssClasses_.RIPPLE_IGNORE_EVENTS);\n this.rippleContainerElement_ = document.createElement('span');\n this.rippleContainerElement_.classList.add(this.CssClasses_.RIPPLE_CONTAINER);\n this.rippleContainerElement_.classList.add(this.CssClasses_.RIPPLE_EFFECT);\n this.rippleContainerElement_.classList.add(this.CssClasses_.RIPPLE_CENTER);\n this.rippleContainerElement_.addEventListener('mouseup', this.boundMouseUpHandler);\n var ripple = document.createElement('span');\n ripple.classList.add(this.CssClasses_.RIPPLE);\n this.rippleContainerElement_.appendChild(ripple);\n this.element_.appendChild(this.rippleContainerElement_);\n }\n this.boundChangeHandler = this.onChange_.bind(this);\n this.boundFocusHandler = this.onFocus_.bind(this);\n this.boundBlurHandler = this.onBlur_.bind(this);\n this.inputElement_.addEventListener('change', this.boundChangeHandler);\n this.inputElement_.addEventListener('focus', this.boundFocusHandler);\n this.inputElement_.addEventListener('blur', this.boundBlurHandler);\n this.element_.addEventListener('mouseup', this.boundMouseUpHandler);\n this.updateClasses_();\n this.element_.classList.add('is-upgraded');\n }\n};\n/**\n * Downgrade the component.\n *\n * @private\n */\nMaterialSwitch.prototype.mdlDowngrade_ = function () {\n if (this.rippleContainerElement_) {\n this.rippleContainerElement_.removeEventListener('mouseup', this.boundMouseUpHandler);\n }\n this.inputElement_.removeEventListener('change', this.boundChangeHandler);\n this.inputElement_.removeEventListener('focus', this.boundFocusHandler);\n this.inputElement_.removeEventListener('blur', this.boundBlurHandler);\n this.element_.removeEventListener('mouseup', this.boundMouseUpHandler);\n};\n/**\n * Public alias for the downgrade method.\n *\n * @public\n */\nMaterialSwitch.prototype.mdlDowngrade = MaterialSwitch.prototype.mdlDowngrade_;\nMaterialSwitch.prototype['mdlDowngrade'] = MaterialSwitch.prototype.mdlDowngrade;\n// The component registers itself. It can assume componentHandler is available\n// in the global scope.\ncomponentHandler.register({\n constructor: MaterialSwitch,\n classAsString: 'MaterialSwitch',\n cssClass: 'mdl-js-switch',\n widget: true\n});","/**\n * @license\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Class constructor for Textfield MDL component.\n * Implements MDL component design pattern defined at:\n * https://github.com/jasonmayes/mdl-component-design-pattern\n *\n * @constructor\n * @param {HTMLElement} element The element that will be upgraded.\n */\nvar MaterialTextfield = function MaterialTextfield(element) {\n this.element_ = element;\n this.maxRows = this.Constant_.NO_MAX_ROWS;\n // Initialize instance.\n this.init();\n};\nwindow['MaterialTextfield'] = MaterialTextfield;\n/**\n * Store constants in one place so they can be updated easily.\n *\n * @enum {string | number}\n * @private\n */\nMaterialTextfield.prototype.Constant_ = {\n NO_MAX_ROWS: -1,\n MAX_ROWS_ATTRIBUTE: 'maxrows'\n};\n/**\n * Store strings for class names defined by this component that are used in\n * JavaScript. This allows us to simply change it in one place should we\n * decide to modify at a later date.\n *\n * @enum {string}\n * @private\n */\nMaterialTextfield.prototype.CssClasses_ = {\n LABEL: 'mdl-textfield__label',\n INPUT: 'mdl-textfield__input',\n IS_DIRTY: 'is-dirty',\n IS_FOCUSED: 'is-focused',\n IS_DISABLED: 'is-disabled',\n IS_INVALID: 'is-invalid',\n IS_UPGRADED: 'is-upgraded'\n};\n/**\n * Handle input being entered.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialTextfield.prototype.onKeyDown_ = function (event) {\n var currentRowCount = event.target.value.split('\\n').length;\n if (event.keyCode === 13) {\n if (currentRowCount >= this.maxRows) {\n event.preventDefault();\n }\n }\n};\n/**\n * Handle focus.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialTextfield.prototype.onFocus_ = function (event) {\n this.element_.classList.add(this.CssClasses_.IS_FOCUSED);\n};\n/**\n * Handle lost focus.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialTextfield.prototype.onBlur_ = function (event) {\n this.element_.classList.remove(this.CssClasses_.IS_FOCUSED);\n};\n/**\n * Handle class updates.\n *\n * @private\n */\nMaterialTextfield.prototype.updateClasses_ = function () {\n this.checkDisabled();\n this.checkValidity();\n this.checkDirty();\n};\n// Public methods.\n/**\n * Check the disabled state and update field accordingly.\n *\n * @public\n */\nMaterialTextfield.prototype.checkDisabled = function () {\n if (this.input_.disabled) {\n this.element_.classList.add(this.CssClasses_.IS_DISABLED);\n } else {\n this.element_.classList.remove(this.CssClasses_.IS_DISABLED);\n }\n};\nMaterialTextfield.prototype['checkDisabled'] = MaterialTextfield.prototype.checkDisabled;\n/**\n * Check the validity state and update field accordingly.\n *\n * @public\n */\nMaterialTextfield.prototype.checkValidity = function () {\n if (this.input_.validity) {\n if (this.input_.validity.valid) {\n this.element_.classList.remove(this.CssClasses_.IS_INVALID);\n } else {\n this.element_.classList.add(this.CssClasses_.IS_INVALID);\n }\n }\n};\nMaterialTextfield.prototype['checkValidity'] = MaterialTextfield.prototype.checkValidity;\n/**\n * Check the dirty state and update field accordingly.\n *\n * @public\n */\nMaterialTextfield.prototype.checkDirty = function () {\n if (this.input_.value && this.input_.value.length > 0) {\n this.element_.classList.add(this.CssClasses_.IS_DIRTY);\n } else {\n this.element_.classList.remove(this.CssClasses_.IS_DIRTY);\n }\n};\nMaterialTextfield.prototype['checkDirty'] = MaterialTextfield.prototype.checkDirty;\n/**\n * Disable text field.\n *\n * @public\n */\nMaterialTextfield.prototype.disable = function () {\n this.input_.disabled = true;\n this.updateClasses_();\n};\nMaterialTextfield.prototype['disable'] = MaterialTextfield.prototype.disable;\n/**\n * Enable text field.\n *\n * @public\n */\nMaterialTextfield.prototype.enable = function () {\n this.input_.disabled = false;\n this.updateClasses_();\n};\nMaterialTextfield.prototype['enable'] = MaterialTextfield.prototype.enable;\n/**\n * Update text field value.\n *\n * @param {string} value The value to which to set the control (optional).\n * @public\n */\nMaterialTextfield.prototype.change = function (value) {\n this.input_.value = value || '';\n this.updateClasses_();\n};\nMaterialTextfield.prototype['change'] = MaterialTextfield.prototype.change;\n/**\n * Initialize element.\n */\nMaterialTextfield.prototype.init = function () {\n if (this.element_) {\n this.label_ = this.element_.querySelector('.' + this.CssClasses_.LABEL);\n this.input_ = this.element_.querySelector('.' + this.CssClasses_.INPUT);\n if (this.input_) {\n if (this.input_.hasAttribute(this.Constant_.MAX_ROWS_ATTRIBUTE)) {\n this.maxRows = parseInt(this.input_.getAttribute(this.Constant_.MAX_ROWS_ATTRIBUTE), 10);\n if (isNaN(this.maxRows)) {\n this.maxRows = this.Constant_.NO_MAX_ROWS;\n }\n }\n this.boundUpdateClassesHandler = this.updateClasses_.bind(this);\n this.boundFocusHandler = this.onFocus_.bind(this);\n this.boundBlurHandler = this.onBlur_.bind(this);\n this.input_.addEventListener('input', this.boundUpdateClassesHandler);\n this.input_.addEventListener('focus', this.boundFocusHandler);\n this.input_.addEventListener('blur', this.boundBlurHandler);\n if (this.maxRows !== this.Constant_.NO_MAX_ROWS) {\n // TODO: This should handle pasting multi line text.\n // Currently doesn't.\n this.boundKeyDownHandler = this.onKeyDown_.bind(this);\n this.input_.addEventListener('keydown', this.boundKeyDownHandler);\n }\n var invalid = this.element_.classList.contains(this.CssClasses_.IS_INVALID);\n this.updateClasses_();\n this.element_.classList.add(this.CssClasses_.IS_UPGRADED);\n if (invalid) {\n this.element_.classList.add(this.CssClasses_.IS_INVALID);\n }\n }\n }\n};\n/**\n * Downgrade the component\n *\n * @private\n */\nMaterialTextfield.prototype.mdlDowngrade_ = function () {\n this.input_.removeEventListener('input', this.boundUpdateClassesHandler);\n this.input_.removeEventListener('focus', this.boundFocusHandler);\n this.input_.removeEventListener('blur', this.boundBlurHandler);\n if (this.boundKeyDownHandler) {\n this.input_.removeEventListener('keydown', this.boundKeyDownHandler);\n }\n};\n/**\n * Public alias for the downgrade method.\n *\n * @public\n */\nMaterialTextfield.prototype.mdlDowngrade = MaterialTextfield.prototype.mdlDowngrade_;\nMaterialTextfield.prototype['mdlDowngrade'] = MaterialTextfield.prototype.mdlDowngrade;\n// The component registers itself. It can assume componentHandler is available\n// in the global scope.\ncomponentHandler.register({\n constructor: MaterialTextfield,\n classAsString: 'MaterialTextfield',\n cssClass: 'mdl-js-textfield',\n widget: true\n});","/**\n * @license\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Class constructor for Tooltip MDL component.\n * Implements MDL component design pattern defined at:\n * https://github.com/jasonmayes/mdl-component-design-pattern\n *\n * @constructor\n * @param {HTMLElement} element The element that will be upgraded.\n */\nvar MaterialTooltip = function MaterialTooltip(element) {\n this.element_ = element;\n // Initialize instance.\n this.init();\n};\nwindow['MaterialTooltip'] = MaterialTooltip;\n/**\n * Store constants in one place so they can be updated easily.\n *\n * @enum {string | number}\n * @private\n */\nMaterialTooltip.prototype.Constant_ = {};\n/**\n * Store strings for class names defined by this component that are used in\n * JavaScript. This allows us to simply change it in one place should we\n * decide to modify at a later date.\n *\n * @enum {string}\n * @private\n */\nMaterialTooltip.prototype.CssClasses_ = { IS_ACTIVE: 'is-active' };\n/**\n * Handle mouseenter for tooltip.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialTooltip.prototype.handleMouseEnter_ = function (event) {\n event.stopPropagation();\n var props = event.target.getBoundingClientRect();\n var left = props.left + props.width / 2;\n var marginLeft = -1 * (this.element_.offsetWidth / 2);\n if (left + marginLeft < 0) {\n this.element_.style.left = 0;\n this.element_.style.marginLeft = 0;\n } else {\n this.element_.style.left = left + 'px';\n this.element_.style.marginLeft = marginLeft + 'px';\n }\n this.element_.style.top = props.top + props.height + 10 + 'px';\n this.element_.classList.add(this.CssClasses_.IS_ACTIVE);\n window.addEventListener('scroll', this.boundMouseLeaveHandler, false);\n window.addEventListener('touchmove', this.boundMouseLeaveHandler, false);\n};\n/**\n * Handle mouseleave for tooltip.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialTooltip.prototype.handleMouseLeave_ = function (event) {\n event.stopPropagation();\n this.element_.classList.remove(this.CssClasses_.IS_ACTIVE);\n window.removeEventListener('scroll', this.boundMouseLeaveHandler);\n window.removeEventListener('touchmove', this.boundMouseLeaveHandler, false);\n};\n/**\n * Initialize element.\n */\nMaterialTooltip.prototype.init = function () {\n if (this.element_) {\n var forElId = this.element_.getAttribute('for');\n if (forElId) {\n this.forElement_ = document.getElementById(forElId);\n }\n if (this.forElement_) {\n // Tabindex needs to be set for `blur` events to be emitted\n if (!this.forElement_.hasAttribute('tabindex')) {\n this.forElement_.setAttribute('tabindex', '0');\n }\n this.boundMouseEnterHandler = this.handleMouseEnter_.bind(this);\n this.boundMouseLeaveHandler = this.handleMouseLeave_.bind(this);\n this.forElement_.addEventListener('mouseenter', this.boundMouseEnterHandler, false);\n this.forElement_.addEventListener('click', this.boundMouseEnterHandler, false);\n this.forElement_.addEventListener('blur', this.boundMouseLeaveHandler);\n this.forElement_.addEventListener('touchstart', this.boundMouseEnterHandler, false);\n this.forElement_.addEventListener('mouseleave', this.boundMouseLeaveHandler);\n }\n }\n};\n/**\n * Downgrade the component\n *\n * @private\n */\nMaterialTooltip.prototype.mdlDowngrade_ = function () {\n if (this.forElement_) {\n this.forElement_.removeEventListener('mouseenter', this.boundMouseEnterHandler, false);\n this.forElement_.removeEventListener('click', this.boundMouseEnterHandler, false);\n this.forElement_.removeEventListener('touchstart', this.boundMouseEnterHandler, false);\n this.forElement_.removeEventListener('mouseleave', this.boundMouseLeaveHandler);\n }\n};\n/**\n * Public alias for the downgrade method.\n *\n * @public\n */\nMaterialTooltip.prototype.mdlDowngrade = MaterialTooltip.prototype.mdlDowngrade_;\nMaterialTooltip.prototype['mdlDowngrade'] = MaterialTooltip.prototype.mdlDowngrade;\n// The component registers itself. It can assume componentHandler is available\n// in the global scope.\ncomponentHandler.register({\n constructor: MaterialTooltip,\n classAsString: 'MaterialTooltip',\n cssClass: 'mdl-tooltip'\n});","/**\n * @license\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Class constructor for Data Table Card MDL component.\n * Implements MDL component design pattern defined at:\n * https://github.com/jasonmayes/mdl-component-design-pattern\n *\n * @constructor\n * @param {HTMLElement} element The element that will be upgraded.\n */\nvar MaterialDataTable = function MaterialDataTable(element) {\n this.element_ = element;\n // Initialize instance.\n this.init();\n};\nwindow['MaterialDataTable'] = MaterialDataTable;\n/**\n * Store constants in one place so they can be updated easily.\n *\n * @enum {string | number}\n * @private\n */\nMaterialDataTable.prototype.Constant_ = {};\n/**\n * Store strings for class names defined by this component that are used in\n * JavaScript. This allows us to simply change it in one place should we\n * decide to modify at a later date.\n *\n * @enum {string}\n * @private\n */\nMaterialDataTable.prototype.CssClasses_ = {\n DATA_TABLE: 'mdl-data-table',\n SELECTABLE: 'mdl-data-table--selectable',\n SELECT_ELEMENT: 'mdl-data-table__select',\n IS_SELECTED: 'is-selected',\n IS_UPGRADED: 'is-upgraded'\n};\n/**\n * Generates and returns a function that toggles the selection state of a\n * single row (or multiple rows).\n *\n * @param {Element} checkbox Checkbox that toggles the selection state.\n * @param {HTMLElement} row Row to toggle when checkbox changes.\n * @param {(Array|NodeList)=} opt_rows Rows to toggle when checkbox changes.\n * @private\n */\nMaterialDataTable.prototype.selectRow_ = function (checkbox, row, opt_rows) {\n if (row) {\n return function () {\n if (checkbox.checked) {\n row.classList.add(this.CssClasses_.IS_SELECTED);\n } else {\n row.classList.remove(this.CssClasses_.IS_SELECTED);\n }\n }.bind(this);\n }\n if (opt_rows) {\n return function () {\n var i;\n var el;\n if (checkbox.checked) {\n for (i = 0; i < opt_rows.length; i++) {\n el = opt_rows[i].querySelector('td').querySelector('.mdl-checkbox');\n el['MaterialCheckbox'].check();\n opt_rows[i].classList.add(this.CssClasses_.IS_SELECTED);\n }\n } else {\n for (i = 0; i < opt_rows.length; i++) {\n el = opt_rows[i].querySelector('td').querySelector('.mdl-checkbox');\n el['MaterialCheckbox'].uncheck();\n opt_rows[i].classList.remove(this.CssClasses_.IS_SELECTED);\n }\n }\n }.bind(this);\n }\n};\n/**\n * Creates a checkbox for a single or or multiple rows and hooks up the\n * event handling.\n *\n * @param {HTMLElement} row Row to toggle when checkbox changes.\n * @param {(Array|NodeList)=} opt_rows Rows to toggle when checkbox changes.\n * @private\n */\nMaterialDataTable.prototype.createCheckbox_ = function (row, opt_rows) {\n var label = document.createElement('label');\n var labelClasses = [\n 'mdl-checkbox',\n 'mdl-js-checkbox',\n 'mdl-js-ripple-effect',\n this.CssClasses_.SELECT_ELEMENT\n ];\n label.className = labelClasses.join(' ');\n var checkbox = document.createElement('input');\n checkbox.type = 'checkbox';\n checkbox.classList.add('mdl-checkbox__input');\n checkbox.addEventListener('change', this.selectRow_(checkbox, row, opt_rows));\n label.appendChild(checkbox);\n componentHandler.upgradeElement(label, 'MaterialCheckbox');\n return label;\n};\n/**\n * Initialize element.\n */\nMaterialDataTable.prototype.init = function () {\n if (this.element_) {\n var firstHeader = this.element_.querySelector('th');\n var rows = this.element_.querySelector('tbody').querySelectorAll('tr');\n if (this.element_.classList.contains(this.CssClasses_.SELECTABLE)) {\n var th = document.createElement('th');\n var headerCheckbox = this.createCheckbox_(null, rows);\n th.appendChild(headerCheckbox);\n firstHeader.parentElement.insertBefore(th, firstHeader);\n for (var i = 0; i < rows.length; i++) {\n var firstCell = rows[i].querySelector('td');\n if (firstCell) {\n var td = document.createElement('td');\n var rowCheckbox = this.createCheckbox_(rows[i]);\n td.appendChild(rowCheckbox);\n rows[i].insertBefore(td, firstCell);\n }\n }\n }\n this.element_.classList.add(this.CssClasses_.IS_UPGRADED);\n }\n};\n// The component registers itself. It can assume componentHandler is available\n// in the global scope.\ncomponentHandler.register({\n constructor: MaterialDataTable,\n classAsString: 'MaterialDataTable',\n cssClass: 'mdl-js-data-table'\n});","/**\n * @license\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Class constructor for Ripple MDL component.\n * Implements MDL component design pattern defined at:\n * https://github.com/jasonmayes/mdl-component-design-pattern\n *\n * @constructor\n * @param {HTMLElement} element The element that will be upgraded.\n */\nvar MaterialRipple = function MaterialRipple(element) {\n this.element_ = element;\n // Initialize instance.\n this.init();\n};\nwindow['MaterialRipple'] = MaterialRipple;\n/**\n * Store constants in one place so they can be updated easily.\n *\n * @enum {string | number}\n * @private\n */\nMaterialRipple.prototype.Constant_ = {\n INITIAL_SCALE: 'scale(0.0001, 0.0001)',\n INITIAL_SIZE: '1px',\n INITIAL_OPACITY: '0.4',\n FINAL_OPACITY: '0',\n FINAL_SCALE: ''\n};\n/**\n * Store strings for class names defined by this component that are used in\n * JavaScript. This allows us to simply change it in one place should we\n * decide to modify at a later date.\n *\n * @enum {string}\n * @private\n */\nMaterialRipple.prototype.CssClasses_ = {\n RIPPLE_CENTER: 'mdl-ripple--center',\n RIPPLE_EFFECT_IGNORE_EVENTS: 'mdl-js-ripple-effect--ignore-events',\n RIPPLE: 'mdl-ripple',\n IS_ANIMATING: 'is-animating',\n IS_VISIBLE: 'is-visible'\n};\n/**\n * Handle mouse / finger down on element.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialRipple.prototype.downHandler_ = function (event) {\n if (!this.rippleElement_.style.width && !this.rippleElement_.style.height) {\n var rect = this.element_.getBoundingClientRect();\n this.boundHeight = rect.height;\n this.boundWidth = rect.width;\n this.rippleSize_ = Math.sqrt(rect.width * rect.width + rect.height * rect.height) * 2 + 2;\n this.rippleElement_.style.width = this.rippleSize_ + 'px';\n this.rippleElement_.style.height = this.rippleSize_ + 'px';\n }\n this.rippleElement_.classList.add(this.CssClasses_.IS_VISIBLE);\n if (event.type === 'mousedown' && this.ignoringMouseDown_) {\n this.ignoringMouseDown_ = false;\n } else {\n if (event.type === 'touchstart') {\n this.ignoringMouseDown_ = true;\n }\n var frameCount = this.getFrameCount();\n if (frameCount > 0) {\n return;\n }\n this.setFrameCount(1);\n var bound = event.currentTarget.getBoundingClientRect();\n var x;\n var y;\n // Check if we are handling a keyboard click.\n if (event.clientX === 0 && event.clientY === 0) {\n x = Math.round(bound.width / 2);\n y = Math.round(bound.height / 2);\n } else {\n var clientX = event.clientX ? event.clientX : event.touches[0].clientX;\n var clientY = event.clientY ? event.clientY : event.touches[0].clientY;\n x = Math.round(clientX - bound.left);\n y = Math.round(clientY - bound.top);\n }\n this.setRippleXY(x, y);\n this.setRippleStyles(true);\n window.requestAnimationFrame(this.animFrameHandler.bind(this));\n }\n};\n/**\n * Handle mouse / finger up on element.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialRipple.prototype.upHandler_ = function (event) {\n // Don't fire for the artificial \"mouseup\" generated by a double-click.\n if (event && event.detail !== 2) {\n this.rippleElement_.classList.remove(this.CssClasses_.IS_VISIBLE);\n }\n // Allow a repaint to occur before removing this class, so the animation\n // shows for tap events, which seem to trigger a mouseup too soon after\n // mousedown.\n window.setTimeout(function () {\n this.rippleElement_.classList.remove(this.CssClasses_.IS_VISIBLE);\n }.bind(this), 0);\n};\n/**\n * Initialize element.\n */\nMaterialRipple.prototype.init = function () {\n if (this.element_) {\n var recentering = this.element_.classList.contains(this.CssClasses_.RIPPLE_CENTER);\n if (!this.element_.classList.contains(this.CssClasses_.RIPPLE_EFFECT_IGNORE_EVENTS)) {\n this.rippleElement_ = this.element_.querySelector('.' + this.CssClasses_.RIPPLE);\n this.frameCount_ = 0;\n this.rippleSize_ = 0;\n this.x_ = 0;\n this.y_ = 0;\n // Touch start produces a compat mouse down event, which would cause a\n // second ripples. To avoid that, we use this property to ignore the first\n // mouse down after a touch start.\n this.ignoringMouseDown_ = false;\n this.boundDownHandler = this.downHandler_.bind(this);\n this.element_.addEventListener('mousedown', this.boundDownHandler);\n this.element_.addEventListener('touchstart', this.boundDownHandler);\n this.boundUpHandler = this.upHandler_.bind(this);\n this.element_.addEventListener('mouseup', this.boundUpHandler);\n this.element_.addEventListener('mouseleave', this.boundUpHandler);\n this.element_.addEventListener('touchend', this.boundUpHandler);\n this.element_.addEventListener('blur', this.boundUpHandler);\n /**\n * Getter for frameCount_.\n * @return {number} the frame count.\n */\n this.getFrameCount = function () {\n return this.frameCount_;\n };\n /**\n * Setter for frameCount_.\n * @param {number} fC the frame count.\n */\n this.setFrameCount = function (fC) {\n this.frameCount_ = fC;\n };\n /**\n * Getter for rippleElement_.\n * @return {Element} the ripple element.\n */\n this.getRippleElement = function () {\n return this.rippleElement_;\n };\n /**\n * Sets the ripple X and Y coordinates.\n * @param {number} newX the new X coordinate\n * @param {number} newY the new Y coordinate\n */\n this.setRippleXY = function (newX, newY) {\n this.x_ = newX;\n this.y_ = newY;\n };\n /**\n * Sets the ripple styles.\n * @param {boolean} start whether or not this is the start frame.\n */\n this.setRippleStyles = function (start) {\n if (this.rippleElement_ !== null) {\n var transformString;\n var scale;\n var size;\n var offset = 'translate(' + this.x_ + 'px, ' + this.y_ + 'px)';\n if (start) {\n scale = this.Constant_.INITIAL_SCALE;\n size = this.Constant_.INITIAL_SIZE;\n } else {\n scale = this.Constant_.FINAL_SCALE;\n size = this.rippleSize_ + 'px';\n if (recentering) {\n offset = 'translate(' + this.boundWidth / 2 + 'px, ' + this.boundHeight / 2 + 'px)';\n }\n }\n transformString = 'translate(-50%, -50%) ' + offset + scale;\n this.rippleElement_.style.webkitTransform = transformString;\n this.rippleElement_.style.msTransform = transformString;\n this.rippleElement_.style.transform = transformString;\n if (start) {\n this.rippleElement_.classList.remove(this.CssClasses_.IS_ANIMATING);\n } else {\n this.rippleElement_.classList.add(this.CssClasses_.IS_ANIMATING);\n }\n }\n };\n /**\n * Handles an animation frame.\n */\n this.animFrameHandler = function () {\n if (this.frameCount_-- > 0) {\n window.requestAnimationFrame(this.animFrameHandler.bind(this));\n } else {\n this.setRippleStyles(false);\n }\n };\n }\n }\n};\n/**\n * Downgrade the component\n *\n * @private\n */\nMaterialRipple.prototype.mdlDowngrade_ = function () {\n this.element_.removeEventListener('mousedown', this.boundDownHandler);\n this.element_.removeEventListener('touchstart', this.boundDownHandler);\n this.element_.removeEventListener('mouseup', this.boundUpHandler);\n this.element_.removeEventListener('mouseleave', this.boundUpHandler);\n this.element_.removeEventListener('touchend', this.boundUpHandler);\n this.element_.removeEventListener('blur', this.boundUpHandler);\n};\n/**\n * Public alias for the downgrade method.\n *\n * @public\n */\nMaterialRipple.prototype.mdlDowngrade = MaterialRipple.prototype.mdlDowngrade_;\nMaterialRipple.prototype['mdlDowngrade'] = MaterialRipple.prototype.mdlDowngrade;\n// The component registers itself. It can assume componentHandler is available\n// in the global scope.\ncomponentHandler.register({\n constructor: MaterialRipple,\n classAsString: 'MaterialRipple',\n cssClass: 'mdl-js-ripple-effect',\n widget: false\n});"],"sourceRoot":"/source/"} \ No newline at end of file diff --git a/examples/scalajs-play-core-react/server/src/main/resources/application.conf b/examples/scalajs-play-core-react/server/src/main/resources/application.conf new file mode 100644 index 0000000..cd1bb1b --- /dev/null +++ b/examples/scalajs-play-core-react/server/src/main/resources/application.conf @@ -0,0 +1,4 @@ +play.application.loader = "GlobalApplicationLoader" +//play.i18n.langs = [ "en" ] +play.http.parser.maxMemoryBuffer = 512k +play.http.parser.maxDiskBuffer = 1g diff --git a/examples/scalajs-play-core-react/server/src/main/scala/GlobalApplicationLoader.scala b/examples/scalajs-play-core-react/server/src/main/scala/GlobalApplicationLoader.scala new file mode 100644 index 0000000..6545380 --- /dev/null +++ b/examples/scalajs-play-core-react/server/src/main/scala/GlobalApplicationLoader.scala @@ -0,0 +1,35 @@ +import com.softwaremill.macwire._ +import controllers.Assets +import modules.{Controllers, Service} +import play.api.ApplicationLoader.Context +import play.api.routing.Router +import play.api.routing.sird._ +import play.api.{Application, ApplicationLoader, BuiltInComponents, BuiltInComponentsFromContext} + +/** + * Global application context + */ +class GlobalApplicationLoader extends ApplicationLoader { + override def load(context: Context): Application = (new BuiltInComponentFromContextWithPlayWorkaround(context) with ApplicationComponents).application +} + +abstract class BuiltInComponentFromContextWithPlayWorkaround(context: Context) extends BuiltInComponentsFromContext(context) { + + import play.api.inject.{Injector, NewInstanceInjector, SimpleInjector} + import play.api.libs.Files.DefaultTemporaryFileCreator + + lazy val defaultTemporaryFileCreator = new DefaultTemporaryFileCreator(applicationLifecycle) + + override lazy val injector: Injector = new SimpleInjector(NewInstanceInjector) + router + crypto + httpConfiguration + defaultTemporaryFileCreator +} + +trait ApplicationComponents extends BuiltInComponents with Controllers with Service { + lazy val assets: Assets = wire[Assets] + lazy val router: Router = Router.from { + case GET(p"/") => applicationController.index + case GET(p"/assets/$file*") => Assets.versioned(path = "/public", file = file) + case POST(p"/api/sample/$path*") => apiController.sampleApi(path) + } +} + + diff --git a/examples/scalajs-play-core-react/server/src/main/scala/controllers/ApiController.scala b/examples/scalajs-play-core-react/server/src/main/scala/controllers/ApiController.scala new file mode 100644 index 0000000..e05d7a2 --- /dev/null +++ b/examples/scalajs-play-core-react/server/src/main/scala/controllers/ApiController.scala @@ -0,0 +1,20 @@ +package controllers + +import demo.SampleApi +import play.api.mvc.Action +import service.SampleApiImpl +import boopickle.Default._ +import scala.concurrent.ExecutionContext.Implicits.global + +/** + * API Controller + * Each request will create a SampleApiImpl instance. + * It's necessary if you want to set the user in the constructor, otherwise you can use singleton + */ +class ApiController extends ServiceController { + def sampleApi(path: String) = Action.async(parse.raw) { implicit request => + internalRoute(path, request) { + Router.route[SampleApi](new SampleApiImpl()) + } + } +} diff --git a/examples/scalajs-play-core-react/server/src/main/scala/controllers/DemoController.scala b/examples/scalajs-play-core-react/server/src/main/scala/controllers/DemoController.scala new file mode 100644 index 0000000..4700eaf --- /dev/null +++ b/examples/scalajs-play-core-react/server/src/main/scala/controllers/DemoController.scala @@ -0,0 +1,11 @@ +package controllers + +import play.api.mvc._ + +import scala.concurrent.ExecutionContext.Implicits.global +import scala.concurrent.Future + +class DemoController extends Controller { + + def index = Action.async(Future(Ok(views.html.index("SPA tutorial")))) +} diff --git a/examples/scalajs-play-core-react/server/src/main/scala/controllers/ServiceController.scala b/examples/scalajs-play-core-react/server/src/main/scala/controllers/ServiceController.scala new file mode 100644 index 0000000..b05c1ac --- /dev/null +++ b/examples/scalajs-play-core-react/server/src/main/scala/controllers/ServiceController.scala @@ -0,0 +1,38 @@ +package controllers + +import java.nio.ByteBuffer + +import boopickle.Default._ +import boopickle.Pickler +import play.api.mvc.{Controller, RawBuffer, Request} + +import scala.concurrent.ExecutionContext.Implicits.global +/** + * Autowire router + */ +object Router extends autowire.Server[ByteBuffer, Pickler, Pickler] { + + override def read[R: Pickler](p: ByteBuffer) = Unpickle[R].fromBytes(p) + + override def write[R: Pickler](r: R) = Pickle.intoBytes(r) +} + +trait ServiceController extends Controller { + /** + * Helper for internal routing + * @param path + * @param request + * @param router + * @return + */ + protected def internalRoute(path: String, request: Request[RawBuffer])(router: => Router.Router) = { + val b = request.body.asBytes(parse.UNLIMITED).get + router( + autowire.Core.Request(path.split("/"), Unpickle[Map[String, ByteBuffer]].fromBytes(ByteBuffer.wrap(b))) + ).map(buffer => { + val data = Array.ofDim[Byte](buffer.remaining()) + buffer.get(data) + Ok(data) + }) + } +} diff --git a/examples/scalajs-play-core-react/server/src/main/scala/modules/Controllers.scala b/examples/scalajs-play-core-react/server/src/main/scala/modules/Controllers.scala new file mode 100644 index 0000000..59bffdb --- /dev/null +++ b/examples/scalajs-play-core-react/server/src/main/scala/modules/Controllers.scala @@ -0,0 +1,13 @@ +package modules + +import com.softwaremill.macwire._ +import controllers.{ApiController, DemoController} +import play.api.BuiltInComponents + +/** + * Created by Janos on 12/9/2015. + */ +trait Controllers extends BuiltInComponents { + lazy val applicationController = wire[DemoController] + lazy val apiController = wire[ApiController] +} diff --git a/examples/scalajs-play-core-react/server/src/main/scala/modules/Service.scala b/examples/scalajs-play-core-react/server/src/main/scala/modules/Service.scala new file mode 100644 index 0000000..d938327 --- /dev/null +++ b/examples/scalajs-play-core-react/server/src/main/scala/modules/Service.scala @@ -0,0 +1,8 @@ +package modules + +/** + * Created by Janos on 12/9/2015. + */ +trait Service { + +} diff --git a/examples/scalajs-play-core-react/server/src/main/scala/service/SampleApiImpl.scala b/examples/scalajs-play-core-react/server/src/main/scala/service/SampleApiImpl.scala new file mode 100644 index 0000000..00757f1 --- /dev/null +++ b/examples/scalajs-play-core-react/server/src/main/scala/service/SampleApiImpl.scala @@ -0,0 +1,10 @@ +package service + +import demo.SampleApi + +/** + * Created by Janos on 12/9/2015. + */ +class SampleApiImpl extends SampleApi { + override def echo(name: String): String = s"Echoed: ${name}" +} diff --git a/examples/scalajs-play-core-react/server/src/main/twirl/views/index.scala.html b/examples/scalajs-play-core-react/server/src/main/twirl/views/index.scala.html new file mode 100644 index 0000000..292a212 --- /dev/null +++ b/examples/scalajs-play-core-react/server/src/main/twirl/views/index.scala.html @@ -0,0 +1,20 @@ +@(title: String) + + + + + + @title + + + + + + + +
      +
      + @playscalajs.html.scripts("client") + + + \ No newline at end of file diff --git a/examples/scalajs-play-core-react/shared/.js/.gitignore b/examples/scalajs-play-core-react/shared/.js/.gitignore new file mode 100644 index 0000000..801cc40 --- /dev/null +++ b/examples/scalajs-play-core-react/shared/.js/.gitignore @@ -0,0 +1,7 @@ +/scala/ +/scala/ +/scala/ +/scala/ +/scala/ +/scala/ +/scala/ diff --git a/examples/scalajs-play-core-react/shared/src/main/scala/demo/SampleApi.scala b/examples/scalajs-play-core-react/shared/src/main/scala/demo/SampleApi.scala new file mode 100644 index 0000000..171a0d0 --- /dev/null +++ b/examples/scalajs-play-core-react/shared/src/main/scala/demo/SampleApi.scala @@ -0,0 +1,5 @@ +package demo + +trait SampleApi { + def echo(name: String): String +} \ No newline at end of file diff --git a/examples/scalajs-play-core-react/shared/src/main/scala/demo/SampleApi.sjsir b/examples/scalajs-play-core-react/shared/src/main/scala/demo/SampleApi.sjsir new file mode 100644 index 0000000000000000000000000000000000000000..13dcc1e95415cd7e1e8c88673d495163c9f08da9 GIT binary patch literal 323 zcmX^0&nuXL)j-co&y<1RCnYsEKR!4yw;(6gu^^LynE?hE8MsoDGxFo(Lx6~Z5hjd_ zW@OMbu&^|p&Gqg-Ju(UAO2br%AH(#$fIWZ>@Xa*am)qEhOdT^ycMaT|d z$xF;lWnc_phdO`>WQZ-4!@vl4H%Oe3mCc@Unwe;(?GLT(hc~^juY7fluV$*H)UB8t yMH^=K1ihk$AmxQ&{!Eu{x&qZ3K)eSs1B4m5z^1VPnJnBuf|Y>*L^1q_2Lb?2N -// b.unbind() onComplete { -// case _ => // ... -// } -// } -// } -} \ No newline at end of file diff --git a/it/src/test/scala/org/zalando/nakadi/client/example2/HttpClientExample.scala b/it/src/test/scala/org/zalando/nakadi/client/example2/HttpClientExample.scala index 3790764..83bd71f 100644 --- a/it/src/test/scala/org/zalando/nakadi/client/example2/HttpClientExample.scala +++ b/it/src/test/scala/org/zalando/nakadi/client/example2/HttpClientExample.scala @@ -45,7 +45,7 @@ object HttpClient extends App with ClientFactory with HttpFactory with JacksonJs private implicit val http = Http(actorSystem) implicit val materializer = ActorMaterializer() val eventName = "/event-types/test-client-integration-event-1936085527-148383828851369665/events" - val cursor = Cursor(0,30115) + val cursor = Cursor(0,Some(30115)) val params = Some(StreamParameters()) val headers = RawHeader("Accept", "application/x-json-stream") :: withHeaders(params) val request = withHttpRequest(eventName, HttpMethods.GET, headers, OAuth2Token) diff --git a/it/src/test/scala/org/zalando/nakadi/client/example2/Subscriber.scala b/it/src/test/scala/org/zalando/nakadi/client/example2/Subscriber.scala new file mode 100644 index 0000000..59f9e75 --- /dev/null +++ b/it/src/test/scala/org/zalando/nakadi/client/example2/Subscriber.scala @@ -0,0 +1,59 @@ +package org.zalando.nakadi.client.example2 + +import com.fasterxml +import org.zalando +import org.zalando +import org.zalando +import scala.concurrent.Future +import org.zalando.nakadi.client.model.JacksonJsonMarshaller +import org.zalando.nakadi.client.ClientFactory +import org.zalando.nakadi.client.StreamParameters +import org.zalando.nakadi.client.Listener +import org.zalando.nakadi.client.ClientError +import com.fasterxml.jackson.core.`type`.TypeReference +import org.zalando.nakadi.client.model.Cursor +import scala.concurrent.Await +import scala.concurrent.duration.DurationInt + +case class MyEventExample(orderNumber: String) +object Subscriber extends App { + val a = new A() + // a.startListening() + // a.sendEvents() + a.printPartitions() +} + +class A extends ClientFactory with JacksonJsonMarshaller { + val eventType = "test-client-integration-event-1936085527-148383828851369665" + implicit def myEventExampleTR: TypeReference[MyEventExample] = new TypeReference[MyEventExample] {} + def startListening() = { + + val listener = new Listener[MyEventExample] { + def id: String = "test" + def onError(sourceUrl: String, cursor: Cursor, error: ClientError): Unit = { + println("YOOOOOOOOOOOOOO ") + } + def onSubscribed(): Unit = ??? + def onUnsubscribed(): Unit = ??? + def onReceive(sourceUrl: String, cursor: Cursor, event: MyEventExample): Unit = ??? + } + val url = "/event-types/test-client-integration-event-1936085527-148383828851369665/events" + val c = Cursor(0, Some(1)) + val params = new StreamParameters(cursor = Some(c)) + client.subscribe(eventType, params, listener) + } + + def printPartitions() = { + val result = Await.result(client.partitions(eventType), 5.seconds) + println("###########################") + println("partitions" + result) + println("###########################") + + } + def sendEvents() = { + val events = for { + a <- 0 to 1 + } yield MyEventExample("order-" + a) + client.newEvents(eventType, events) + } +} \ No newline at end of file diff --git a/model/src/main/scala/org/zalando/nakadi/client/model/Model.scala b/model/src/main/scala/org/zalando/nakadi/client/model/Model.scala index e9256bb..7bde412 100644 --- a/model/src/main/scala/org/zalando/nakadi/client/model/Model.scala +++ b/model/src/main/scala/org/zalando/nakadi/client/model/Model.scala @@ -99,16 +99,16 @@ case class Metrics(metrics: Map[String, Any]) * @param newestAvailableOffset An offset of the newest available Event in that partition. This value will be changing upon reception of new events for this partition by Nakadi. This value can be used to construct a cursor when opening streams (see `GET /event-type/{name}/events` for details). Might assume the special name BEGIN, meaning a pointer to the offset of the oldest available event in the partition. */ case class Partition( - partition: String, - oldestAvailableOffset: String, - newestAvailableOffset: String) + partition: Integer, + oldestAvailableOffset: Integer, + newestAvailableOffset: Integer) /** * @param partition Id of the partition pointed to by this cursor. * @param offset Offset of the event being pointed to. */ case class Cursor( partition: Integer, - offset: Integer) + offset: Option[Integer]) /** * One chunk of events in a stream. A batch consists of an array of `Event`s plus a `Cursor` pointing to the offset of the last Event in the stream. The size of the array of Event is limited by the parameters used to initialize a Stream. If acting as a keep alive message (see `GET /event-type/{name}/events`) the events array will be omitted. Sequential batches might repeat the cursor if no new events arrive. From ef5aa3747c3b90c205e7059eb6e2cd3d286e58f2 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Tue, 26 Apr 2016 17:32:41 +0200 Subject: [PATCH 023/183] techjira:LAAS-60 Implementing Java client now! --- .../zalando/nakadi/client/java/Client.java | 145 ++++++++++++++++++ .../org/zalando/nakadi/client/Listener.scala | 23 +++ .../zalando/nakadi/client/Serialization.scala | 17 ++ .../zalando/nakadi/client/scala}/Client.scala | 65 ++++---- .../nakadi/client/java/ClientImpl.java | 144 +++++++++++++++++ .../zalando/nakadi/client/Serialization.scala | 17 -- .../client/actor/EventConsumingActor.scala | 4 +- .../client/model/JacksonJsonMarshaller.scala | 38 ++--- .../client/model/SprayJsonMarshaller.scala | 15 +- .../nakadi/client/scala/ClientBuilder.scala | 71 +++++++++ .../client/{ => scala}/ClientImpl.scala | 48 +++--- .../client/{ => scala}/Connection.scala | 25 +-- .../client/{ => scala}/HttpFactory.scala | 6 +- .../nakadi/client/{ => scala}/package.scala | 6 +- .../nakadi/client/utils/Conversions.scala | 36 +++++ .../client/{ => scala}/ClientTest.scala | 5 +- .../DeserializerSerializerTest.scala | 5 +- .../client/{ => scala}/EnumModule.scala | 2 +- .../SerializerDeserializerTest.scala | 14 +- .../client/{ => scala}/TestFactory.scala | 35 +++-- .../zalando/nakadi/client/model/Model.scala | 5 +- 21 files changed, 569 insertions(+), 157 deletions(-) create mode 100644 api/src/main/java/org/zalando/nakadi/client/java/Client.java create mode 100644 api/src/main/scala/org/zalando/nakadi/client/Listener.scala create mode 100644 api/src/main/scala/org/zalando/nakadi/client/Serialization.scala rename {client/src/main/scala/org/zalando/nakadi/client => api/src/main/scala/org/zalando/nakadi/client/scala}/Client.scala (56%) create mode 100644 client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java delete mode 100644 client/src/main/scala/org/zalando/nakadi/client/Serialization.scala create mode 100644 client/src/main/scala/org/zalando/nakadi/client/scala/ClientBuilder.scala rename client/src/main/scala/org/zalando/nakadi/client/{ => scala}/ClientImpl.scala (75%) rename client/src/main/scala/org/zalando/nakadi/client/{ => scala}/Connection.scala (85%) rename client/src/main/scala/org/zalando/nakadi/client/{ => scala}/HttpFactory.scala (97%) rename client/src/main/scala/org/zalando/nakadi/client/{ => scala}/package.scala (91%) create mode 100644 client/src/main/scala/org/zalando/nakadi/client/utils/Conversions.scala rename client/src/test/scala/org/zalando/nakadi/client/{ => scala}/ClientTest.scala (92%) rename client/src/test/scala/org/zalando/nakadi/client/{ => scala}/DeserializerSerializerTest.scala (93%) rename client/src/test/scala/org/zalando/nakadi/client/{ => scala}/EnumModule.scala (98%) rename client/src/test/scala/org/zalando/nakadi/client/{ => scala}/SerializerDeserializerTest.scala (90%) rename client/src/test/scala/org/zalando/nakadi/client/{ => scala}/TestFactory.scala (66%) diff --git a/api/src/main/java/org/zalando/nakadi/client/java/Client.java b/api/src/main/java/org/zalando/nakadi/client/java/Client.java new file mode 100644 index 0000000..f045365 --- /dev/null +++ b/api/src/main/java/org/zalando/nakadi/client/java/Client.java @@ -0,0 +1,145 @@ +package org.zalando.nakadi.client.java; + +import java.util.List; +import java.util.Optional; +import java.util.concurrent.Future; + +import org.zalando.nakadi.client.Serializer; +import org.zalando.nakadi.client.model.Event; +import org.zalando.nakadi.client.model.EventEnrichmentStrategy; +import org.zalando.nakadi.client.model.EventType; +import org.zalando.nakadi.client.model.EventValidationStrategy; +import org.zalando.nakadi.client.model.Metrics; +import org.zalando.nakadi.client.model.Partition; +import org.zalando.nakadi.client.model.PartitionStrategy; + +import com.fasterxml.jackson.core.type.TypeReference; + +public interface Client { + + /** + * Retrieves monitoring metrics. NOTE: metrics format is v + * + * @return metrics data + */ + Future> getMetrics(); + + /** + * Retrieves all registered EventTypes. + * + * @return List of known EventTypes + */ + Future>> getEventTypes(); + + /** + * Creates an eventType(topic). + * + * @param eventType The EventType to create + * @return Void in case of success + */ + Future createEventType(EventType eventType); + + /** + * Retrieves the EventType. + * + * @param eventTypeName The unique name (id) of the EventType to retrieve + * @return The EventType if it can be found + */ + Future> getEventType(String eventTypeName); + + /** + * Updates the eventType. + * @param eventTypeName The unique name (id) of the EventType to update + * @param eventType The eventType to be updated. + * @return Void in case of success + */ + Future updateEventType(String eventTypeName, EventType eventType); + + /** + * Deletes an eventType. + * @param eventTypeName The unique name (id) of the EventType to update + * @return Void in case of success + */ + Future deleteEventType(String eventTypeName); + + /** + * Publishes a single event to the given eventType using a custom serializer.
      + * Partition selection is done using the defined partition resolution,
      + * which is defined per topic and managed by the event store. + * @param eventTypeName The unique name (id) of the EventType target + * @param event The event to be published + * @param serializer The custom serializer to serialize the event. + * @return Void in case of success + */ + Future publishEvent(String eventTypeName, T event, Serializer serializer); + + /** + * Publishes a single event to the given eventType.
      + * Partition selection is done using the defined partition resolution,
      + * which is defined per topic and managed by the event store. + * @param eventTypeName The unique name (id) of the EventType target + * @param event The event to be published + * @param ref The Jackson TypeReference of the Event to be used by the default Jackson Marshaller. + * @return Void in case of success + */ + Future publishEvent(String eventTypeName, T event, TypeReference ref); + + /** + * Publishes a List of events to the given eventType using a custom serializer.
      + * Partition selection is done using the defined partition resolution,
      + * which is defined per topic and managed by the event store. + * @param eventTypeName The unique name (id) of the EventType target + * @param events The list of events to be published + * @param serializer The custom serializer to serialize the events. + * @return Void in case of success + */ + Future publishEvents(String eventTypeName, List events, Serializer> serializer); + /** + * Publishes a List of events to the given eventType.
      + * Partition selection is done using the defined partition resolution,
      + * which is defined per topic and managed by the event store. + * @param eventTypeName The unique name (id) of the EventType target + * @param event The event to be published + * @param ref The Jackson TypeReference of the Event to be used by the default Jackson Marshaller. + * @return Void in case of success + */ + Future publishEvents(String eventTypeName, List events, TypeReference> ref); + + /** + * Retrieves the existing partitions for the given EventType. + * @param eventTypeName The unique name (id) of the EventType + * @return list of existing partitions + */ + Future>> getPartitions(String eventTypeName); + + /** + * Retrieves the unique partition, identified by the given parameters. + * @param eventTypeName The unique name (id) of the EventType + * @param id The id of the partition + * @return the partition if exists + */ + Future> getPartitionById(String eventTypeName, String id); + + /** + * Retrieves a List of all Validation strategies supported by the Event store. + * @return list of validation strategies + */ + Future>> getValidationStrategies(); + + /** + * Retrieves a List of all Enrichment strategies supported by the Event store. + * @return list of enrichment strategies + */ + Future>> getEnrichmentStrategies(); + /** + * Retrieves a List of all Partition strategies supported by the Event store. + * @return list of enrichment strategies + */ + Future>> getPartitionStrategies(); + + /** + * Shuts down the communication system of the client + * @return Void in case of success + */ + Future stop(); +} \ No newline at end of file diff --git a/api/src/main/scala/org/zalando/nakadi/client/Listener.scala b/api/src/main/scala/org/zalando/nakadi/client/Listener.scala new file mode 100644 index 0000000..2f2d3fb --- /dev/null +++ b/api/src/main/scala/org/zalando/nakadi/client/Listener.scala @@ -0,0 +1,23 @@ +package org.zalando.nakadi.client + +import org.zalando.nakadi.client.model.Cursor + +case class ClientError(msg: String, status: Option[Int]) + + +case class StreamParameters(cursor: Option[Cursor] = None, // + batchLimit: Option[Integer] = None, + streamLimit: Option[Integer] = None, + batchFlushTimeout: Option[Integer] = None, + streamTimeout: Option[Integer] = None, + streamKeepAliveLimit: Option[Integer] = None, + flowId: Option[String] = None) { +} + +trait Listener[T] { + def id: String + def onSubscribed(): Unit + def onUnsubscribed(): Unit + def onReceive(sourceUrl: String, cursor: Cursor, event: T): Unit + def onError(sourceUrl: String, cursor: Cursor, error: ClientError): Unit +} \ No newline at end of file diff --git a/api/src/main/scala/org/zalando/nakadi/client/Serialization.scala b/api/src/main/scala/org/zalando/nakadi/client/Serialization.scala new file mode 100644 index 0000000..111ae8b --- /dev/null +++ b/api/src/main/scala/org/zalando/nakadi/client/Serialization.scala @@ -0,0 +1,17 @@ +package org.zalando.nakadi.client + + +/** + * + */ +trait Deserializer[T] { + def from(from: String): T +} + +/** + * + */ +trait Serializer[T] { + def to(from: T): String +} + diff --git a/client/src/main/scala/org/zalando/nakadi/client/Client.scala b/api/src/main/scala/org/zalando/nakadi/client/scala/Client.scala similarity index 56% rename from client/src/main/scala/org/zalando/nakadi/client/Client.scala rename to api/src/main/scala/org/zalando/nakadi/client/scala/Client.scala index c1c9047..900ccba 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/Client.scala +++ b/api/src/main/scala/org/zalando/nakadi/client/scala/Client.scala @@ -1,28 +1,21 @@ -package org.zalando.nakadi.client +package org.zalando.nakadi.client.scala import scala.concurrent.Future -import org.zalando.nakadi.client.model._ -import akka.actor.Terminated -import akka.http.scaladsl.model.HttpResponse - -case class ClientError(msg: String, status: Option[Int]) - -case class StreamParameters(cursor: Option[Cursor] = None, // - batchLimit: Option[Integer] = None, - streamLimit: Option[Integer] = None, - batchFlushTimeout: Option[Integer] = None, - streamTimeout: Option[Integer] = None, - streamKeepAliveLimit: Option[Integer] = None, - flowId: Option[String] = None) { -} -trait Listener[T] { - def id: String - def onSubscribed(): Unit - def onUnsubscribed(): Unit - def onReceive(sourceUrl: String, cursor: Cursor, event: T): Unit - def onError(sourceUrl: String, cursor: Cursor, error: ClientError): Unit -} +import org.zalando.nakadi.client.ClientError +import org.zalando.nakadi.client.Listener +import org.zalando.nakadi.client.Deserializer +import org.zalando.nakadi.client.Serializer +import org.zalando.nakadi.client.StreamParameters +import org.zalando.nakadi.client.model.EventEnrichmentStrategy +import org.zalando.nakadi.client.model.EventType +import org.zalando.nakadi.client.model.EventValidationStrategy +import org.zalando.nakadi.client.model.Metrics +import org.zalando.nakadi.client.model.Partition +import org.zalando.nakadi.client.model.PartitionStrategy + + + trait Client { @@ -33,7 +26,7 @@ trait Client { * curl --request GET /metrics * }}} */ - def metrics()(implicit ser: NakadiDeserializer[Metrics]): Future[Either[ClientError, Option[Metrics]]] + def getMetrics()(implicit ser: Deserializer[Metrics]): Future[Either[ClientError, Option[Metrics]]] /** * Returns a list of all registered EventTypes. @@ -43,7 +36,7 @@ trait Client { * }}} * */ - def eventTypes()(implicit ser: NakadiDeserializer[Seq[EventType]]): Future[Either[ClientError, Option[Seq[EventType]]]] + def getEventTypes()(implicit ser: Deserializer[Seq[EventType]]): Future[Either[ClientError, Option[Seq[EventType]]]] /** * Creates a new EventType. @@ -55,7 +48,7 @@ trait Client { * @param event - The EventType to create. * */ - def newEventType(eventType: EventType)(implicit ser: NakadiSerializer[EventType]): Future[Option[ClientError]] + def createEventType(eventType: EventType)(implicit ser: Serializer[EventType]): Future[Option[ClientError]] /** * Returns the EventType identified by its name. @@ -64,7 +57,7 @@ trait Client { * }}} * @param name - Name of the EventType */ - def eventType(name: String)(implicit ser: NakadiDeserializer[EventType]): Future[Either[ClientError, Option[EventType]]] + def getEventType(name: String)(implicit ser: Deserializer[EventType]): Future[Either[ClientError, Option[EventType]]] /** * Updates the EventType identified by its name. * {{{ @@ -73,7 +66,7 @@ trait Client { * @param name - Name of the EventType * @param event - Event to update */ - def updateEventType(name: String, eventType: EventType)(implicit ser: NakadiSerializer[EventType]): Future[Option[ClientError]] + def updateEventType(name: String, eventType: EventType)(implicit ser: Serializer[EventType]): Future[Option[ClientError]] /** * Deletes an EventType identified by its name. * @@ -93,7 +86,7 @@ trait Client { * @param name - Name of the EventType * @param event - Event to create */ - def newEvents[T](name: String, events: Seq[T])(implicit ser: NakadiSerializer[Seq[T]]): Future[Option[ClientError]] + def publishEvents[T](name: String, events: Seq[T])(implicit ser: Serializer[Seq[T]]): Future[Option[ClientError]] /** * Request a stream delivery for the specified partitions of the given EventType. @@ -103,7 +96,7 @@ trait Client { * @param name - Name of the EventType * */ - def events[T](name: String, params: Option[StreamParameters])(implicit ser: NakadiDeserializer[T]): Future[Either[ClientError, Option[T]]] + def publishEvents[T](name: String, params: Option[StreamParameters])(implicit ser: Deserializer[T]): Future[Either[ClientError, Option[T]]] /** * List the partitions for the given EventType. @@ -112,7 +105,7 @@ trait Client { * }}} * @param name - Name of the EventType */ - def partitions(name: String)(implicit ser: NakadiDeserializer[Seq[Partition]]): Future[Either[ClientError, Option[Seq[Partition]]]] + def getPartitions(name: String)(implicit ser: Deserializer[Seq[Partition]]): Future[Either[ClientError, Option[Seq[Partition]]]] /** * Returns the Partition for the given EventType. @@ -123,7 +116,7 @@ trait Client { * @param partition - Partition id for the given EventType */ - def partitionById(name: String, id: String)(implicit ser: NakadiDeserializer[Partition]): Future[Either[ClientError, Option[Partition]]] + def getPartitionById(name: String, id: String)(implicit ser: Deserializer[Partition]): Future[Either[ClientError, Option[Partition]]] /** * Returns all of the validation strategies supported by this installation of Nakadi. @@ -132,7 +125,7 @@ trait Client { * curl --request GET /registry/validation-strategies * }}} */ - def validationStrategies()(implicit des: NakadiDeserializer[Seq[EventValidationStrategy.Value]]): Future[Either[ClientError, Option[Seq[EventValidationStrategy.Value]]]] + def getValidationStrategies()(implicit des: Deserializer[Seq[EventValidationStrategy.Value]]): Future[Either[ClientError, Option[Seq[EventValidationStrategy.Value]]]] /** * Returns all of the enrichment strategies supported by this installation of Nakadi. @@ -141,7 +134,7 @@ trait Client { * }}} */ - def enrichmentStrategies()(implicit des: NakadiDeserializer[Seq[EventEnrichmentStrategy.Value]]): Future[Either[ClientError, Option[Seq[EventEnrichmentStrategy.Value]]]] + def getEnrichmentStrategies()(implicit des: Deserializer[Seq[EventEnrichmentStrategy.Value]]): Future[Either[ClientError, Option[Seq[EventEnrichmentStrategy.Value]]]] /** * Returns all of the partitioning strategies supported by this installation of Nakadi. @@ -149,13 +142,13 @@ trait Client { * curl --request GET /registry/partitioning-strategies * }}} */ - def partitionStrategies()(implicit des: NakadiDeserializer[Seq[PartitionStrategy.Value]]): Future[Either[ClientError, Option[Seq[PartitionStrategy.Value]]]] + def getPartitionStrategies()(implicit des: Deserializer[Seq[PartitionStrategy.Value]]): Future[Either[ClientError, Option[Seq[PartitionStrategy.Value]]]] /** * Shuts down the communication system of the client */ - def stop(): Future[Terminated] + def stop(): Future[Option[ClientError]] /** * Registers the subscription of a listener to start streaming events from a partition in non-blocking fashion. @@ -164,7 +157,7 @@ trait Client { * @parameters - Parameters for the streaming of events. * @listener - Listener to pass the event to when it is received. */ - def subscribe[T](eventType: String, parameters: StreamParameters, listener: Listener[T])(implicit ser: NakadiDeserializer[T]): Future[Option[ClientError]] + def subscribe[T](eventType: String, parameters: StreamParameters, listener: Listener[T])(implicit ser: Deserializer[T]): Future[Option[ClientError]] /** * Removes the subscription of a listener, to stop streaming events from a partition. * diff --git a/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java b/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java new file mode 100644 index 0000000..6e202f2 --- /dev/null +++ b/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java @@ -0,0 +1,144 @@ +package org.zalando.nakadi.client.java; + +import java.util.List; +import java.util.Optional; +import java.util.concurrent.Future; +import java.util.function.Supplier; + +import org.zalando.nakadi.client.scala.ClientBuilder; +import org.zalando.nakadi.client.Serializer; +import org.zalando.nakadi.client.model.*; + +import com.fasterxml.jackson.core.type.TypeReference; + +public class ClientImpl implements Client { + private final org.zalando.nakadi.client.scala.Client client; + + public ClientImpl(org.zalando.nakadi.client.scala.Client client) { + this.client = client; + } + + public ClientImpl(String host, // + Optional port, // + Optional> provider, // + Optional securedConnection, // + Optional verifySSlCertificate) { + + ClientBuilder builder = new ClientBuilder().withHost(host); + if (port.isPresent()) + builder = builder.withPort(port.get()); + if (provider.isPresent()) + builder = builder.withTokenProvider4Java(provider.get()); + if (securedConnection.isPresent()) + builder = builder.withSecuredConnection(securedConnection.get()); + if (verifySSlCertificate.isPresent()) + builder = builder.withVerifiedSslCertificate(verifySSlCertificate + .get()); + + client = builder.build(); + } + + @Override + public Future> getMetrics() { + + // Object a= client.metrics(); + return null; + } + + @Override + public Future>> getEventTypes() { + // TODO Auto-generated method stub + return null; + } + + @Override + public Future createEventType(EventType eventType) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Future> getEventType(String eventTypeName) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Future updateEventType(String eventTypeName, + EventType eventType) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Future deleteEventType(String eventTypeName) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Future publishEvent(String eventTypeName, + T event, Serializer serializer) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Future publishEvent(String eventTypeName, + T event, TypeReference ref) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Future publishEvents(String eventTypeName, + List events, Serializer> serializer) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Future publishEvents(String eventTypeName, + List events, TypeReference> ref) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Future>> getPartitions(String eventTypeName) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Future> getPartitionById(String eventTypeName, + String id) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Future>> getValidationStrategies() { + // TODO Auto-generated method stub + return null; + } + + @Override + public Future>> getEnrichmentStrategies() { + // TODO Auto-generated method stub + return null; + } + + @Override + public Future>> getPartitionStrategies() { + // TODO Auto-generated method stub + return null; + } + + @Override + public Future stop() { + // TODO Auto-generated method stub + return null; + } + +} \ No newline at end of file diff --git a/client/src/main/scala/org/zalando/nakadi/client/Serialization.scala b/client/src/main/scala/org/zalando/nakadi/client/Serialization.scala deleted file mode 100644 index f4da31b..0000000 --- a/client/src/main/scala/org/zalando/nakadi/client/Serialization.scala +++ /dev/null @@ -1,17 +0,0 @@ -package org.zalando.nakadi.client - - -/** - * - */ -trait NakadiDeserializer[T] { - def fromJson(from: String): T -} - -/** - * - */ -trait NakadiSerializer[T] { - def toJson(from: T): String -} - diff --git a/client/src/main/scala/org/zalando/nakadi/client/actor/EventConsumingActor.scala b/client/src/main/scala/org/zalando/nakadi/client/actor/EventConsumingActor.scala index a85fb48..0e43cae 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/actor/EventConsumingActor.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/actor/EventConsumingActor.scala @@ -7,7 +7,7 @@ import akka.stream.actor.ActorSubscriberMessage.OnNext import akka.stream.actor.RequestStrategy import akka.util.ByteString import org.zalando.nakadi.client.Listener -import org.zalando.nakadi.client.NakadiDeserializer +import org.zalando.nakadi.client.Deserializer import org.zalando.nakadi.client.model.Cursor import scala.util.Try import scala.util.Success @@ -22,7 +22,7 @@ object EventConsumer { case class ShutdownMsg() } -class EventConsumer[T](url: String, eventType: String, listener: Listener[T], ser: NakadiDeserializer[T]) extends Actor with ActorLogging with ActorSubscriber { +class EventConsumer[T](url: String, eventType: String, listener: Listener[T], ser: Deserializer[T]) extends Actor with ActorLogging with ActorSubscriber { import EventConsumer._ var count = 0 diff --git a/client/src/main/scala/org/zalando/nakadi/client/model/JacksonJsonMarshaller.scala b/client/src/main/scala/org/zalando/nakadi/client/model/JacksonJsonMarshaller.scala index fea71a1..9611c8f 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/model/JacksonJsonMarshaller.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/model/JacksonJsonMarshaller.scala @@ -4,8 +4,8 @@ import scala.reflect.runtime.universe import scala.reflect.runtime.universe import scala.reflect.runtime.universe._ import org.slf4j.LoggerFactory -import org.zalando.nakadi.client.NakadiDeserializer -import org.zalando.nakadi.client.NakadiSerializer +import org.zalando.nakadi.client.Deserializer +import org.zalando.nakadi.client.Serializer import com.fasterxml.jackson.annotation.JsonInclude import com.fasterxml.jackson.core.JsonFactory import com.fasterxml.jackson.core.JsonParser @@ -23,13 +23,7 @@ import com.typesafe.scalalogging.Logger import com.fasterxml.jackson.databind.`type`.ArrayType import com.fasterxml.jackson.databind.jsontype.TypeDeserializer -//case class CustomDeserialier(array: ArrayType, des: JsonDeserializer[AnyRef], elem: TypeDeserializer) extends ObjectArrayDeserializer(array, des, elem) { -// override def getNullValue():ArrayList={ -// null -// } -//} - -trait JacksonJsonMarshaller { +object JacksonJsonMarshaller { val logger = Logger(LoggerFactory.getLogger(this.getClass)) val factory = new JsonFactory(); @@ -59,29 +53,27 @@ trait JacksonJsonMarshaller { implicit def listOfEventTypeTR: TypeReference[Seq[EventType]] = new TypeReference[Seq[EventType]] {} implicit def listOfPartitionTR: TypeReference[Seq[Partition]] = new TypeReference[Seq[Partition]] {} - - - implicit def optionalDeserializer[T](implicit expectedType: TypeReference[T]): NakadiDeserializer[Option[T]] = new NakadiDeserializer[Option[T]] { - def fromJson(from: String): Option[T] = { - - defaultObjectMapper.readValue[Option[T]](from, expectedType) - } + implicit def optionalDeserializer[T](implicit expectedType: TypeReference[T]): Deserializer[Option[T]] = new Deserializer[Option[T]] { + def from(from: String): Option[T] = { + + defaultObjectMapper.readValue[Option[T]](from, expectedType) + } } - implicit def serializer[T]: NakadiSerializer[T] = new NakadiSerializer[T] { - def toJson(from: T): String = defaultObjectMapper.writeValueAsString(from) + implicit def serializer[T]: Serializer[T] = new Serializer[T] { + def to(from: T): String = defaultObjectMapper.writeValueAsString(from) } - implicit def deserializer[T](implicit expectedType: TypeReference[T]): NakadiDeserializer[T] = new NakadiDeserializer[T] { - def fromJson(from: String): T = defaultObjectMapper.readValue[T](from, expectedType) + implicit def deserializer[T](implicit expectedType: TypeReference[T]): Deserializer[T] = new Deserializer[T] { + def from(from: String): T = defaultObjectMapper.readValue[T](from, expectedType) } - lazy val defaultObjectMapper: ObjectMapper = new ObjectMapper().registerModule(new DefaultScalaModule) + lazy val defaultObjectMapper: ObjectMapper = new ObjectMapper() // + .registerModule(new DefaultScalaModule) .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) .setPropertyNamingStrategy(PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES) - .setSerializationInclusion(JsonInclude.Include.NON_EMPTY) - // .enable(DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT) + .setSerializationInclusion(JsonInclude.Include.NON_NULL) .addHandler(new DeserializationProblemHandler() { override def handleUnknownProperty(ctxt: DeserializationContext, jp: JsonParser, deserializer: JsonDeserializer[_], diff --git a/client/src/main/scala/org/zalando/nakadi/client/model/SprayJsonMarshaller.scala b/client/src/main/scala/org/zalando/nakadi/client/model/SprayJsonMarshaller.scala index 65cafae..40aa351 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/model/SprayJsonMarshaller.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/model/SprayJsonMarshaller.scala @@ -1,19 +1,6 @@ package org.zalando.nakadi.client.model -import org.zalando.nakadi.client.utils.ParseHelper -import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport -import akka.http.scaladsl.marshalling.Marshaller -import akka.http.scaladsl.unmarshalling.Unmarshaller -import spray.json._ -import spray.json.DefaultJsonProtocol -import spray.json.JsValue -import spray.json.JsonFormat -import spray.json.JsonReader -import spray.json.JsonWriter -import spray.json.pimpAny -import spray.json.pimpString -import org.zalando.nakadi.client.NakadiSerializer -import org.zalando.nakadi.client.NakadiDeserializer + //trait SprayJsonMarshaller extends SprayJsonSupport with DefaultJsonProtocol { // import ParseHelper._ diff --git a/client/src/main/scala/org/zalando/nakadi/client/scala/ClientBuilder.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/ClientBuilder.scala new file mode 100644 index 0000000..b36a923 --- /dev/null +++ b/client/src/main/scala/org/zalando/nakadi/client/scala/ClientBuilder.scala @@ -0,0 +1,71 @@ +package org.zalando.nakadi.client.scala + +import java.util.function.Supplier + + +object ClientBuilder { + + def apply( + host: String = null, + port: Int = DEFAULT_PORT, + tokenProvider: () => String = null, + securedConnection: Boolean = true, + verifySSlCertificate: Boolean = true) = { + + } + + private val DEFAULT_PORT = 443 +} + +class ClientBuilder private (host: String = "", + port: Int, tokenProvider: () => String = () => "", + securedConnection: Boolean = true, verifySSlCertificate: Boolean = true) { + def this() = this(null, ClientBuilder.DEFAULT_PORT, null, true, true) + def withHost(host: String): ClientBuilder = new ClientBuilder( + checkNotNull(host), + port, + tokenProvider, + securedConnection, + verifySSlCertificate) + + def withPort(port: Int): ClientBuilder = new ClientBuilder( + host, + port, + tokenProvider, + securedConnection, + verifySSlCertificate) + + def withTokenProvider(tokenProvider: () => String): ClientBuilder = new ClientBuilder( + host, + port, + checkNotNull(tokenProvider), + securedConnection, + verifySSlCertificate) + + def withTokenProvider4Java(tokenProvider: Supplier[String]): ClientBuilder = withTokenProvider(() => tokenProvider.get()) + + def withSecuredConnection(securedConnection: Boolean = true): ClientBuilder = new ClientBuilder( + host, + port, + tokenProvider, + checkNotNull(securedConnection), + verifySSlCertificate) + + def withVerifiedSslCertificate(verifySSlCertificate: Boolean = true): ClientBuilder = new ClientBuilder( + host, + port, + tokenProvider, + securedConnection, + checkNotNull(verifySSlCertificate)) + + def build: Client = new ClientImpl(Connection.newConnection(host, port, tokenProvider, securedConnection, verifySSlCertificate), "UTF-8") + + def buildJavaClient:org.zalando.nakadi.client.java.Client = new org.zalando.nakadi.client.java.ClientImpl(build) + + private def checkNotNull[T](subject: T): T = + if (Option(subject).isEmpty) throw new NullPointerException else subject + + private def checkState[T](subject: T, predicate: (T) => Boolean, msg: String): T = + if (predicate(subject)) subject else throw new IllegalStateException() + +} \ No newline at end of file diff --git a/client/src/main/scala/org/zalando/nakadi/client/ClientImpl.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala similarity index 75% rename from client/src/main/scala/org/zalando/nakadi/client/ClientImpl.scala rename to client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala index 251c8d6..f87b8a7 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/ClientImpl.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala @@ -1,43 +1,48 @@ -package org.zalando.nakadi.client +package org.zalando.nakadi.client.scala import scala.{ Left, Right } import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.Future import scala.concurrent.duration.DurationInt - import org.slf4j.LoggerFactory import org.zalando.nakadi.client.model._ - import com.typesafe.scalalogging.Logger - import akka.actor.Terminated import akka.http.scaladsl.model.{ HttpHeader, HttpMethod, HttpMethods, HttpResponse, MediaRange } import akka.http.scaladsl.model.MediaTypes.`application/json` import akka.http.scaladsl.model.StatusCodes import akka.http.scaladsl.model.headers.{ Accept, RawHeader } import akka.http.scaladsl.unmarshalling.Unmarshal +import org.zalando.nakadi.client.StreamParameters +import org.zalando.nakadi.client.Serializer +import org.zalando.nakadi.client.Deserializer +import org.zalando.nakadi.client.Listener +import org.zalando.nakadi.client.ClientError +import akka.dispatch.sysmsg.Failed +import scala.concurrent.Await + private[client] class ClientImpl(connection: Connection, charSet: String = "UTF-8") extends Client with HttpFactory { implicit val materializer = connection.materializer val logger = Logger(LoggerFactory.getLogger(this.getClass)) - def metrics()(implicit ser: NakadiDeserializer[Metrics]): Future[Either[ClientError, Option[Metrics]]] = { + def getMetrics()(implicit ser: Deserializer[Metrics]): Future[Either[ClientError, Option[Metrics]]] = { logFutureEither(connection.get(URI_METRICS).flatMap(mapToEither(_))) } - def eventTypes()(implicit ser: NakadiDeserializer[Seq[EventType]]): Future[Either[ClientError, Option[Seq[EventType]]]] = { + def getEventTypes()(implicit ser: Deserializer[Seq[EventType]]): Future[Either[ClientError, Option[Seq[EventType]]]] = { logFutureEither(connection.get(URI_EVENT_TYPES).flatMap(mapToEither(_))) } - def newEventType(eventType: EventType)(implicit ser: NakadiSerializer[EventType]): Future[Option[ClientError]] = { + def createEventType(eventType: EventType)(implicit ser: Serializer[EventType]): Future[Option[ClientError]] = { logFutureOption(connection.post(URI_EVENT_TYPES, eventType).flatMap(mapToOption(_))) } - def eventType(name: String)(implicit ser: NakadiDeserializer[EventType]): Future[Either[ClientError, Option[EventType]]] = { + def getEventType(name: String)(implicit ser: Deserializer[EventType]): Future[Either[ClientError, Option[EventType]]] = { logFutureEither(connection.get(URI_EVENT_TYPE_BY_NAME.format(name)).flatMap(in => mapToEither(in))) } - def updateEventType(name: String, eventType: EventType)(implicit ser: NakadiSerializer[EventType]): Future[Option[ClientError]] = { + def updateEventType(name: String, eventType: EventType)(implicit ser: Serializer[EventType]): Future[Option[ClientError]] = { val result = connection.put(URI_EVENT_TYPE_BY_NAME.format(name), eventType) logFutureOption(result.flatMap(in => mapToOption(in))) } @@ -46,37 +51,40 @@ private[client] class ClientImpl(connection: Connection, charSet: String = "UTF- logFutureOption(connection.delete(URI_EVENT_TYPE_BY_NAME.format(name)).flatMap(in => mapToOption(in))) } - def newEvents[T](name: String, events: Seq[T])(implicit ser: NakadiSerializer[Seq[T]]): Future[Option[ClientError]] = { + def publishEvents[T](name: String, events: Seq[T])(implicit ser: Serializer[Seq[T]]): Future[Option[ClientError]] = { logFutureOption(connection.post(URI_EVENTS_OF_EVENT_TYPE.format(name), events).flatMap(in => mapToOption(in))) } - def events[T](name: String, params: Option[StreamParameters])(implicit ser: NakadiDeserializer[T]): Future[Either[ClientError, Option[T]]] = { + def publishEvents[T](name: String, params: Option[StreamParameters])(implicit ser: Deserializer[T]): Future[Either[ClientError, Option[T]]] = { val headers = withHeaders(params) :+ Accept(MediaRange(`application/json`)) logFutureEither(connection.get(URI_EVENTS_OF_EVENT_TYPE.format(name), headers).flatMap(in => mapToEither(in))) } - def partitions(name: String)(implicit ser: NakadiDeserializer[Seq[Partition]]): Future[Either[ClientError, Option[Seq[Partition]]]] = { + def getPartitions(name: String)(implicit ser: Deserializer[Seq[Partition]]): Future[Either[ClientError, Option[Seq[Partition]]]] = { logFutureEither(connection.get(URI_PARTITIONS_BY_EVENT_TYPE.format(name)).flatMap(in => mapToEither(in))) } - def partitionById(name: String, id: String)(implicit ser: NakadiDeserializer[Partition]): Future[Either[ClientError, Option[Partition]]] = { + def getPartitionById(name: String, id: String)(implicit ser: Deserializer[Partition]): Future[Either[ClientError, Option[Partition]]] = { logFutureEither(connection.get(URI_PARTITION_BY_EVENT_TYPE_AND_ID.format(name)).flatMap(in => mapToEither(in))) } - def validationStrategies()(implicit des: NakadiDeserializer[Seq[EventValidationStrategy.Value]]): Future[Either[ClientError, Option[Seq[EventValidationStrategy.Value]]]] = { + def getValidationStrategies()(implicit des: Deserializer[Seq[EventValidationStrategy.Value]]): Future[Either[ClientError, Option[Seq[EventValidationStrategy.Value]]]] = { logFutureEither(connection.get(URI_VALIDATION_STRATEGIES).flatMap(mapToEither(_))) } - def enrichmentStrategies()(implicit des: NakadiDeserializer[Seq[EventEnrichmentStrategy.Value]]): Future[Either[ClientError, Option[Seq[EventEnrichmentStrategy.Value]]]] = { + def getEnrichmentStrategies()(implicit des: Deserializer[Seq[EventEnrichmentStrategy.Value]]): Future[Either[ClientError, Option[Seq[EventEnrichmentStrategy.Value]]]] = { logFutureEither(connection.get(URI_ENRICHMENT_STRATEGIES).flatMap(mapToEither(_))) } - def partitionStrategies()(implicit des: NakadiDeserializer[Seq[PartitionStrategy.Value]]): Future[Either[ClientError, Option[Seq[PartitionStrategy.Value]]]] = + def getPartitionStrategies()(implicit des: Deserializer[Seq[PartitionStrategy.Value]]): Future[Either[ClientError, Option[Seq[PartitionStrategy.Value]]]] = logFutureEither(connection.get(URI_PARTITIONING_STRATEGIES).flatMap(mapToEither(_))) - def stop(): Future[Terminated] = connection.stop() + def stop(): Future[Option[ClientError]] = { + val result = Await.ready(connection.stop(), 120.seconds) + Future.successful(None) + } - def subscribe[T](eventType: String, params: StreamParameters, listener: Listener[T])(implicit ser: NakadiDeserializer[T]): Future[Option[ClientError]] = { + def subscribe[T](eventType: String, params: StreamParameters, listener: Listener[T])(implicit ser: Deserializer[T]): Future[Option[ClientError]] = { //TODO: Validate here before starting listening (eventType, params, listener) match { @@ -122,12 +130,12 @@ private[client] class ClientImpl(connection: Connection, charSet: String = "UTF- } } - private[client] def mapToEither[T](response: HttpResponse)(implicit deserializer: NakadiDeserializer[T]): Future[Either[ClientError, Option[T]]] = { + private[client] def mapToEither[T](response: HttpResponse)(implicit deserializer: Deserializer[T]): Future[Either[ClientError, Option[T]]] = { logger.debug("received [response={}]", response) response match { case HttpResponse(status, headers, entity, protocol) if (status.isSuccess()) => try { - Unmarshal(entity).to[String].map(body => Right(Some(deserializer.fromJson(body)))) + Unmarshal(entity).to[String].map(body => Right(Some(deserializer.from(body)))) } catch { case e: Throwable => val msg = "Failed to deserialise the content with error: %s".format(e.getMessage) diff --git a/client/src/main/scala/org/zalando/nakadi/client/Connection.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala similarity index 85% rename from client/src/main/scala/org/zalando/nakadi/client/Connection.scala rename to client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala index a3fa4c9..4264190 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/Connection.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala @@ -1,4 +1,5 @@ -package org.zalando.nakadi.client +package org.zalando.nakadi.client.scala + import java.security.SecureRandom import java.security.cert.X509Certificate @@ -19,6 +20,10 @@ import akka.stream.scaladsl.{ Flow, Sink, Source } import akka.util.ByteString import javax.net.ssl.{ SSLContext, TrustManager, X509TrustManager } import org.zalando.nakadi.client.actor.EventConsumer.ShutdownMsg +import org.zalando.nakadi.client.Deserializer +import org.zalando.nakadi.client.Serializer +import org.zalando.nakadi.client.Listener + trait Connection extends HttpFactory { @@ -32,9 +37,9 @@ trait Connection extends HttpFactory { def get(endpoint: String, headers: Seq[HttpHeader]): Future[HttpResponse] def stream(endpoint: String, headers: Seq[HttpHeader]): Future[HttpResponse] def delete(endpoint: String): Future[HttpResponse] - def post[T](endpoint: String, model: T)(implicit serializer: NakadiSerializer[T]): Future[HttpResponse] - def put[T](endpoint: String, model: T)(implicit serializer: NakadiSerializer[T]): Future[HttpResponse] - def subscribe[T](url: String, eventType: String, request: HttpRequest, listener: Listener[T])(implicit des: NakadiDeserializer[T]) + def post[T](endpoint: String, model: T)(implicit serializer: Serializer[T]): Future[HttpResponse] + def put[T](endpoint: String, model: T)(implicit serializer: Serializer[T]): Future[HttpResponse] + def subscribe[T](url: String, eventType: String, request: HttpRequest, listener: Listener[T])(implicit des: Deserializer[T]) def stop(): Future[Terminated] def materializer(): ActorMaterializer @@ -70,10 +75,10 @@ object Connection { } /** - * Class for handling the configuration and basic http calls. + * Class for handling the basic http calls. */ -private[client] class ConnectionImpl(val host: String, val port: Int, val tokenProvider: () => String, securedConnection: Boolean, verifySSlCertificate: Boolean) extends Connection { +sealed class ConnectionImpl(val host: String, val port: Int, val tokenProvider: () => String, securedConnection: Boolean, verifySSlCertificate: Boolean) extends Connection { import Connection._ private implicit val actorSystem = ActorSystem("Nakadi-Client-Connections") @@ -103,13 +108,13 @@ private[client] class ConnectionImpl(val host: String, val port: Int, val tokenP executeCall(withHttpRequest(endpoint, HttpMethods.GET, headers, tokenProvider)) //TODO: Change to stream single event } - def put[T](endpoint: String, model: T)(implicit serializer: NakadiSerializer[T]): Future[HttpResponse] = { + def put[T](endpoint: String, model: T)(implicit serializer: Serializer[T]): Future[HttpResponse] = { logger.info("Get: {}", endpoint) executeCall(withHttpRequest(endpoint, HttpMethods.GET, Nil, tokenProvider)) } - def post[T](endpoint: String, model: T)(implicit serializer: NakadiSerializer[T]): Future[HttpResponse] = { - val entity = serializer.toJson(model) + def post[T](endpoint: String, model: T)(implicit serializer: Serializer[T]): Future[HttpResponse] = { + val entity = serializer.to(model) logger.info("Posting to endpoint {}", endpoint) logger.debug("Data to post {}", entity) executeCall(withHttpRequestAndPayload(endpoint, entity, HttpMethods.POST, tokenProvider)) @@ -137,7 +142,7 @@ private[client] class ConnectionImpl(val host: String, val port: Int, val tokenP def stop(): Future[Terminated] = actorSystem.terminate() - def subscribe[T](url: String, eventType: String, request: HttpRequest, listener: Listener[T])(implicit des: NakadiDeserializer[T]) = { + def subscribe[T](url: String, eventType: String, request: HttpRequest, listener: Listener[T])(implicit des: Deserializer[T]) = { import EventConsumer._ case class MyEventExample(orderNumber: String) val subscriberRef = actorSystem.actorOf(Props(classOf[EventConsumer[T]], url, eventType, listener, des)) diff --git a/client/src/main/scala/org/zalando/nakadi/client/HttpFactory.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/HttpFactory.scala similarity index 97% rename from client/src/main/scala/org/zalando/nakadi/client/HttpFactory.scala rename to client/src/main/scala/org/zalando/nakadi/client/scala/HttpFactory.scala index cd20710..0d6c1d8 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/HttpFactory.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/scala/HttpFactory.scala @@ -1,4 +1,4 @@ -package org.zalando.nakadi.client +package org.zalando.nakadi.client.scala import akka.http.scaladsl.model.HttpHeader import akka.http.scaladsl.model.headers.RawHeader @@ -35,7 +35,9 @@ import javax.net.ssl.TrustManager import javax.net.ssl.X509TrustManager import akka.http.scaladsl.model.HttpHeader import scala.collection.immutable.Seq +import org.zalando.nakadi.client.StreamParameters +// trait HttpFactory { type TokenProvider = () => String def withHeaders(params: Option[StreamParameters]): List[HttpHeader] = { @@ -71,4 +73,4 @@ trait HttpFactory { } } - +// diff --git a/client/src/main/scala/org/zalando/nakadi/client/package.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/package.scala similarity index 91% rename from client/src/main/scala/org/zalando/nakadi/client/package.scala rename to client/src/main/scala/org/zalando/nakadi/client/scala/package.scala index 4b40e20..9dbe751 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/package.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/scala/package.scala @@ -1,7 +1,6 @@ -package org.zalando.nakadi +package org.zalando.nakadi.client -package object client { - +package object scala { val URI_METRICS = "/metrics" @@ -18,5 +17,4 @@ package object client { val URI_VALIDATION_STRATEGIES = "/registry/validation-strategies" val URI_ENRICHMENT_STRATEGIES = "/registry/enrichment-strategies" val URI_PARTITIONING_STRATEGIES = "/registry/partition-strategies" - } \ No newline at end of file diff --git a/client/src/main/scala/org/zalando/nakadi/client/utils/Conversions.scala b/client/src/main/scala/org/zalando/nakadi/client/utils/Conversions.scala new file mode 100644 index 0000000..6fb6055 --- /dev/null +++ b/client/src/main/scala/org/zalando/nakadi/client/utils/Conversions.scala @@ -0,0 +1,36 @@ +package org.zalando.nakadi.client.utils + +import java.util.concurrent.TimeUnit +import scala.concurrent.Await +import scala.concurrent.duration.Duration +import collection.JavaConversions._ + + +object Conversions { + + + private def extract[T](either: Either[String, T]): T = either match { + case Left(error) => throw new RuntimeException(error) + case Right(t) => t + } + +// def metricsDeserializer= + + + def convert[T](x: scala.concurrent.Future[Either[String, T]]): java.util.concurrent.Future[T] = + new MFuture[Either[String, T], T](x, a => extract(a)) + + +} + + private class MFuture[A, B](f: scala.concurrent.Future[A], converter: A => B) extends java.util.concurrent.Future[B]{ + override def isCancelled: Boolean = throw new UnsupportedOperationException + + override def get(): B = converter.apply(Await.result(f, Duration.Inf)) + + override def get(timeout: Long, unit: TimeUnit): B = converter.apply(Await.result(f, Duration.create(timeout, unit))) + + override def cancel(mayInterruptIfRunning: Boolean): Boolean = throw new UnsupportedOperationException + + override def isDone: Boolean = f.isCompleted +} diff --git a/client/src/test/scala/org/zalando/nakadi/client/ClientTest.scala b/client/src/test/scala/org/zalando/nakadi/client/scala/ClientTest.scala similarity index 92% rename from client/src/test/scala/org/zalando/nakadi/client/ClientTest.scala rename to client/src/test/scala/org/zalando/nakadi/client/scala/ClientTest.scala index b8fcf5a..e27d652 100644 --- a/client/src/test/scala/org/zalando/nakadi/client/ClientTest.scala +++ b/client/src/test/scala/org/zalando/nakadi/client/scala/ClientTest.scala @@ -1,4 +1,4 @@ -package org.zalando.nakadi.client +package org.zalando.nakadi.client.scala import scala.concurrent.Future import scala.concurrent.Await @@ -24,7 +24,8 @@ import akka.http.scaladsl.model.HttpEntity import akka.http.scaladsl.model.ContentTypes import org.zalando.nakadi.client.model.JacksonJsonMarshaller -class ClientTest extends WordSpec with Matchers with JacksonJsonMarshaller with MockitoSugar with BeforeAndAfter { +class ClientTest extends WordSpec with Matchers with MockitoSugar with BeforeAndAfter { + import JacksonJsonMarshaller._ private var connection: Connection = mock[Connection] private val client: Client = new ClientImpl(connection) before { diff --git a/client/src/test/scala/org/zalando/nakadi/client/DeserializerSerializerTest.scala b/client/src/test/scala/org/zalando/nakadi/client/scala/DeserializerSerializerTest.scala similarity index 93% rename from client/src/test/scala/org/zalando/nakadi/client/DeserializerSerializerTest.scala rename to client/src/test/scala/org/zalando/nakadi/client/scala/DeserializerSerializerTest.scala index b2b925e..eb08564 100644 --- a/client/src/test/scala/org/zalando/nakadi/client/DeserializerSerializerTest.scala +++ b/client/src/test/scala/org/zalando/nakadi/client/scala/DeserializerSerializerTest.scala @@ -1,4 +1,4 @@ -package org.zalando.nakadi.client +package org.zalando.nakadi.client.scala import scala.concurrent.Await import scala.concurrent.duration.DurationInt @@ -12,8 +12,9 @@ import akka.stream.Materializer import spray.json.JsonFormat import org.zalando.nakadi.client.model.JacksonJsonMarshaller -class DeserializerSerializerTest extends WordSpec with Matchers with JacksonJsonMarshaller with AkkaConfig { +class DeserializerSerializerTest extends WordSpec with Matchers with AkkaConfig { import TestJsonEntity._ + import JacksonJsonMarshaller._ val file = getClass().getClassLoader().getResource("Events.txt").getFile val input = scala.io.Source.fromFile(file).mkString diff --git a/client/src/test/scala/org/zalando/nakadi/client/EnumModule.scala b/client/src/test/scala/org/zalando/nakadi/client/scala/EnumModule.scala similarity index 98% rename from client/src/test/scala/org/zalando/nakadi/client/EnumModule.scala rename to client/src/test/scala/org/zalando/nakadi/client/scala/EnumModule.scala index 3ed2bad..5c3f928 100644 --- a/client/src/test/scala/org/zalando/nakadi/client/EnumModule.scala +++ b/client/src/test/scala/org/zalando/nakadi/client/scala/EnumModule.scala @@ -1,4 +1,4 @@ -package org.zalando.nakadi.client +package org.zalando.nakadi.client.scala import scala.reflect._ import scala.reflect.runtime.universe._ diff --git a/client/src/test/scala/org/zalando/nakadi/client/SerializerDeserializerTest.scala b/client/src/test/scala/org/zalando/nakadi/client/scala/SerializerDeserializerTest.scala similarity index 90% rename from client/src/test/scala/org/zalando/nakadi/client/SerializerDeserializerTest.scala rename to client/src/test/scala/org/zalando/nakadi/client/scala/SerializerDeserializerTest.scala index c1e48eb..35e2c06 100644 --- a/client/src/test/scala/org/zalando/nakadi/client/SerializerDeserializerTest.scala +++ b/client/src/test/scala/org/zalando/nakadi/client/scala/SerializerDeserializerTest.scala @@ -1,4 +1,4 @@ -package org.zalando.nakadi.client +package org.zalando.nakadi.client.scala import scala.concurrent.duration.DurationInt import org.scalatest.Matchers @@ -11,6 +11,8 @@ import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.module.scala.JsonScalaEnumeration import org.zalando.nakadi.client.util.TestScalaEntity import org.zalando.nakadi.client.model._ +import org.zalando.nakadi.client.Deserializer +import org.zalando.nakadi.client.Serializer /** * Tests the Marshalling and Umarshalling of the same object in a single run. It tests in this sequence: 1.Marshall and 2.Unmarshall.
      @@ -18,8 +20,8 @@ import org.zalando.nakadi.client.model._ * Marshallers/Unmarshallers are used and produce different * unexpected results. */ -class SerializerDeserializerTest extends WordSpec with Matchers with JacksonJsonMarshaller with AkkaConfig { - +class SerializerDeserializerTest extends WordSpec with Matchers with AkkaConfig { +import JacksonJsonMarshaller._ import TestScalaEntity._ "When an entity(scala object) is marshalled and unmarshalled it" should { @@ -77,10 +79,10 @@ class SerializerDeserializerTest extends WordSpec with Matchers with JacksonJson } - def checkSerializationDeserializationProcess[T](key: String, value: T)(implicit ser: NakadiSerializer[T], des: NakadiDeserializer[T]) { - val jsonEntity = ser.toJson(value) // Marshal + def checkSerializationDeserializationProcess[T](key: String, value: T)(implicit ser: Serializer[T], des: Deserializer[T]) { + val jsonEntity = ser.to(value) // Marshal println("#### Json-Entity:" + jsonEntity) - val scalaEntity = des.fromJson(jsonEntity) //Unmarshal + val scalaEntity = des.from(jsonEntity) //Unmarshal println("#### Scala-Entity:" + scalaEntity) assert(scalaEntity == value, s"Failed to marshall $key correctly!!!") } diff --git a/client/src/test/scala/org/zalando/nakadi/client/TestFactory.scala b/client/src/test/scala/org/zalando/nakadi/client/scala/TestFactory.scala similarity index 66% rename from client/src/test/scala/org/zalando/nakadi/client/TestFactory.scala rename to client/src/test/scala/org/zalando/nakadi/client/scala/TestFactory.scala index bb99e55..fb58893 100644 --- a/client/src/test/scala/org/zalando/nakadi/client/TestFactory.scala +++ b/client/src/test/scala/org/zalando/nakadi/client/scala/TestFactory.scala @@ -1,10 +1,9 @@ -package org.zalando.nakadi.client +package org.zalando.nakadi.client.scala import scala.concurrent.Await import scala.concurrent.Future import scala.concurrent.duration.DurationInt import scala.util.Random - import org.joda.time.DateTime import org.zalando.nakadi.client.model.Event import org.zalando.nakadi.client.model.EventMetadata @@ -15,9 +14,13 @@ import org.zalando.nakadi.client.model.EventTypeSchema import org.zalando.nakadi.client.model.JacksonJsonMarshaller import org.zalando.nakadi.client.model.PartitionStrategy import org.zalando.nakadi.client.model.SchemaType +import org.zalando.nakadi.client.model.JacksonJsonMarshaller +import org.zalando.nakadi.client.Serializer +import org.zalando.nakadi.client.model.JacksonJsonMarshaller +import org.zalando.nakadi.client.Deserializer trait ClientFactory { - val host = "nakadi-sandbox.my-test.fernan.do" + val host = "nakadi-sandbox.aruha-test.zalan.do" val OAuth2Token = () => "" val port = 443 val connection = Connection.newConnection(host, port, OAuth2Token, true, false) @@ -26,26 +29,26 @@ trait ClientFactory { } -case class EventActions(client: Client) extends JacksonJsonMarshaller { - - def create[T](name: String, event: Seq[T])(implicit ser: NakadiSerializer[Seq[T]]) = { - client.newEvents[T](name, event) +case class EventActions(client: Client) { +import JacksonJsonMarshaller._ + def create[T](name: String, event: Seq[T])(implicit ser: Serializer[Seq[T]]) = { + client.publishEvents[T](name, event) } } -case class EventTypesActions(client: Client) extends JacksonJsonMarshaller { - - def create(event: EventType)(implicit ser: NakadiSerializer[EventType]) = { - executeCall(client.newEventType(event)) +case class EventTypesActions(client: Client) { +import JacksonJsonMarshaller._ + def create(event: EventType)(implicit ser: Serializer[EventType]) = { + executeCall(client.createEventType(event)) } - def update(event: EventType)(implicit ser: NakadiSerializer[EventType]) = { + def update(event: EventType)(implicit ser: Serializer[EventType]) = { executeCall(client.updateEventType(event.name, event)) } - def get(name: String)(implicit ser: NakadiDeserializer[Option[EventType]]) = { - executeCall(client.eventType(name)) + def get(name: String)(implicit ser: Deserializer[Option[EventType]]) = { + executeCall(client.getEventType(name)) } - def getAll()(implicit ser: NakadiDeserializer[Option[Seq[EventType]]]) = { - executeCall(client.eventTypes()) + def getAll()(implicit ser: Deserializer[Option[Seq[EventType]]]) = { + executeCall(client.getEventTypes()) } def delete(name: String) = { executeCall(client.deleteEventType(name)) diff --git a/model/src/main/scala/org/zalando/nakadi/client/model/Model.scala b/model/src/main/scala/org/zalando/nakadi/client/model/Model.scala index 7bde412..4ee8e83 100644 --- a/model/src/main/scala/org/zalando/nakadi/client/model/Model.scala +++ b/model/src/main/scala/org/zalando/nakadi/client/model/Model.scala @@ -22,7 +22,7 @@ import com.fasterxml.jackson.databind.annotation.JsonDeserialize * @param title */ trait Event { - def metadata(): Option[EventMetadata] + } /** @@ -51,6 +51,7 @@ case class EventMetadata( * */ trait BusinessEvent extends Event { + def metadata(): Option[EventMetadata] } /** @@ -86,7 +87,7 @@ case class DataChangeEvent[T]( case class Problem( problemType: String, title: String, - status: Int, + status: Integer, detail: Option[String], instance: Option[String]) From 6637d26913814e95320248a332de951696d38137 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Tue, 26 Apr 2016 18:58:30 +0200 Subject: [PATCH 024/183] techjira:LAAS-60 Implementing Java client, WIP! --- .../nakadi/client/java/ClientImpl.java | 23 ++++--- .../nakadi/client/scala/ClientImpl.scala | 3 +- .../nakadi/client/utils/Conversions.scala | 36 ----------- .../client/utils/FutureConversions.scala | 63 +++++++++++++++++++ .../scala/DeserializerSerializerTest.scala | 4 +- .../scala/SerializerDeserializerTest.scala | 4 +- .../client/{util => utils}/AkkaConfig.scala | 2 +- .../{util => utils}/TestJsonEntity.scala | 2 +- .../{util => utils}/TestScalaEntity.scala | 2 +- 9 files changed, 87 insertions(+), 52 deletions(-) delete mode 100644 client/src/main/scala/org/zalando/nakadi/client/utils/Conversions.scala create mode 100644 client/src/main/scala/org/zalando/nakadi/client/utils/FutureConversions.scala rename client/src/test/scala/org/zalando/nakadi/client/{util => utils}/AkkaConfig.scala (87%) rename client/src/test/scala/org/zalando/nakadi/client/{util => utils}/TestJsonEntity.scala (97%) rename client/src/test/scala/org/zalando/nakadi/client/{util => utils}/TestScalaEntity.scala (98%) diff --git a/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java b/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java index 6e202f2..4c1f29e 100644 --- a/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java +++ b/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java @@ -5,15 +5,25 @@ import java.util.concurrent.Future; import java.util.function.Supplier; -import org.zalando.nakadi.client.scala.ClientBuilder; import org.zalando.nakadi.client.Serializer; -import org.zalando.nakadi.client.model.*; +import org.zalando.nakadi.client.model.Event; +import org.zalando.nakadi.client.model.EventEnrichmentStrategy; +import org.zalando.nakadi.client.model.EventType; +import org.zalando.nakadi.client.model.EventValidationStrategy; +import org.zalando.nakadi.client.model.Metrics; +import org.zalando.nakadi.client.model.Partition; +import org.zalando.nakadi.client.model.PartitionStrategy; +import org.zalando.nakadi.client.scala.ClientBuilder; +import org.zalando.nakadi.client.utils.FutureConversions; +import org.zalando.nakadi.client.utils.Serialization; import com.fasterxml.jackson.core.type.TypeReference; public class ClientImpl implements Client { private final org.zalando.nakadi.client.scala.Client client; - + + + public ClientImpl(org.zalando.nakadi.client.scala.Client client) { this.client = client; } @@ -40,15 +50,12 @@ public ClientImpl(String host, // @Override public Future> getMetrics() { - - // Object a= client.metrics(); - return null; + return FutureConversions.fromOptionOfEither2Optional(client.getMetrics(Serialization.metricsDeserializer())); } @Override public Future>> getEventTypes() { - // TODO Auto-generated method stub - return null; + return FutureConversions.fromSeqOfOptionalEither2OptionalList(client.getEventTypes(Serialization.seqOfEventTypeDeserializer())); } @Override diff --git a/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala index f87b8a7..96cddac 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala @@ -20,6 +20,7 @@ import org.zalando.nakadi.client.Listener import org.zalando.nakadi.client.ClientError import akka.dispatch.sysmsg.Failed import scala.concurrent.Await +import scala.concurrent.duration.Duration private[client] class ClientImpl(connection: Connection, charSet: String = "UTF-8") extends Client with HttpFactory { @@ -80,7 +81,7 @@ private[client] class ClientImpl(connection: Connection, charSet: String = "UTF- logFutureEither(connection.get(URI_PARTITIONING_STRATEGIES).flatMap(mapToEither(_))) def stop(): Future[Option[ClientError]] = { - val result = Await.ready(connection.stop(), 120.seconds) + val result = Await.ready(connection.stop(), Duration.Inf) Future.successful(None) } diff --git a/client/src/main/scala/org/zalando/nakadi/client/utils/Conversions.scala b/client/src/main/scala/org/zalando/nakadi/client/utils/Conversions.scala deleted file mode 100644 index 6fb6055..0000000 --- a/client/src/main/scala/org/zalando/nakadi/client/utils/Conversions.scala +++ /dev/null @@ -1,36 +0,0 @@ -package org.zalando.nakadi.client.utils - -import java.util.concurrent.TimeUnit -import scala.concurrent.Await -import scala.concurrent.duration.Duration -import collection.JavaConversions._ - - -object Conversions { - - - private def extract[T](either: Either[String, T]): T = either match { - case Left(error) => throw new RuntimeException(error) - case Right(t) => t - } - -// def metricsDeserializer= - - - def convert[T](x: scala.concurrent.Future[Either[String, T]]): java.util.concurrent.Future[T] = - new MFuture[Either[String, T], T](x, a => extract(a)) - - -} - - private class MFuture[A, B](f: scala.concurrent.Future[A], converter: A => B) extends java.util.concurrent.Future[B]{ - override def isCancelled: Boolean = throw new UnsupportedOperationException - - override def get(): B = converter.apply(Await.result(f, Duration.Inf)) - - override def get(timeout: Long, unit: TimeUnit): B = converter.apply(Await.result(f, Duration.create(timeout, unit))) - - override def cancel(mayInterruptIfRunning: Boolean): Boolean = throw new UnsupportedOperationException - - override def isDone: Boolean = f.isCompleted -} diff --git a/client/src/main/scala/org/zalando/nakadi/client/utils/FutureConversions.scala b/client/src/main/scala/org/zalando/nakadi/client/utils/FutureConversions.scala new file mode 100644 index 0000000..c4e2033 --- /dev/null +++ b/client/src/main/scala/org/zalando/nakadi/client/utils/FutureConversions.scala @@ -0,0 +1,63 @@ +package org.zalando.nakadi.client.utils + +import java.util.concurrent.TimeUnit +import scala.concurrent.Await +import scala.concurrent.duration.Duration +import collection.JavaConversions._ +import org.zalando.nakadi.client.ClientError +import java.util.Optional + +object FutureConversions { + + private def extract[T](either: Either[String, T]): T = either match { + case Left(error) => throw new RuntimeException(error) + case Right(t) => t + } + + def fromOptionOfEither2Optional[T](in: scala.concurrent.Future[Either[ClientError, Option[T]]]): java.util.concurrent.Future[Optional[T]] = { + new MFuture[Either[ClientError, Option[T]], Optional[T]](in, a => fromRightOptionOfEither2Option(a)) + } + + def fromSeqOfOptionalEither2OptionalList[T](in: scala.concurrent.Future[Either[ClientError, Option[Seq[T]]]]): java.util.concurrent.Future[Optional[java.util.List[T]]] = { + new MFuture[Either[ClientError, Option[Seq[T]]], Optional[java.util.List[T]]](in, a => fromSeqOfOptionalEither2OptionalList(a)) + } + + private def fromSequenceToList[T](in: Seq[T]): Optional[java.util.List[T]] = in match { + case Nil => Optional.empty() + case seq => Optional.of(new java.util.ArrayList[T](seq)) + + } + + private def fromRightOptionOfEither2Option[L, R](in: Either[L, Option[R]]): Optional[R] = in match { + case Left(_) => Optional.empty() + case Right(Some(value)) => Optional.of(value) + case Right(None) => Optional.empty() + } + private def fromSeqOfOptionalEither2OptionalList[L, R](in: Either[L, Option[Seq[R]]]): Optional[java.util.List[R]] = in match { + case Left(_) => Optional.empty() + case Right(None) => Optional.empty() + case Right(Some(Nil)) => Optional.of(new java.util.ArrayList[R]()) + case Right(Some(seq)) => Optional.of(new java.util.ArrayList[R](seq)) + } + + private def convertOption[A](option: Option[A]): Optional[A] = option match { + case Some(v) => Optional.of(v) + case None => Optional.empty() + } + + private def convert[T](x: scala.concurrent.Future[Either[String, T]]): java.util.concurrent.Future[T] = + new MFuture[Either[String, T], T](x, a => extract(a)) + +} + +private class MFuture[A, B](f: scala.concurrent.Future[A], converter: A => B) extends java.util.concurrent.Future[B] { + override def isCancelled: Boolean = throw new UnsupportedOperationException + + override def get(): B = converter.apply(Await.result(f, Duration.Inf)) + + override def get(timeout: Long, unit: TimeUnit): B = converter.apply(Await.result(f, Duration.create(timeout, unit))) + + override def cancel(mayInterruptIfRunning: Boolean): Boolean = throw new UnsupportedOperationException + + override def isDone: Boolean = f.isCompleted +} diff --git a/client/src/test/scala/org/zalando/nakadi/client/scala/DeserializerSerializerTest.scala b/client/src/test/scala/org/zalando/nakadi/client/scala/DeserializerSerializerTest.scala index eb08564..aea5c16 100644 --- a/client/src/test/scala/org/zalando/nakadi/client/scala/DeserializerSerializerTest.scala +++ b/client/src/test/scala/org/zalando/nakadi/client/scala/DeserializerSerializerTest.scala @@ -5,8 +5,8 @@ import scala.concurrent.duration.DurationInt import org.scalatest.Matchers import org.scalatest.WordSpec import org.zalando.nakadi.client.model.EventType -import org.zalando.nakadi.client.util.AkkaConfig -import org.zalando.nakadi.client.util.TestJsonEntity +import org.zalando.nakadi.client.utils.AkkaConfig +import org.zalando.nakadi.client.utils.TestJsonEntity import akka.http.scaladsl.unmarshalling.Unmarshaller import akka.stream.Materializer import spray.json.JsonFormat diff --git a/client/src/test/scala/org/zalando/nakadi/client/scala/SerializerDeserializerTest.scala b/client/src/test/scala/org/zalando/nakadi/client/scala/SerializerDeserializerTest.scala index 35e2c06..3789bdc 100644 --- a/client/src/test/scala/org/zalando/nakadi/client/scala/SerializerDeserializerTest.scala +++ b/client/src/test/scala/org/zalando/nakadi/client/scala/SerializerDeserializerTest.scala @@ -5,11 +5,11 @@ import org.scalatest.Matchers import org.scalatest.WordSpec import org.zalando.nakadi.client.model._ import org.zalando.nakadi.client.model.JacksonJsonMarshaller -import org.zalando.nakadi.client.util.AkkaConfig +import org.zalando.nakadi.client.utils.AkkaConfig +import org.zalando.nakadi.client.utils.TestScalaEntity import com.fasterxml.jackson.core.`type`.TypeReference import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.module.scala.JsonScalaEnumeration -import org.zalando.nakadi.client.util.TestScalaEntity import org.zalando.nakadi.client.model._ import org.zalando.nakadi.client.Deserializer import org.zalando.nakadi.client.Serializer diff --git a/client/src/test/scala/org/zalando/nakadi/client/util/AkkaConfig.scala b/client/src/test/scala/org/zalando/nakadi/client/utils/AkkaConfig.scala similarity index 87% rename from client/src/test/scala/org/zalando/nakadi/client/util/AkkaConfig.scala rename to client/src/test/scala/org/zalando/nakadi/client/utils/AkkaConfig.scala index dd0c33f..bbdb134 100644 --- a/client/src/test/scala/org/zalando/nakadi/client/util/AkkaConfig.scala +++ b/client/src/test/scala/org/zalando/nakadi/client/utils/AkkaConfig.scala @@ -1,4 +1,4 @@ -package org.zalando.nakadi.client.util +package org.zalando.nakadi.client.utils import akka.actor.ActorSystem import akka.stream.Materializer diff --git a/client/src/test/scala/org/zalando/nakadi/client/util/TestJsonEntity.scala b/client/src/test/scala/org/zalando/nakadi/client/utils/TestJsonEntity.scala similarity index 97% rename from client/src/test/scala/org/zalando/nakadi/client/util/TestJsonEntity.scala rename to client/src/test/scala/org/zalando/nakadi/client/utils/TestJsonEntity.scala index 6f353ca..86ea3ae 100644 --- a/client/src/test/scala/org/zalando/nakadi/client/util/TestJsonEntity.scala +++ b/client/src/test/scala/org/zalando/nakadi/client/utils/TestJsonEntity.scala @@ -1,4 +1,4 @@ -package org.zalando.nakadi.client.util +package org.zalando.nakadi.client.utils object TestJsonEntity { val singleEvent="""{ diff --git a/client/src/test/scala/org/zalando/nakadi/client/util/TestScalaEntity.scala b/client/src/test/scala/org/zalando/nakadi/client/utils/TestScalaEntity.scala similarity index 98% rename from client/src/test/scala/org/zalando/nakadi/client/util/TestScalaEntity.scala rename to client/src/test/scala/org/zalando/nakadi/client/utils/TestScalaEntity.scala index dd8c59f..452a384 100644 --- a/client/src/test/scala/org/zalando/nakadi/client/util/TestScalaEntity.scala +++ b/client/src/test/scala/org/zalando/nakadi/client/utils/TestScalaEntity.scala @@ -1,4 +1,4 @@ -package org.zalando.nakadi.client.util +package org.zalando.nakadi.client.utils import org.zalando.nakadi.client.model._ import org.zalando.nakadi.client.model.Problem From d72e73788c938f2e2acca5391271e2117b703492 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Tue, 26 Apr 2016 18:58:43 +0200 Subject: [PATCH 025/183] techjira:LAAS-60 Implementing Java client, WIP! --- .../nakadi/client/utils/ImplicitHelper.scala | 17 +++++++++ .../nakadi/client/utils/ConversionsTest.scala | 35 +++++++++++++++++++ 2 files changed, 52 insertions(+) create mode 100644 client/src/main/scala/org/zalando/nakadi/client/utils/ImplicitHelper.scala create mode 100644 client/src/test/scala/org/zalando/nakadi/client/utils/ConversionsTest.scala diff --git a/client/src/main/scala/org/zalando/nakadi/client/utils/ImplicitHelper.scala b/client/src/main/scala/org/zalando/nakadi/client/utils/ImplicitHelper.scala new file mode 100644 index 0000000..e8c3f60 --- /dev/null +++ b/client/src/main/scala/org/zalando/nakadi/client/utils/ImplicitHelper.scala @@ -0,0 +1,17 @@ +package org.zalando.nakadi.client.utils + +import org.zalando.nakadi.client.Deserializer +import org.zalando.nakadi.client.model.Metrics +import org.zalando.nakadi.client.model.JacksonJsonMarshaller +import org.zalando.nakadi.client.model.EventType +import org.zalando.nakadi.client.Serializer + + +/** + * Meant for usage in the client side + */ +object Serialization { + def metricsDeserializer():Deserializer[Metrics] = JacksonJsonMarshaller.deserializer(JacksonJsonMarshaller.metricsTR) + def seqOfEventTypeDeserializer(): Deserializer[Seq[EventType]] = JacksonJsonMarshaller.deserializer(JacksonJsonMarshaller.listOfEventTypeTR) + def eventTypeSerializer():Serializer[EventType] = JacksonJsonMarshaller.serializer +} \ No newline at end of file diff --git a/client/src/test/scala/org/zalando/nakadi/client/utils/ConversionsTest.scala b/client/src/test/scala/org/zalando/nakadi/client/utils/ConversionsTest.scala new file mode 100644 index 0000000..0bf14e9 --- /dev/null +++ b/client/src/test/scala/org/zalando/nakadi/client/utils/ConversionsTest.scala @@ -0,0 +1,35 @@ +package org.zalando.nakadi.client.scala + +import scala.concurrent.Future +import scala.concurrent.Await +import scala.concurrent.duration.DurationInt +import org.mockito.Matchers.any +import org.mockito.Matchers.anyString +import org.mockito.Mockito.reset +import org.mockito.Mockito.times +import org.mockito.Mockito.verify +import org.mockito.Mockito.when +import org.scalatest.BeforeAndAfter +import org.scalatest.FlatSpec +import org.scalatest.Matchers +import org.scalatest.WordSpec +import org.scalatest.mock.MockitoSugar +import akka.http.scaladsl.model.HttpResponse +import akka.stream.Materializer +import spray.json.JsonFormat +import akka.http.scaladsl.model.StatusCodes +import akka.http.scaladsl.model.HttpProtocol +import akka.http.scaladsl.model.HttpProtocols +import akka.http.scaladsl.model.HttpEntity +import akka.http.scaladsl.model.ContentTypes +import org.zalando.nakadi.client.model.JacksonJsonMarshaller + +class ConversionsTest extends WordSpec with Matchers with MockitoSugar { + "Conversions" should { + + "be implemented" in { + fail() + } + } + +} \ No newline at end of file From d1f845edc1eedc30ecdbaee431a9dfabd6b69468 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Thu, 28 Apr 2016 19:45:07 +0200 Subject: [PATCH 026/183] techjira:LAAS-60 Adding Java Model! --- .../java/BatchItemPublishingStatus.java | 38 +++++ .../nakadi/client/java/BatchItemStep.java | 43 ++++++ .../zalando/nakadi/client/java/Client.java | 45 +++--- .../nakadi/client/java/DataOperation.java | 37 +++++ .../client/java/EventEnrichmentStrategy.java | 37 +++++ .../nakadi/client/java/EventTypeCategory.java | 34 +++++ .../client/java/EventValidationStrategy.java | 33 +++++ .../zalando/nakadi/client/java/Metrics.java | 5 + .../nakadi/client/java/PartitionStrategy.java | 38 +++++ .../nakadi/client/java/SchemaType.java | 30 ++++ .../org/zalando/nakadi/client/Listener.scala | 4 +- .../zalando/nakadi/client/scala/Client.scala | 68 ++++----- .../zalando/nakadi/client/scala}/Model.scala | 14 +- .../nakadi/client/java/ClientImpl.java | 135 ++++++++---------- .../client/actor/EventConsumingActor.scala | 35 +++-- .../client/actor/EventReceivingActor.scala | 33 +++++ .../client/model/JacksonJsonMarshaller.scala | 18 +-- .../nakadi/client/scala/ClientImpl.scala | 53 +++---- .../nakadi/client/scala/Connection.scala | 31 ++-- .../nakadi/client/scala/HttpFactory.scala | 22 ++- .../zalando/nakadi/client/scala/package.scala | 1 - .../{scala => utils}/ClientBuilder.scala | 25 ++-- .../client/utils/FutureConversions.scala | 42 ++++-- .../client/utils/GeneralConversions.scala | 25 ++++ .../nakadi/client/utils/ImplicitHelper.scala | 20 ++- .../scala/SerializerDeserializerTest.scala | 1 + .../nakadi/client/scala/TestFactory.scala | 39 +++-- .../nakadi/client/utils/ConversionsTest.scala | 4 +- .../nakadi/client/utils/TestScalaEntity.scala | 4 +- .../client/examples/java/ClientExample.java | 48 +++++++ .../examples/java/EventCreationExample.java | 88 ++++++++++++ .../client/ClientSubscriptionTest.scala | 28 ++-- .../client/EventTypesIntegrationTest.scala | 11 +- .../nakadi/client/KlientIntegrationTest.scala | 32 +++-- .../nakadi/client/example/ClientExample.scala | 10 +- .../client/example2/ClientExample.scala | 2 +- .../nakadi/client/example2/Ecample3.scala | 13 +- .../client/example2/HttpClientExample.scala | 22 +-- .../client/example2/ServerExample.scala | 2 +- .../scala/ClientCreationExample.scala | 26 ++++ .../examples/scala/EventCreationExample.scala | 82 +++++++++++ .../nakadi/client/logic/ReceiverGraph.scala | 2 +- .../nakadi/client/subscription/LeClient.scala | 26 ++++ .../Subscriber.scala | 56 +++++--- model/src/test/resources/application.conf | 29 ---- model/src/test/resources/logback.xml | 18 --- project/Build.scala | 17 +-- project/Dependencies.scala | 11 +- 48 files changed, 1052 insertions(+), 385 deletions(-) create mode 100644 api/src/main/java/org/zalando/nakadi/client/java/BatchItemPublishingStatus.java create mode 100644 api/src/main/java/org/zalando/nakadi/client/java/BatchItemStep.java create mode 100644 api/src/main/java/org/zalando/nakadi/client/java/DataOperation.java create mode 100644 api/src/main/java/org/zalando/nakadi/client/java/EventEnrichmentStrategy.java create mode 100644 api/src/main/java/org/zalando/nakadi/client/java/EventTypeCategory.java create mode 100644 api/src/main/java/org/zalando/nakadi/client/java/EventValidationStrategy.java create mode 100644 api/src/main/java/org/zalando/nakadi/client/java/Metrics.java create mode 100644 api/src/main/java/org/zalando/nakadi/client/java/PartitionStrategy.java create mode 100644 api/src/main/java/org/zalando/nakadi/client/java/SchemaType.java rename {model/src/main/scala/org/zalando/nakadi/client/model => api/src/main/scala/org/zalando/nakadi/client/scala}/Model.scala (98%) create mode 100644 client/src/main/scala/org/zalando/nakadi/client/actor/EventReceivingActor.scala rename client/src/main/scala/org/zalando/nakadi/client/{scala => utils}/ClientBuilder.scala (67%) create mode 100644 client/src/main/scala/org/zalando/nakadi/client/utils/GeneralConversions.scala create mode 100644 it/src/test/java/org/zalando/nakadi/client/examples/java/ClientExample.java create mode 100644 it/src/test/java/org/zalando/nakadi/client/examples/java/EventCreationExample.java create mode 100644 it/src/test/scala/org/zalando/nakadi/client/examples/scala/ClientCreationExample.scala create mode 100644 it/src/test/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala create mode 100644 it/src/test/scala/org/zalando/nakadi/client/subscription/LeClient.scala rename it/src/test/scala/org/zalando/nakadi/client/{example2 => subscription}/Subscriber.scala (56%) delete mode 100644 model/src/test/resources/application.conf delete mode 100644 model/src/test/resources/logback.xml diff --git a/api/src/main/java/org/zalando/nakadi/client/java/BatchItemPublishingStatus.java b/api/src/main/java/org/zalando/nakadi/client/java/BatchItemPublishingStatus.java new file mode 100644 index 0000000..4c28225 --- /dev/null +++ b/api/src/main/java/org/zalando/nakadi/client/java/BatchItemPublishingStatus.java @@ -0,0 +1,38 @@ +package org.zalando.nakadi.client.java; + +import org.zalando.nakadi.client.model.BatchItemPublishingStatus; + +import scala.Option; + +import com.fasterxml.jackson.annotation.JsonValue; + +/** + * Indicator of the submission of the Event within a Batch.
      + * - SUBMITTED indicates successful submission, including commit on he underlying broker.
      + * - FAILED indicates the message submission was not possible and can be resubmitted if so desired.
      + * - ABORTED indicates that the submission of this item was not attempted any further due to a failure on another item in the batch.
      + *
      + */ +public enum BatchItemPublishingStatus { + SUBMITTED("SUBMITTED"), // + FAILED("FAILED"), // + ABORTED("ABORTED"); + private final String status; + + private BatchItemPublishingStatus(String status) { + this.status = status; + } + + @JsonValue + public String getStatus() { + return status; + }; + + public static Option withName(String code) { + for (BatchItemPublishingStatus e : BatchItemPublishingStatus.values()) { + if (e.status.equals(code)) + return Option.apply(e); + } + return Option.empty(); + } +} diff --git a/api/src/main/java/org/zalando/nakadi/client/java/BatchItemStep.java b/api/src/main/java/org/zalando/nakadi/client/java/BatchItemStep.java new file mode 100644 index 0000000..e9b9553 --- /dev/null +++ b/api/src/main/java/org/zalando/nakadi/client/java/BatchItemStep.java @@ -0,0 +1,43 @@ +package org.zalando.nakadi.client.java; + +import org.zalando.nakadi.client.model.BatchItemStep; + +import com.fasterxml.jackson.annotation.JsonValue; + +import scala.Option; + + +/** + * Indicator of the step in the pulbishing process this Event reached. + * In Items that FAILED means the step of the failure. + * - NONE indicates that nothing was yet attempted for the publishing of this Event. Should be present + * only in the case of aborting the publishing during the validation of another (previous) Event.
      + * - VALIDATING, ENRICHING, PARTITIONING and PUBLISHING indicate all the corresponding steps of the + * publishing process.

      + * Values = NONE("NONE"), VALIDATING("VALIDATING"), ENRICHING("ENRICHING"), PARTITIONING("PARTITIONING"), PUBLISHING("PUBLISHING") + */ +public enum BatchItemStep { + NONE("NONE"), // + VALIDATING("VALIDATING"), // + ENRICHING("ENRICHING"), // + PARTITIONING("PARTITIONING"), // + PUBLISHING("PUBLISHING"); + private final String step; + + private BatchItemStep(String step) { + this.step = step; + } + @JsonValue + public String getStep() { + return step; + } + + public static Option withName(String code) { + for (BatchItemStep e : BatchItemStep.values()) { + if (e.step.equals(code)) + return Option.apply(e); + } + return Option.empty(); + } + +} diff --git a/api/src/main/java/org/zalando/nakadi/client/java/Client.java b/api/src/main/java/org/zalando/nakadi/client/java/Client.java index f045365..6748766 100644 --- a/api/src/main/java/org/zalando/nakadi/client/java/Client.java +++ b/api/src/main/java/org/zalando/nakadi/client/java/Client.java @@ -4,16 +4,10 @@ import java.util.Optional; import java.util.concurrent.Future; +import org.zalando.nakadi.client.Deserializer; +import org.zalando.nakadi.client.Listener; import org.zalando.nakadi.client.Serializer; -import org.zalando.nakadi.client.model.Event; -import org.zalando.nakadi.client.model.EventEnrichmentStrategy; -import org.zalando.nakadi.client.model.EventType; -import org.zalando.nakadi.client.model.EventValidationStrategy; -import org.zalando.nakadi.client.model.Metrics; -import org.zalando.nakadi.client.model.Partition; -import org.zalando.nakadi.client.model.PartitionStrategy; - -import com.fasterxml.jackson.core.type.TypeReference; +import org.zalando.nakadi.client.StreamParameters; public interface Client { @@ -82,7 +76,7 @@ public interface Client { * @param ref The Jackson TypeReference of the Event to be used by the default Jackson Marshaller. * @return Void in case of success */ - Future publishEvent(String eventTypeName, T event, TypeReference ref); + Future publishEvent(String eventTypeName, T event); /** * Publishes a List of events to the given eventType using a custom serializer.
      @@ -93,17 +87,16 @@ public interface Client { * @param serializer The custom serializer to serialize the events. * @return Void in case of success */ - Future publishEvents(String eventTypeName, List events, Serializer> serializer); + Future publishEvents(String eventTypeName, List events, Serializer serializer); /** * Publishes a List of events to the given eventType.
      * Partition selection is done using the defined partition resolution,
      * which is defined per topic and managed by the event store. * @param eventTypeName The unique name (id) of the EventType target * @param event The event to be published - * @param ref The Jackson TypeReference of the Event to be used by the default Jackson Marshaller. * @return Void in case of success */ - Future publishEvents(String eventTypeName, List events, TypeReference> ref); + Future publishEvents(String eventTypeName, List events); /** * Retrieves the existing partitions for the given EventType. @@ -112,14 +105,6 @@ public interface Client { */ Future>> getPartitions(String eventTypeName); - /** - * Retrieves the unique partition, identified by the given parameters. - * @param eventTypeName The unique name (id) of the EventType - * @param id The id of the partition - * @return the partition if exists - */ - Future> getPartitionById(String eventTypeName, String id); - /** * Retrieves a List of all Validation strategies supported by the Event store. * @return list of validation strategies @@ -135,11 +120,27 @@ public interface Client { * Retrieves a List of all Partition strategies supported by the Event store. * @return list of enrichment strategies */ - Future>> getPartitionStrategies(); + Future>> getPartitioningStrategies(); /** * Shuts down the communication system of the client * @return Void in case of success */ Future stop(); + + /** + * Registers the subscription of a listener to start streaming events from a partition in non-blocking fashion. + * @param eventTypeName The unique name (id) of the EventType target + * @param parameters Parameters for customizing the details of the streaming. + * @param listener Listener to pass the event to when it is received. + * @return Void in case of success + */ + public Future subscribe(String eventTypeName, StreamParameters parameters, Listener listener, Deserializer deserializer); + /** + * Removes the subscription of a listener, to stop streaming events from a partition. + * @param eventTypeName The unique name (id) of the EventType target + * @param listener Listener to pass the event to when it is received. + * @return Void in case of success + */ + Future unsubscribe(String eventTypeName, Listener listener); } \ No newline at end of file diff --git a/api/src/main/java/org/zalando/nakadi/client/java/DataOperation.java b/api/src/main/java/org/zalando/nakadi/client/java/DataOperation.java new file mode 100644 index 0000000..60fd0f4 --- /dev/null +++ b/api/src/main/java/org/zalando/nakadi/client/java/DataOperation.java @@ -0,0 +1,37 @@ +package org.zalando.nakadi.client.java; + +import org.zalando.nakadi.client.model.DataOperation; + +import com.fasterxml.jackson.annotation.JsonValue; + +import scala.Option; + + +/** + * Identifier for the type of operation to executed on the entity.
      + * C: Creation
      + * U: Update
      + * D: Deletion
      + * S: Snapshot
      + *
      + */ +public enum DataOperation { + CREATE("C"), UPDATE("U"), DELETE("D"), SNAPSHOT("S"); + private final String operation; + + private DataOperation(String operation) { + this.operation = operation; + } + @JsonValue + public String getOperation() { + return operation; + } + + public static Option withName(String code){ + for(DataOperation e : DataOperation.values()){ + if (e.operation.equals(code)) + return Option.apply(e); + } + return Option.empty(); + } +} diff --git a/api/src/main/java/org/zalando/nakadi/client/java/EventEnrichmentStrategy.java b/api/src/main/java/org/zalando/nakadi/client/java/EventEnrichmentStrategy.java new file mode 100644 index 0000000..01ad879 --- /dev/null +++ b/api/src/main/java/org/zalando/nakadi/client/java/EventEnrichmentStrategy.java @@ -0,0 +1,37 @@ +package org.zalando.nakadi.client.java; + +import org.zalando.nakadi.client.model.EventEnrichmentStrategy; + +import com.fasterxml.jackson.annotation.JsonValue; + +import scala.Option; + +/** + * Defines a rule for transformation of an incoming Event. No existing fields + * might be modified. In practice this is used to set automatically values in + * the Event upon reception (i.e. set a reception timestamp on the Event). Rules + * might require additional parameters; see the `doc` field of the existing + * rules for details. See GET /registry/enrichment-strategies for a list of + * available rules. + */ + +public enum EventEnrichmentStrategy { + METADATA("metadata_enrichment"); + + private final String metadata; + + private EventEnrichmentStrategy(String metadata) { + this.metadata = metadata; + } + @JsonValue + public String getMetadata() { + return metadata; + } + public static Option withName(String code) { + for (EventEnrichmentStrategy e : EventEnrichmentStrategy.values()) { + if (e.metadata.equals(code)) + return Option.apply(e); + } + return Option.empty(); + } +} diff --git a/api/src/main/java/org/zalando/nakadi/client/java/EventTypeCategory.java b/api/src/main/java/org/zalando/nakadi/client/java/EventTypeCategory.java new file mode 100644 index 0000000..acde559 --- /dev/null +++ b/api/src/main/java/org/zalando/nakadi/client/java/EventTypeCategory.java @@ -0,0 +1,34 @@ +package org.zalando.nakadi.client.java; + +import org.zalando.nakadi.client.model.EventTypeCategory; + +import com.fasterxml.jackson.annotation.JsonValue; + +import scala.Option; + +/** + * Defines the category of an EventType.
      + */ +public enum EventTypeCategory { + UNDEFINED("undefined"), // + DATA("data"), // + BUSINESS("business"); + + private final String category; + + private EventTypeCategory(String category) { + this.category = category; + } + @JsonValue + public String getCategory() { + return category; + } + + public static Option withName(String code){ + for(EventTypeCategory e : EventTypeCategory.values()){ + if (e.category.equals(code)) + return Option.apply(e); + } + return Option.empty(); + } +} diff --git a/api/src/main/java/org/zalando/nakadi/client/java/EventValidationStrategy.java b/api/src/main/java/org/zalando/nakadi/client/java/EventValidationStrategy.java new file mode 100644 index 0000000..f1c3a52 --- /dev/null +++ b/api/src/main/java/org/zalando/nakadi/client/java/EventValidationStrategy.java @@ -0,0 +1,33 @@ +package org.zalando.nakadi.client.java; + +import org.zalando.nakadi.client.model.EventValidationStrategy; + +import com.fasterxml.jackson.annotation.JsonValue; + +import scala.Option; + +/** + * Defines a rule for validation of an incoming Event. Rules might require + * additional parameters; see the `doc` field of the existing rules for details. + * See GET /registry/validation-strategies for a list of available rules. + */ +public enum EventValidationStrategy { + NONE("None"); + private final String strategy; + + private EventValidationStrategy(String strategy) { + this.strategy = strategy; + } + @JsonValue + public String getStrategy() { + return strategy; + } + + public static Option withName(String code) { + for (EventValidationStrategy e : EventValidationStrategy.values()) { + if (e.strategy.equals(code)) + return Option.apply(e); + } + return Option.empty(); + } +} diff --git a/api/src/main/java/org/zalando/nakadi/client/java/Metrics.java b/api/src/main/java/org/zalando/nakadi/client/java/Metrics.java new file mode 100644 index 0000000..10f48ba --- /dev/null +++ b/api/src/main/java/org/zalando/nakadi/client/java/Metrics.java @@ -0,0 +1,5 @@ +package org.zalando.nakadi.client.java; + +public class Metrics { + +} diff --git a/api/src/main/java/org/zalando/nakadi/client/java/PartitionStrategy.java b/api/src/main/java/org/zalando/nakadi/client/java/PartitionStrategy.java new file mode 100644 index 0000000..d39f4fa --- /dev/null +++ b/api/src/main/java/org/zalando/nakadi/client/java/PartitionStrategy.java @@ -0,0 +1,38 @@ +package org.zalando.nakadi.client.java; + +import org.zalando.nakadi.client.model.PartitionStrategy; + +import com.fasterxml.jackson.annotation.JsonValue; + +import scala.Option; + +/** + * Defines a rule for the resolution of incoming Events into partitions. Rules + * might require additional parameters; see the `doc` field of the existing + * rules for details. See `GET /registry/partition-strategies` for a list of + * available rules. + */ + +public enum PartitionStrategy { + HASH("hash"), // + USER_DEFINED("user_defined"), // + RANDOM("random"); + + private final String strategy; + + private PartitionStrategy(String strategy) { + this.strategy = strategy; + } + @JsonValue + public String getStrategy() { + return strategy; + } + + public static Option withName(String code){ + for(PartitionStrategy e : PartitionStrategy.values()){ + if (e.strategy.equals(code)) + return Option.apply(e); + } + return Option.empty(); + } +} diff --git a/api/src/main/java/org/zalando/nakadi/client/java/SchemaType.java b/api/src/main/java/org/zalando/nakadi/client/java/SchemaType.java new file mode 100644 index 0000000..134d4cf --- /dev/null +++ b/api/src/main/java/org/zalando/nakadi/client/java/SchemaType.java @@ -0,0 +1,30 @@ +package org.zalando.nakadi.client.java; + +import org.zalando.nakadi.client.model.SchemaType; + +import scala.Option; + +import com.fasterxml.jackson.annotation.JsonValue; + +public enum SchemaType { + JSON("json_schema"); + + private final String schema; + + private SchemaType(String schema) { + this.schema = schema; + } + @JsonValue + public String getSchema() { + return schema; + } + + public static Option withName(String code) { + for (SchemaType e : SchemaType.values()) { + if (e.schema.equals(code)) + return Option.apply(e); + } + return Option.empty(); + } + +} diff --git a/api/src/main/scala/org/zalando/nakadi/client/Listener.scala b/api/src/main/scala/org/zalando/nakadi/client/Listener.scala index 2f2d3fb..adca3a3 100644 --- a/api/src/main/scala/org/zalando/nakadi/client/Listener.scala +++ b/api/src/main/scala/org/zalando/nakadi/client/Listener.scala @@ -1,6 +1,6 @@ package org.zalando.nakadi.client -import org.zalando.nakadi.client.model.Cursor +import org.zalando.nakadi.client.scala.Cursor case class ClientError(msg: String, status: Option[Int]) @@ -18,6 +18,6 @@ trait Listener[T] { def id: String def onSubscribed(): Unit def onUnsubscribed(): Unit - def onReceive(sourceUrl: String, cursor: Cursor, event: T): Unit + def onReceive(sourceUrl: String, cursor: Cursor, event: Seq[T]): Unit def onError(sourceUrl: String, cursor: Cursor, error: ClientError): Unit } \ No newline at end of file diff --git a/api/src/main/scala/org/zalando/nakadi/client/scala/Client.scala b/api/src/main/scala/org/zalando/nakadi/client/scala/Client.scala index 900ccba..c0e26dc 100644 --- a/api/src/main/scala/org/zalando/nakadi/client/scala/Client.scala +++ b/api/src/main/scala/org/zalando/nakadi/client/scala/Client.scala @@ -7,12 +7,6 @@ import org.zalando.nakadi.client.Listener import org.zalando.nakadi.client.Deserializer import org.zalando.nakadi.client.Serializer import org.zalando.nakadi.client.StreamParameters -import org.zalando.nakadi.client.model.EventEnrichmentStrategy -import org.zalando.nakadi.client.model.EventType -import org.zalando.nakadi.client.model.EventValidationStrategy -import org.zalando.nakadi.client.model.Metrics -import org.zalando.nakadi.client.model.Partition -import org.zalando.nakadi.client.model.PartitionStrategy @@ -26,7 +20,7 @@ trait Client { * curl --request GET /metrics * }}} */ - def getMetrics()(implicit ser: Deserializer[Metrics]): Future[Either[ClientError, Option[Metrics]]] + def getMetrics()(implicit des: Deserializer[Metrics]): Future[Either[ClientError, Option[Metrics]]] /** * Returns a list of all registered EventTypes. @@ -36,7 +30,7 @@ trait Client { * }}} * */ - def getEventTypes()(implicit ser: Deserializer[Seq[EventType]]): Future[Either[ClientError, Option[Seq[EventType]]]] + def getEventTypes()(implicit des: Deserializer[Seq[EventType]]): Future[Either[ClientError, Option[Seq[EventType]]]] /** * Creates a new EventType. @@ -55,18 +49,18 @@ trait Client { * {{{ * curl --request GET /event-types/{name} * }}} - * @param name - Name of the EventType + * @param eventTypeName - Name of the EventType */ - def getEventType(name: String)(implicit ser: Deserializer[EventType]): Future[Either[ClientError, Option[EventType]]] + def getEventType(eventTypeName: String)(implicit des: Deserializer[EventType]): Future[Either[ClientError, Option[EventType]]] /** * Updates the EventType identified by its name. * {{{ * curl --request PUT -d @fileWithEventType /event-types/{name} * }}} - * @param name - Name of the EventType + * @param eventTypeName - Name of the EventType * @param event - Event to update */ - def updateEventType(name: String, eventType: EventType)(implicit ser: Serializer[EventType]): Future[Option[ClientError]] + def updateEventType(eventTypeName: String, eventType: EventType)(implicit ser: Serializer[EventType]): Future[Option[ClientError]] /** * Deletes an EventType identified by its name. * @@ -74,49 +68,49 @@ trait Client { * curl --request DELETE /event-types/{name} * }}} * - * @param name - Name of the EventType + * @param eventTypeName - Name of the EventType */ - def deleteEventType(name: String): Future[Option[ClientError]] + def deleteEventType(eventTypeName: String): Future[Option[ClientError]] /** - * Creates a new batch of Events for the given EventType. + * Publishes multiple Events for the given EventType. * {{{ * curl --request POST -d @fileWithEvent /event-types/{name}/events * }}} - * @param name - Name of the EventType - * @param event - Event to create + * @param eventTypeName - Name of the EventType + * @param event - Event to publish */ - def publishEvents[T](name: String, events: Seq[T])(implicit ser: Serializer[Seq[T]]): Future[Option[ClientError]] + def publishEvents[T <: Event](eventTypeName: String, events: Seq[T])(implicit ser: Serializer[Seq[T]]): Future[Option[ClientError]] - /** - * Request a stream delivery for the specified partitions of the given EventType. + /** + * Publishes multiple Events for the given EventType. * {{{ - * curl --request GET /event-types/{name}/events + * curl --request POST -d @fileWithEvent /event-types/{name}/events * }}} - * @param name - Name of the EventType - * + * @param eventTypeName - Name of the EventType + * @param event - Event to publish */ - def publishEvents[T](name: String, params: Option[StreamParameters])(implicit ser: Deserializer[T]): Future[Either[ClientError, Option[T]]] + def publishEvents[T <: Event](eventTypeName: String, events: java.util.List[T])(implicit ser: Serializer[T]): Future[Option[ClientError]] /** - * List the partitions for the given EventType. + * Publishes a single Events for the given EventType. * {{{ - * curl --request GET /event-types/{name}/partitions + * curl --request POST -d @fileWithEvent /event-types/{name}/events * }}} - * @param name - Name of the EventType + * @param eventTypeName - Name of the EventType + * @param event - Event to publish + * */ - def getPartitions(name: String)(implicit ser: Deserializer[Seq[Partition]]): Future[Either[ClientError, Option[Seq[Partition]]]] + def publishEvent[T <: Event](eventTypeName: String, event: T)(implicit ser: Serializer[T]): Future[Option[ClientError]] /** - * Returns the Partition for the given EventType. + * List the partitions for the given EventType. * {{{ - * curl --request GET /event-types/{name}/partitions/{partition} + * curl --request GET /event-types/{name}/partitions * }}} - * @param name - Name of the EventType - * @param partition - Partition id for the given EventType + * @param eventTypeName - Name of the EventType */ - - def getPartitionById(name: String, id: String)(implicit ser: Deserializer[Partition]): Future[Either[ClientError, Option[Partition]]] + def getPartitions(eventTypeName: String)(implicit des: Deserializer[Seq[Partition]]): Future[Either[ClientError, Option[Seq[Partition]]]] /** * Returns all of the validation strategies supported by this installation of Nakadi. @@ -142,7 +136,7 @@ trait Client { * curl --request GET /registry/partitioning-strategies * }}} */ - def getPartitionStrategies()(implicit des: Deserializer[Seq[PartitionStrategy.Value]]): Future[Either[ClientError, Option[Seq[PartitionStrategy.Value]]]] + def getPartitioningStrategies()(implicit des: Deserializer[Seq[PartitionStrategy.Value]]): Future[Either[ClientError, Option[Seq[PartitionStrategy.Value]]]] /** * Shuts down the communication system of the client @@ -157,14 +151,14 @@ trait Client { * @parameters - Parameters for the streaming of events. * @listener - Listener to pass the event to when it is received. */ - def subscribe[T](eventType: String, parameters: StreamParameters, listener: Listener[T])(implicit ser: Deserializer[T]): Future[Option[ClientError]] + def subscribe[T](eventTypeName: String, parameters: StreamParameters, listener: Listener[T])(implicit des: Deserializer[T]): Future[Option[ClientError]] /** * Removes the subscription of a listener, to stop streaming events from a partition. * * @eventType - Name of the EventType. * @listener - Listener to unsubscribe from the streaming events. */ - def unsubscribe[T](eventType: String, listener: Listener[T]): Future[Option[ClientError]] + def unsubscribe[T](eventTypeName: String, listener: Listener[T]): Future[Option[ClientError]] } diff --git a/model/src/main/scala/org/zalando/nakadi/client/model/Model.scala b/api/src/main/scala/org/zalando/nakadi/client/scala/Model.scala similarity index 98% rename from model/src/main/scala/org/zalando/nakadi/client/model/Model.scala rename to api/src/main/scala/org/zalando/nakadi/client/scala/Model.scala index 4ee8e83..48bb70c 100644 --- a/model/src/main/scala/org/zalando/nakadi/client/model/Model.scala +++ b/api/src/main/scala/org/zalando/nakadi/client/scala/Model.scala @@ -1,9 +1,13 @@ -package org.zalando.nakadi.client.model +package org.zalando.nakadi.client.scala -import com.fasterxml.jackson.core.`type`.TypeReference -import com.fasterxml.jackson.module.scala.JsonScalaEnumeration import com.fasterxml.jackson.annotation.JsonProperty -import com.fasterxml.jackson.databind.annotation.JsonDeserialize +import scala.collection.JavaConversions._ +import com.fasterxml.jackson.module.scala.JsonScalaEnumeration +import com.fasterxml.jackson.core.`type`.TypeReference + +// Updated untill commit 7839be3 +// Compare + // Updated untill commit 7839be3 // Compare @@ -57,7 +61,7 @@ trait BusinessEvent extends Event { /** * Indicators of a `DataChangeEvent`'s referred data type and the type of operations done on them. * @param dataType The datatype of the `DataChangeEvent`. - * @param dataOp The type of operation executed on the entity. * C: Creation * U: Update * D: Deletion * S: Snapshot + * @param dataOperation The type of operation executed on the entity. * C: Creation * U: Update * D: Deletion * S: Snapshot */ trait DataChangeEventQualifier { def dataType(): String diff --git a/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java b/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java index 4c1f29e..ecec401 100644 --- a/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java +++ b/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java @@ -3,149 +3,134 @@ import java.util.List; import java.util.Optional; import java.util.concurrent.Future; -import java.util.function.Supplier; +import scala.collection.JavaConverters; + +import org.zalando.nakadi.client.Deserializer; +import org.zalando.nakadi.client.Listener; import org.zalando.nakadi.client.Serializer; -import org.zalando.nakadi.client.model.Event; -import org.zalando.nakadi.client.model.EventEnrichmentStrategy; -import org.zalando.nakadi.client.model.EventType; -import org.zalando.nakadi.client.model.EventValidationStrategy; -import org.zalando.nakadi.client.model.Metrics; -import org.zalando.nakadi.client.model.Partition; -import org.zalando.nakadi.client.model.PartitionStrategy; -import org.zalando.nakadi.client.scala.ClientBuilder; +import org.zalando.nakadi.client.StreamParameters; +import org.zalando.nakadi.client.java.model.Event; +import org.zalando.nakadi.client.java.model.EventEnrichmentStrategy; +import org.zalando.nakadi.client.java.model.EventType; +import org.zalando.nakadi.client.java.model.EventValidationStrategy; +import org.zalando.nakadi.client.java.model.Metrics; +import org.zalando.nakadi.client.java.model.Partition; +import org.zalando.nakadi.client.java.model.PartitionStrategy; import org.zalando.nakadi.client.utils.FutureConversions; import org.zalando.nakadi.client.utils.Serialization; -import com.fasterxml.jackson.core.type.TypeReference; +import scala.collection.Seq; + public class ClientImpl implements Client { private final org.zalando.nakadi.client.scala.Client client; + //Deserializers + private final Deserializer metricsDeserializer =Serialization.metricsDeserializer(); + private final Deserializer partitionDeserializer = Serialization.partitionDeserializer(); + //Seq Deserializers + private final Deserializer> seqOfEventTypeDeserializer =Serialization.seqOfEventTypeDeserializer(); + private final Deserializer> seqOfPartitionDeserializer =Serialization.seqOfPartitionDeserializer(); + private final Deserializer> seqOfEventValidationStrategy =Serialization.seqOfEventValidationStrategy(); + private final Deserializer> seqOfEventEnrichmentStrategy =Serialization.seqOfEventEnrichmentStrategy(); + private final Deserializer> seqOfPartitionStrategy =Serialization.seqOfPartitionStrategy(); + //Serializers + private final Serializer eventTypeSerializer =Serialization.defaultSerializer(); + private final Deserializer eventTypeDeserializer = Serialization.eventTypeDeserializer(); public ClientImpl(org.zalando.nakadi.client.scala.Client client) { this.client = client; } - public ClientImpl(String host, // - Optional port, // - Optional> provider, // - Optional securedConnection, // - Optional verifySSlCertificate) { - - ClientBuilder builder = new ClientBuilder().withHost(host); - if (port.isPresent()) - builder = builder.withPort(port.get()); - if (provider.isPresent()) - builder = builder.withTokenProvider4Java(provider.get()); - if (securedConnection.isPresent()) - builder = builder.withSecuredConnection(securedConnection.get()); - if (verifySSlCertificate.isPresent()) - builder = builder.withVerifiedSslCertificate(verifySSlCertificate - .get()); - - client = builder.build(); - } + @Override public Future> getMetrics() { - return FutureConversions.fromOptionOfEither2Optional(client.getMetrics(Serialization.metricsDeserializer())); + return FutureConversions.fromOptionOfEither2Optional(client.getMetrics(metricsDeserializer)); } @Override public Future>> getEventTypes() { - return FutureConversions.fromSeqOfOptionalEither2OptionalList(client.getEventTypes(Serialization.seqOfEventTypeDeserializer())); + return FutureConversions.fromSeqOfOptionalEither2OptionalList(client.getEventTypes(seqOfEventTypeDeserializer)); } @Override public Future createEventType(EventType eventType) { - // TODO Auto-generated method stub - return null; + + return FutureConversions.fromOptional2Future(client.createEventType(eventType,eventTypeSerializer)); } @Override public Future> getEventType(String eventTypeName) { - // TODO Auto-generated method stub - return null; + return FutureConversions.fromOptionOfEither2Optional(client.getEventType(eventTypeName,eventTypeDeserializer)); } @Override - public Future updateEventType(String eventTypeName, - EventType eventType) { - // TODO Auto-generated method stub - return null; + public Future updateEventType(String eventTypeName,EventType eventType) { + return FutureConversions.fromOptional2Future(client.updateEventType(eventTypeName,eventType,eventTypeSerializer)); } @Override public Future deleteEventType(String eventTypeName) { - // TODO Auto-generated method stub - return null; + return FutureConversions.fromOptional2Future(client.deleteEventType(eventTypeName)); } @Override - public Future publishEvent(String eventTypeName, - T event, Serializer serializer) { - // TODO Auto-generated method stub - return null; + public Future publishEvent(String eventTypeName, T event, Serializer serializer) { + return FutureConversions.fromOptional2Future(client.publishEvent(eventTypeName,event,serializer)); } @Override - public Future publishEvent(String eventTypeName, - T event, TypeReference ref) { - // TODO Auto-generated method stub - return null; + public Future publishEvent(String eventTypeName, T event) { + Serializer serializer =Serialization.defaultSerializer(); + return FutureConversions.fromOptional2Future(client.publishEvent(eventTypeName,event,serializer)); } @Override - public Future publishEvents(String eventTypeName, - List events, Serializer> serializer) { - // TODO Auto-generated method stub - return null; + public Future publishEvents(String eventTypeName, List events, Serializer serializer) { + return FutureConversions.fromOptional2Future(client.publishEvents(eventTypeName,events,serializer)); } @Override - public Future publishEvents(String eventTypeName, - List events, TypeReference> ref) { - // TODO Auto-generated method stub - return null; + public Future publishEvents(String eventTypeName, List events) { + Serializer serializer =Serialization.defaultSerializer(); + return FutureConversions.fromOptional2Future(client.publishEvents(eventTypeName,events,serializer)); } @Override public Future>> getPartitions(String eventTypeName) { - // TODO Auto-generated method stub - return null; - } - - @Override - public Future> getPartitionById(String eventTypeName, - String id) { - // TODO Auto-generated method stub - return null; + return FutureConversions.fromSeqOfOptionalEither2OptionalList(client.getPartitions(eventTypeName,seqOfPartitionDeserializer)); } @Override public Future>> getValidationStrategies() { - // TODO Auto-generated method stub - return null; + return FutureConversions.fromSeqOfOptionalEither2OptionalList(client.getValidationStrategies(seqOfEventValidationStrategy)); } @Override public Future>> getEnrichmentStrategies() { - // TODO Auto-generated method stub - return null; + return FutureConversions.fromSeqOfOptionalEither2OptionalList(client.getEnrichmentStrategies(seqOfEventEnrichmentStrategy)); } @Override - public Future>> getPartitionStrategies() { - // TODO Auto-generated method stub - return null; + public Future>> getPartitioningStrategies() { + return FutureConversions.fromSeqOfOptionalEither2OptionalList(client.getPartitioningStrategies(seqOfPartitionStrategy)); } @Override public Future stop() { - // TODO Auto-generated method stub - return null; + return FutureConversions.fromOptional2Future(client.stop()); + } + @Override + public Future subscribe(String eventTypeName, StreamParameters parameters, Listener listener, Deserializer deserializer) { + return FutureConversions.fromOptional2Future(client.subscribe(eventTypeName,parameters,listener,deserializer)); + } + + @Override + public Future unsubscribe(String eventTypeName, Listener listener) { + return FutureConversions.fromOptional2Future(client.unsubscribe(eventTypeName,listener)); } } \ No newline at end of file diff --git a/client/src/main/scala/org/zalando/nakadi/client/actor/EventConsumingActor.scala b/client/src/main/scala/org/zalando/nakadi/client/actor/EventConsumingActor.scala index 0e43cae..d4298ff 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/actor/EventConsumingActor.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/actor/EventConsumingActor.scala @@ -13,16 +13,21 @@ import scala.util.Try import scala.util.Success import scala.util.Failure import org.zalando.nakadi.client.ClientError +import org.zalando.nakadi.client.model.JacksonJsonMarshaller +import org.zalando.nakadi.client.model.EventStreamBatch +import org.zalando.nakadi.client.model.Event +import com.fasterxml.jackson.core.`type`.TypeReference object EventConsumer { - case class Msg( - msg: ByteString) + case class Msg(msg: ByteString) case class ShutdownMsg() } -class EventConsumer[T](url: String, eventType: String, listener: Listener[T], ser: Deserializer[T]) extends Actor with ActorLogging with ActorSubscriber { +case class MyEventExample(orderNumber: String) extends Event + +class EventConsumer[T](url: String, listener: Listener[T], des: Deserializer[T]) extends Actor with ActorLogging with ActorSubscriber { import EventConsumer._ var count = 0 @@ -35,16 +40,16 @@ class EventConsumer[T](url: String, eventType: String, listener: Listener[T], se override def receive: Receive = { case OnNext(msg: ByteString) => val message = msg.utf8String - if (message.contains("events")){ - + + if (message.contains("events")) { count += 1 - println(s"[Got event nr $count for type $eventType and cursor * and message $message ] ") - // Try(ser.fromJson(msg.utf8String)) match { - // case Success(event) => - // listener.onReceive(eventType, cursor, event) - // case Failure(error) => - // val errorMsg = "Failed to Deserialize with error:" + error.getMessage - listener.onError(eventType, null, ClientError("Failed to Deserialize with an error!", None)) + log.info("[Got event nr {} for {} and with msg {} ] ", count, url, message) + // Try(ser.fromJson(msg.utf8String)) match { + // case Success(event) => + // listener.onReceive(eventType, cursor, event) + // case Failure(error) => + // val errorMsg = "Failed to Deserialize with error:" + error.getMessage + // listener.onError(url, null, ClientError("Failed to Deserialize with an error!", None)) } // } case OnNext(_) => @@ -52,4 +57,10 @@ class EventConsumer[T](url: String, eventType: String, listener: Listener[T], se } } +trait MessageSplitter { + + def deserializeMsg[T<:Event](msg: String)(implicit des: Deserializer[EventStreamBatch[T]]): EventStreamBatch[T] = des.from(msg) +} + + diff --git a/client/src/main/scala/org/zalando/nakadi/client/actor/EventReceivingActor.scala b/client/src/main/scala/org/zalando/nakadi/client/actor/EventReceivingActor.scala new file mode 100644 index 0000000..3af08b8 --- /dev/null +++ b/client/src/main/scala/org/zalando/nakadi/client/actor/EventReceivingActor.scala @@ -0,0 +1,33 @@ +package org.zalando.nakadi.client.actor + +import akka.actor.ActorLogging +import akka.actor.Actor +import akka.stream.actor.ActorPublisher +import EventReceivingActor._ +import scala.collection.mutable.Queue +import org.zalando.nakadi.client.model.Cursor + + + + +object EventReceivingActor { + case class NextEvent(lastReceivedCursor:Cursor) +} + + + +//class EventReceivingActor extends Actor with ActorLogging with ActorPublisher[NextEvent] { +// val queue: Queue[NextEvent] = Queue() +// val receivedOffset: Set[Integer] = Set() +// +// override def receive: Receive = { +// case msg: NextEvent => +// if (!visited(msg.lastReceivedCursor.offset.get)) { +// visited += url.url +// queue.enqueue(url) +// if (isActive && totalDemand > 0) { +// onNext(queue.dequeue()) +// } +// } +// } +//} \ No newline at end of file diff --git a/client/src/main/scala/org/zalando/nakadi/client/model/JacksonJsonMarshaller.scala b/client/src/main/scala/org/zalando/nakadi/client/model/JacksonJsonMarshaller.scala index 9611c8f..b7cf18c 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/model/JacksonJsonMarshaller.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/model/JacksonJsonMarshaller.scala @@ -3,9 +3,11 @@ package org.zalando.nakadi.client.model import scala.reflect.runtime.universe import scala.reflect.runtime.universe import scala.reflect.runtime.universe._ + import org.slf4j.LoggerFactory import org.zalando.nakadi.client.Deserializer import org.zalando.nakadi.client.Serializer + import com.fasterxml.jackson.annotation.JsonInclude import com.fasterxml.jackson.core.JsonFactory import com.fasterxml.jackson.core.JsonParser @@ -17,11 +19,8 @@ import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.databind.PropertyNamingStrategy import com.fasterxml.jackson.databind.SerializationFeature import com.fasterxml.jackson.databind.deser.DeserializationProblemHandler -import com.fasterxml.jackson.databind.deser.std.ObjectArrayDeserializer import com.fasterxml.jackson.module.scala.DefaultScalaModule import com.typesafe.scalalogging.Logger -import com.fasterxml.jackson.databind.`type`.ArrayType -import com.fasterxml.jackson.databind.jsontype.TypeDeserializer object JacksonJsonMarshaller { val logger = Logger(LoggerFactory.getLogger(this.getClass)) @@ -33,13 +32,14 @@ object JacksonJsonMarshaller { implicit def partitionTR: TypeReference[Partition] = new TypeReference[Partition] {} implicit def cursorTR: TypeReference[Cursor] = new TypeReference[Cursor] {} implicit def eventTypeSchemaTR: TypeReference[EventTypeSchema] = new TypeReference[EventTypeSchema] {} - implicit def eventValidationStrategyTR: TypeReference[EventValidationStrategy.Value] = new TypeReference[EventValidationStrategy.Value] {} - implicit def partitionResolutionStrategyTR: TypeReference[PartitionStrategy.Value] = new TypeReference[PartitionStrategy.Value] {} - implicit def eventEnrichmentStrategyTR: TypeReference[EventEnrichmentStrategy.Value] = new TypeReference[EventEnrichmentStrategy.Value] {} + implicit def eventValidationStrategyTR: TypeReference[EventValidationStrategy] = new TypeReference[EventValidationStrategy] {} + implicit def partitionResolutionStrategyTR: TypeReference[PartitionStrategy] = new TypeReference[PartitionStrategy] {} + implicit def eventEnrichmentStrategyTR: TypeReference[EventEnrichmentStrategy] = new TypeReference[EventEnrichmentStrategy] {} implicit def dataChangeEventQualifierTR: TypeReference[DataChangeEventQualifier] = new TypeReference[DataChangeEventQualifier] {} implicit def eventTypeStatisticsTR: TypeReference[EventTypeStatistics] = new TypeReference[EventTypeStatistics] {} implicit def eventTypeTR: TypeReference[EventType] = new TypeReference[EventType] {} implicit def eventTR: TypeReference[Event] = new TypeReference[Event] {} + implicit def eventStreamBatchTR: TypeReference[EventStreamBatch[_]] = new TypeReference[EventStreamBatch[_]] {} implicit def eventMetadataTR: TypeReference[EventMetadata] = new TypeReference[EventMetadata] {} implicit def businessEventTR: TypeReference[BusinessEvent] = new TypeReference[BusinessEvent] {} @@ -47,9 +47,9 @@ object JacksonJsonMarshaller { implicit def dataChangeEventTR: TypeReference[DataChangeEvent[Any]] = new TypeReference[DataChangeEvent[Any]] {} //Lists - implicit def listOfPartitionResolutionStrategyTR: TypeReference[Seq[PartitionStrategy.Value]] = new TypeReference[Seq[PartitionStrategy.Value]] {} - implicit def listOfEventValidationStrategyTR: TypeReference[Seq[EventValidationStrategy.Value]] = new TypeReference[Seq[EventValidationStrategy.Value]] {} - implicit def listOfEventEnrichmentStrategyTR: TypeReference[Seq[EventEnrichmentStrategy.Value]] = new TypeReference[Seq[EventEnrichmentStrategy.Value]] {} + implicit def listOfPartitionStrategyTR: TypeReference[Seq[PartitionStrategy]] = new TypeReference[Seq[PartitionStrategy]] {} + implicit def listOfEventValidationStrategyTR: TypeReference[Seq[EventValidationStrategy]] = new TypeReference[Seq[EventValidationStrategy]] {} + implicit def listOfEventEnrichmentStrategyTR: TypeReference[Seq[EventEnrichmentStrategy]] = new TypeReference[Seq[EventEnrichmentStrategy]] {} implicit def listOfEventTypeTR: TypeReference[Seq[EventType]] = new TypeReference[Seq[EventType]] {} implicit def listOfPartitionTR: TypeReference[Seq[Partition]] = new TypeReference[Seq[Partition]] {} diff --git a/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala index 96cddac..7226670 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala @@ -5,7 +5,6 @@ import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.Future import scala.concurrent.duration.DurationInt import org.slf4j.LoggerFactory -import org.zalando.nakadi.client.model._ import com.typesafe.scalalogging.Logger import akka.actor.Terminated import akka.http.scaladsl.model.{ HttpHeader, HttpMethod, HttpMethods, HttpResponse, MediaRange } @@ -15,23 +14,25 @@ import akka.http.scaladsl.model.headers.{ Accept, RawHeader } import akka.http.scaladsl.unmarshalling.Unmarshal import org.zalando.nakadi.client.StreamParameters import org.zalando.nakadi.client.Serializer +import org.zalando.nakadi.client.scala._ import org.zalando.nakadi.client.Deserializer import org.zalando.nakadi.client.Listener import org.zalando.nakadi.client.ClientError import akka.dispatch.sysmsg.Failed import scala.concurrent.Await import scala.concurrent.duration.Duration +import org.zalando.nakadi.client.model.JacksonJsonMarshaller private[client] class ClientImpl(connection: Connection, charSet: String = "UTF-8") extends Client with HttpFactory { implicit val materializer = connection.materializer val logger = Logger(LoggerFactory.getLogger(this.getClass)) - def getMetrics()(implicit ser: Deserializer[Metrics]): Future[Either[ClientError, Option[Metrics]]] = { + def getMetrics()(implicit des: Deserializer[Metrics]): Future[Either[ClientError, Option[Metrics]]] = { logFutureEither(connection.get(URI_METRICS).flatMap(mapToEither(_))) } - def getEventTypes()(implicit ser: Deserializer[Seq[EventType]]): Future[Either[ClientError, Option[Seq[EventType]]]] = { + def getEventTypes()(implicit des: Deserializer[Seq[EventType]]): Future[Either[ClientError, Option[Seq[EventType]]]] = { logFutureEither(connection.get(URI_EVENT_TYPES).flatMap(mapToEither(_))) } @@ -39,7 +40,7 @@ private[client] class ClientImpl(connection: Connection, charSet: String = "UTF- logFutureOption(connection.post(URI_EVENT_TYPES, eventType).flatMap(mapToOption(_))) } - def getEventType(name: String)(implicit ser: Deserializer[EventType]): Future[Either[ClientError, Option[EventType]]] = { + def getEventType(name: String)(implicit des: Deserializer[EventType]): Future[Either[ClientError, Option[EventType]]] = { logFutureEither(connection.get(URI_EVENT_TYPE_BY_NAME.format(name)).flatMap(in => mapToEither(in))) } @@ -52,32 +53,32 @@ private[client] class ClientImpl(connection: Connection, charSet: String = "UTF- logFutureOption(connection.delete(URI_EVENT_TYPE_BY_NAME.format(name)).flatMap(in => mapToOption(in))) } - def publishEvents[T](name: String, events: Seq[T])(implicit ser: Serializer[Seq[T]]): Future[Option[ClientError]] = { - logFutureOption(connection.post(URI_EVENTS_OF_EVENT_TYPE.format(name), events).flatMap(in => mapToOption(in))) + def publishEvents[T <: Event](eventTypeName: String, events: Seq[T])(implicit ser: Serializer[Seq[T]]): Future[Option[ClientError]] = { + logFutureOption(connection.post(URI_EVENTS_OF_EVENT_TYPE.format(eventTypeName), events).flatMap(in => mapToOption(in))) } - - def publishEvents[T](name: String, params: Option[StreamParameters])(implicit ser: Deserializer[T]): Future[Either[ClientError, Option[T]]] = { - val headers = withHeaders(params) :+ Accept(MediaRange(`application/json`)) - logFutureEither(connection.get(URI_EVENTS_OF_EVENT_TYPE.format(name), headers).flatMap(in => mapToEither(in))) + def publishEvents[T <: Event](eventTypeName: String, events: java.util.List[T])(implicit ser: Serializer[T]): Future[Option[ClientError]] = { + import scala.collection.JavaConversions._ + import JacksonJsonMarshaller._ + publishEvents(eventTypeName, events) } - def getPartitions(name: String)(implicit ser: Deserializer[Seq[Partition]]): Future[Either[ClientError, Option[Seq[Partition]]]] = { - logFutureEither(connection.get(URI_PARTITIONS_BY_EVENT_TYPE.format(name)).flatMap(in => mapToEither(in))) + def publishEvent[T <: Event](name: String, event: T)(implicit ser: Serializer[T]): Future[Option[ClientError]] = { + logFutureOption(connection.post(URI_EVENTS_OF_EVENT_TYPE.format(name), event).flatMap(in => mapToOption(in))) } - def getPartitionById(name: String, id: String)(implicit ser: Deserializer[Partition]): Future[Either[ClientError, Option[Partition]]] = { - logFutureEither(connection.get(URI_PARTITION_BY_EVENT_TYPE_AND_ID.format(name)).flatMap(in => mapToEither(in))) + def getPartitions(name: String)(implicit des: Deserializer[Seq[Partition]]): Future[Either[ClientError, Option[Seq[Partition]]]] = { + logFutureEither(connection.get(URI_PARTITIONS_BY_EVENT_TYPE.format(name)).flatMap(in => mapToEither(in))) } - def getValidationStrategies()(implicit des: Deserializer[Seq[EventValidationStrategy.Value]]): Future[Either[ClientError, Option[Seq[EventValidationStrategy.Value]]]] = { + def getValidationStrategies()(implicit des: Deserializer[Seq[EventValidationStrategy.Value]]): Future[Either[ClientError, Option[Seq[EventValidationStrategy]]]] = { logFutureEither(connection.get(URI_VALIDATION_STRATEGIES).flatMap(mapToEither(_))) } - def getEnrichmentStrategies()(implicit des: Deserializer[Seq[EventEnrichmentStrategy.Value]]): Future[Either[ClientError, Option[Seq[EventEnrichmentStrategy.Value]]]] = { + def getEnrichmentStrategies()(implicit des: Deserializer[Seq[EventEnrichmentStrategy]]): Future[Either[ClientError, Option[Seq[EventEnrichmentStrategy]]]] = { logFutureEither(connection.get(URI_ENRICHMENT_STRATEGIES).flatMap(mapToEither(_))) } - def getPartitionStrategies()(implicit des: Deserializer[Seq[PartitionStrategy.Value]]): Future[Either[ClientError, Option[Seq[PartitionStrategy.Value]]]] = + def getPartitioningStrategies()(implicit des: Deserializer[Seq[PartitionStrategy]]): Future[Either[ClientError, Option[Seq[PartitionStrategy]]]] = logFutureEither(connection.get(URI_PARTITIONING_STRATEGIES).flatMap(mapToEither(_))) def stop(): Future[Option[ClientError]] = { @@ -85,31 +86,31 @@ private[client] class ClientImpl(connection: Connection, charSet: String = "UTF- Future.successful(None) } - def subscribe[T](eventType: String, params: StreamParameters, listener: Listener[T])(implicit ser: Deserializer[T]): Future[Option[ClientError]] = { - //TODO: Validate here before starting listening + def subscribe[T](eventType: String, params: StreamParameters, listener: Listener[T])(implicit des: Deserializer[T]): Future[Option[ClientError]] = { (eventType, params, listener) match { case (_, _, listener) if listener == null => + logger.info("listener is null") Future.successful(Option(ClientError("Listener may not be empty(null)!", None))) case (eventType, _, _) if Option(eventType).isEmpty || eventType == "" => + logger.info("eventType is null") Future.successful(Option(ClientError("Eventype may not be empty(null)!", None))) case (eventType, StreamParameters(cursor, _, _, _, _, _, _), listener) if Option(eventType).isDefined => - val url = if (cursor.isDefined) - URI_PARTITION_BY_EVENT_TYPE_AND_ID.format(eventType, cursor.get.partition) - else - URI_EVENTS_OF_EVENT_TYPE.format(eventType) + val url = URI_EVENTS_OF_EVENT_TYPE.format(eventType) + val request = withHttpRequest(url, HttpMethods.GET, RawHeader("Accept", "application/x-json-stream") :: withHeaders(Option(params)), //Headers - connection.tokenProvider()) + connection.tokenProvider(),Option(params)) - logger.info("Subscribing listener {} on eventType {} with cursor {} ", listener.id, eventType, cursor) - connection.subscribe(url, eventType, request, listener) + logger.debug("Subscribing listener {} - cursor {} - parameters {} - eventType {} ", listener.id, cursor,params, eventType) + connection.subscribe(url, request, listener) Future.successful(None) } } + def unsubscribe[T](eventType: String, listener: Listener[T]): Future[Option[ClientError]] = ??? //#################### diff --git a/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala index 4264190..3e70ebb 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala @@ -1,6 +1,5 @@ package org.zalando.nakadi.client.scala - import java.security.SecureRandom import java.security.cert.X509Certificate import scala.collection.immutable.Seq @@ -24,7 +23,6 @@ import org.zalando.nakadi.client.Deserializer import org.zalando.nakadi.client.Serializer import org.zalando.nakadi.client.Listener - trait Connection extends HttpFactory { //Connection details @@ -39,7 +37,7 @@ trait Connection extends HttpFactory { def delete(endpoint: String): Future[HttpResponse] def post[T](endpoint: String, model: T)(implicit serializer: Serializer[T]): Future[HttpResponse] def put[T](endpoint: String, model: T)(implicit serializer: Serializer[T]): Future[HttpResponse] - def subscribe[T](url: String, eventType: String, request: HttpRequest, listener: Listener[T])(implicit des: Deserializer[T]) + def subscribe[T](url: String, request: HttpRequest, listener: Listener[T])(implicit des: Deserializer[T]) def stop(): Future[Terminated] def materializer(): ActorMaterializer @@ -85,6 +83,7 @@ sealed class ConnectionImpl(val host: String, val port: Int, val tokenProvider: private implicit val http = Http(actorSystem) implicit val materializer = ActorMaterializer() private val actors: Map[String, Actor] = Map() + val logger = Logger(LoggerFactory.getLogger(this.getClass)) @@ -96,21 +95,21 @@ sealed class ConnectionImpl(val host: String, val port: Int, val tokenProvider: } def get(endpoint: String): Future[HttpResponse] = { - logger.info("Get: {}", endpoint) - executeCall(withHttpRequest(endpoint, HttpMethods.GET, Nil, tokenProvider)) + logger.info("Get - URL {}", endpoint) + executeCall(withHttpRequest(endpoint, HttpMethods.GET, Nil, tokenProvider,None)) } def get(endpoint: String, headers: Seq[HttpHeader]): Future[HttpResponse] = { - logger.info("Get: {}", endpoint) - executeCall(withHttpRequest(endpoint, HttpMethods.GET, headers, tokenProvider)) + logger.info("Get - URL {} - Headers {}", endpoint,headers) + executeCall(withHttpRequest(endpoint, HttpMethods.GET, headers, tokenProvider,None)) } def stream(endpoint: String, headers: Seq[HttpHeader]): Future[HttpResponse] = { logger.info("Streaming on Get: {}", endpoint) - executeCall(withHttpRequest(endpoint, HttpMethods.GET, headers, tokenProvider)) //TODO: Change to stream single event + executeCall(withHttpRequest(endpoint, HttpMethods.GET, headers, tokenProvider,None)) //TODO: Change to stream single event } def put[T](endpoint: String, model: T)(implicit serializer: Serializer[T]): Future[HttpResponse] = { logger.info("Get: {}", endpoint) - executeCall(withHttpRequest(endpoint, HttpMethods.GET, Nil, tokenProvider)) + executeCall(withHttpRequest(endpoint, HttpMethods.GET, Nil, tokenProvider,None)) } def post[T](endpoint: String, model: T)(implicit serializer: Serializer[T]): Future[HttpResponse] = { @@ -122,7 +121,7 @@ sealed class ConnectionImpl(val host: String, val port: Int, val tokenProvider: def delete(endpoint: String): Future[HttpResponse] = { logger.info("Delete: {}", endpoint) - executeCall(withHttpRequest(endpoint, HttpMethods.DELETE, Nil, tokenProvider)) + executeCall(withHttpRequest(endpoint, HttpMethods.DELETE, Nil, tokenProvider,None)) } private def executeCall(request: HttpRequest): Future[HttpResponse] = { @@ -142,21 +141,23 @@ sealed class ConnectionImpl(val host: String, val port: Int, val tokenProvider: def stop(): Future[Terminated] = actorSystem.terminate() - def subscribe[T](url: String, eventType: String, request: HttpRequest, listener: Listener[T])(implicit des: Deserializer[T]) = { + def subscribe[T](url: String, request: HttpRequest, listener: Listener[T])(implicit des: Deserializer[T]) = { + logger.info("Subscribing listener {} with request {}", listener.id, request.uri) import EventConsumer._ case class MyEventExample(orderNumber: String) - val subscriberRef = actorSystem.actorOf(Props(classOf[EventConsumer[T]], url, eventType, listener, des)) + val subscriberRef = actorSystem.actorOf(Props(classOf[EventConsumer[T]], url, listener, des)) val subscriber = ActorSubscriber[ByteString](subscriberRef) val sink2 = Sink.fromSubscriber(subscriber) val flow: Flow[HttpRequest, HttpResponse, Future[Http.OutgoingConnection]] = connection val req = Source.single(request) // .via(flow) // - .buffer(100, OverflowStrategy.backpressure) // + .buffer(200, OverflowStrategy.backpressure) // .map(x => x.entity.dataBytes) - .runForeach(_.runWith(sink2)).onComplete { _ => + .runForeach(_.runWith(sink2)) + .onComplete { _ => subscriberRef ! ShutdownMsg - println("Shutting down") + logger.info("Shutting down") } } diff --git a/client/src/main/scala/org/zalando/nakadi/client/scala/HttpFactory.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/HttpFactory.scala index 0d6c1d8..ff6e89c 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/scala/HttpFactory.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/scala/HttpFactory.scala @@ -43,26 +43,34 @@ trait HttpFactory { def withHeaders(params: Option[StreamParameters]): List[HttpHeader] = { params match { case Some(StreamParameters(cursor, _, _, _, _, _, flowId)) => - val c = if (cursor.isDefined) Option("[{partition: " + cursor.get.partition + ", offset: " + cursor.get.offset + "}]") else None - val parameters = List(("X-Nakadi-Cursors", c), ("X-Flow-Id", flowId)) + val nakadiCursor = cursor match { + case Some(value) if Option(value.partition).isDefined && Option(value.offset).isDefined => + ("X-Nakadi-Cursors", Some("[{\"partition\":\"" + value.partition + "\", \"offset\": \"" + value.offset + "\"}]")) + case None => + ("X-Nakadi-Cursors", None) + } + val parameters = List(nakadiCursor, ("X-Flow-Id", flowId)) for { (key, optional) <- parameters; value <- optional } yield RawHeader(key, value) case None => Nil } } - def withQueryParams(params: Option[StreamParameters]): List[(String, Any)] = { + def withQueryParams(params: Option[StreamParameters]): List[String] = { params match { case Some(StreamParameters(_, batchLimit, streamLimit, batchFlushTimeout, streamTimeout, streamKeepAliveLimit, _)) => val parameters = List(("batch_limit", batchLimit), ("stream_limit", streamLimit), // ("batch_flush_timeout", batchFlushTimeout), ("stream_timeout", streamTimeout), // ("stream_keep_alive_limit", streamKeepAliveLimit)) - for { (key, optional) <- parameters; value <- optional } yield (key -> value) + for { (key, optional) <- parameters; value <- optional } yield (key + "=" + value) case None => Nil } } - def withHttpRequest(url: String, httpMethod: HttpMethod, additionalHeaders: Seq[HttpHeader], tokenProvider: TokenProvider): HttpRequest = { - val allHeaders: Seq[HttpHeader] = additionalHeaders :+ headers.Authorization(OAuth2BearerToken(tokenProvider())) - HttpRequest(uri = url, method = httpMethod).withHeaders(allHeaders) + def withHttpRequest(url: String, httpMethod: HttpMethod, additionalHeaders: Seq[HttpHeader], tokenProvider: TokenProvider, params: Option[StreamParameters]): HttpRequest = { + val allHeaders: Seq[HttpHeader] = additionalHeaders :+ headers.Authorization(OAuth2BearerToken(tokenProvider())) + val paramsList =withQueryParams(params) + val urlParams = if (!paramsList.isEmpty) paramsList.mkString("?", "&", "") else "" + val finalUrl = url + urlParams + HttpRequest(uri = finalUrl, method = httpMethod).withHeaders(allHeaders) } def withHttpRequestAndPayload(url: String, entity: String, httpMethod: HttpMethod, tokenProvider: TokenProvider): HttpRequest = { diff --git a/client/src/main/scala/org/zalando/nakadi/client/scala/package.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/package.scala index 9dbe751..cdaeecc 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/scala/package.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/scala/package.scala @@ -11,7 +11,6 @@ package object scala { /*Partitions*/ val URI_PARTITIONS_BY_EVENT_TYPE = "/event-types/%s/partitions" - val URI_PARTITION_BY_EVENT_TYPE_AND_ID = "/event-types/%s/partitions/%s" /*Strategies*/ val URI_VALIDATION_STRATEGIES = "/registry/validation-strategies" diff --git a/client/src/main/scala/org/zalando/nakadi/client/scala/ClientBuilder.scala b/client/src/main/scala/org/zalando/nakadi/client/utils/ClientBuilder.scala similarity index 67% rename from client/src/main/scala/org/zalando/nakadi/client/scala/ClientBuilder.scala rename to client/src/main/scala/org/zalando/nakadi/client/utils/ClientBuilder.scala index b36a923..41ebe7c 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/scala/ClientBuilder.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/utils/ClientBuilder.scala @@ -1,5 +1,8 @@ -package org.zalando.nakadi.client.scala +package org.zalando.nakadi.client.utils +import org.zalando.nakadi.client.scala.Client +import org.zalando.nakadi.client.scala.ClientImpl +import org.zalando.nakadi.client.scala.Connection import java.util.function.Supplier @@ -10,17 +13,17 @@ object ClientBuilder { port: Int = DEFAULT_PORT, tokenProvider: () => String = null, securedConnection: Boolean = true, - verifySSlCertificate: Boolean = true) = { - - } + verifySSlCertificate: Boolean = true) = new ClientBuilder(host, port, tokenProvider, securedConnection, verifySSlCertificate) private val DEFAULT_PORT = 443 } -class ClientBuilder private (host: String = "", - port: Int, tokenProvider: () => String = () => "", - securedConnection: Boolean = true, verifySSlCertificate: Boolean = true) { - def this() = this(null, ClientBuilder.DEFAULT_PORT, null, true, true) +class ClientBuilder private(host: String = "", // + port: Int, // + tokenProvider: () => String = () => "", // + securedConnection: Boolean = true, // + verifySSlCertificate: Boolean = true) { + def this() = this(null, ClientBuilder.DEFAULT_PORT, null, true, true) def withHost(host: String): ClientBuilder = new ClientBuilder( checkNotNull(host), port, @@ -58,9 +61,9 @@ class ClientBuilder private (host: String = "", securedConnection, checkNotNull(verifySSlCertificate)) - def build: Client = new ClientImpl(Connection.newConnection(host, port, tokenProvider, securedConnection, verifySSlCertificate), "UTF-8") - - def buildJavaClient:org.zalando.nakadi.client.java.Client = new org.zalando.nakadi.client.java.ClientImpl(build) + def build(): Client = new ClientImpl(Connection.newConnection(host, port, tokenProvider, securedConnection, verifySSlCertificate), "UTF-8") + + def buildJavaClient(): org.zalando.nakadi.client.java.Client = new org.zalando.nakadi.client.java.ClientImpl(build) private def checkNotNull[T](subject: T): T = if (Option(subject).isEmpty) throw new NullPointerException else subject diff --git a/client/src/main/scala/org/zalando/nakadi/client/utils/FutureConversions.scala b/client/src/main/scala/org/zalando/nakadi/client/utils/FutureConversions.scala index c4e2033..3c903d4 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/utils/FutureConversions.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/utils/FutureConversions.scala @@ -9,17 +9,40 @@ import java.util.Optional object FutureConversions { - private def extract[T](either: Either[String, T]): T = either match { + private def extractEither[T](either: Either[String, T]): T = either match { case Left(error) => throw new RuntimeException(error) case Right(t) => t } + private def extractOption[T >: Null](option: Option[ClientError]): T = option match { + case Some(v) => throw new RuntimeException(v.msg) + case None => null + } + /** + * Transforms the value in the option wrapped in the either right inside a + * future into a java Optional wrapped in a Java future. + * If either left inside the future contains a client error, + * then a RuntimeException is thrown with the error! + */ def fromOptionOfEither2Optional[T](in: scala.concurrent.Future[Either[ClientError, Option[T]]]): java.util.concurrent.Future[Optional[T]] = { new MFuture[Either[ClientError, Option[T]], Optional[T]](in, a => fromRightOptionOfEither2Option(a)) } + /** + * Transforms the sequence in the option wrapped in the either right inside a + * future into a java List wrapped in an Optional wrapped in a Java future. + * If either left inside the future contains a client error, + * then a RuntimeException is thrown with the error! + */ def fromSeqOfOptionalEither2OptionalList[T](in: scala.concurrent.Future[Either[ClientError, Option[Seq[T]]]]): java.util.concurrent.Future[Optional[java.util.List[T]]] = { - new MFuture[Either[ClientError, Option[Seq[T]]], Optional[java.util.List[T]]](in, a => fromSeqOfOptionalEither2OptionalList(a)) + new MFuture[Either[ClientError, Option[Seq[T]]], Optional[java.util.List[T]]](in, a => fromSeqOfOptionalEither2OptionalList(a)) + } + + /** + * Transforms an optional into a Void if it is empty, else RuntimeException is thrown with the error! + */ + def fromOptional2Future(in: scala.concurrent.Future[Option[ClientError]]): java.util.concurrent.Future[Void] = { + new MFuture[Option[ClientError], Void](in, a => extractOption(a)) } private def fromSequenceToList[T](in: Seq[T]): Optional[java.util.List[T]] = in match { @@ -28,25 +51,20 @@ object FutureConversions { } - private def fromRightOptionOfEither2Option[L, R](in: Either[L, Option[R]]): Optional[R] = in match { - case Left(_) => Optional.empty() + private def fromRightOptionOfEither2Option[R](in: Either[ClientError, Option[R]]): Optional[R] = in match { + case Left(e) => throw new RuntimeException(e.msg) case Right(Some(value)) => Optional.of(value) case Right(None) => Optional.empty() } - private def fromSeqOfOptionalEither2OptionalList[L, R](in: Either[L, Option[Seq[R]]]): Optional[java.util.List[R]] = in match { - case Left(_) => Optional.empty() + private def fromSeqOfOptionalEither2OptionalList[R](in: Either[ClientError, Option[Seq[R]]]): Optional[java.util.List[R]] = in match { + case Left(e) => throw new RuntimeException(e.msg) case Right(None) => Optional.empty() case Right(Some(Nil)) => Optional.of(new java.util.ArrayList[R]()) case Right(Some(seq)) => Optional.of(new java.util.ArrayList[R](seq)) } - private def convertOption[A](option: Option[A]): Optional[A] = option match { - case Some(v) => Optional.of(v) - case None => Optional.empty() - } - private def convert[T](x: scala.concurrent.Future[Either[String, T]]): java.util.concurrent.Future[T] = - new MFuture[Either[String, T], T](x, a => extract(a)) + new MFuture[Either[String, T], T](x, a => extractEither(a)) } diff --git a/client/src/main/scala/org/zalando/nakadi/client/utils/GeneralConversions.scala b/client/src/main/scala/org/zalando/nakadi/client/utils/GeneralConversions.scala new file mode 100644 index 0000000..6c694a5 --- /dev/null +++ b/client/src/main/scala/org/zalando/nakadi/client/utils/GeneralConversions.scala @@ -0,0 +1,25 @@ +package org.zalando.nakadi.client.utils + +import java.util.Optional +import scala.collection.JavaConversions._ + +object GeneralConversions { + + // def fromOptinalSequence[T](in:Option[Seq[T]]):Optional[List[T]]={ + // + // } + + def fromOptional[T](in: Option[T]) = in match { + case None => Optional.empty() + case Some(value) => Optional.of(value) + + } + def fromOption[T](in: Option[T]): Optional[T] = in match { + case None => Optional.empty() + case Some(value) => Optional.of(value) + } + def fromOptionOfSeq[T](in: Option[Seq[T]]): Optional[java.util.List[T]] = in match { + case None => Optional.empty() + case Some(seq: Seq[T]) => Optional.of(seqAsJavaList(seq)) + } +} \ No newline at end of file diff --git a/client/src/main/scala/org/zalando/nakadi/client/utils/ImplicitHelper.scala b/client/src/main/scala/org/zalando/nakadi/client/utils/ImplicitHelper.scala index e8c3f60..4f8b362 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/utils/ImplicitHelper.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/utils/ImplicitHelper.scala @@ -5,13 +5,29 @@ import org.zalando.nakadi.client.model.Metrics import org.zalando.nakadi.client.model.JacksonJsonMarshaller import org.zalando.nakadi.client.model.EventType import org.zalando.nakadi.client.Serializer +import com.fasterxml.jackson.core.`type`.TypeReference +import org.zalando.nakadi.client.model.Partition +import org.zalando.nakadi.client.model.EventValidationStrategy +import org.zalando.nakadi.client.model.EventEnrichmentStrategy +import org.zalando.nakadi.client.model.PartitionStrategy /** - * Meant for usage in the client side + * Meant for usage in the Java client side, because Java just cannot(meme:badPokerface) handle implicits. */ object Serialization { + + + def defaultSerializer[T]():Serializer[T] = JacksonJsonMarshaller.serializer[T] def metricsDeserializer():Deserializer[Metrics] = JacksonJsonMarshaller.deserializer(JacksonJsonMarshaller.metricsTR) + def eventTypeDeserializer():Deserializer[EventType] = JacksonJsonMarshaller.deserializer(JacksonJsonMarshaller.eventTypeTR) + def customDeserializer[T](tr:TypeReference[T]):Deserializer[T] = JacksonJsonMarshaller.deserializer(tr) + def partitionDeserializer():Deserializer[Partition] = JacksonJsonMarshaller.deserializer(JacksonJsonMarshaller.partitionTR) + + //Sequence def seqOfEventTypeDeserializer(): Deserializer[Seq[EventType]] = JacksonJsonMarshaller.deserializer(JacksonJsonMarshaller.listOfEventTypeTR) - def eventTypeSerializer():Serializer[EventType] = JacksonJsonMarshaller.serializer + def seqOfPartitionDeserializer():Deserializer[Seq[Partition]] = JacksonJsonMarshaller.deserializer(JacksonJsonMarshaller.listOfPartitionTR) + def seqOfEventValidationStrategy():Deserializer[Seq[EventValidationStrategy]] = JacksonJsonMarshaller.deserializer(JacksonJsonMarshaller.listOfEventValidationStrategyTR) + def seqOfEventEnrichmentStrategy():Deserializer[Seq[EventEnrichmentStrategy]] = JacksonJsonMarshaller.deserializer(JacksonJsonMarshaller.listOfEventEnrichmentStrategyTR) + def seqOfPartitionStrategy():Deserializer[Seq[PartitionStrategy]] = JacksonJsonMarshaller.deserializer(JacksonJsonMarshaller.listOfPartitionStrategyTR) } \ No newline at end of file diff --git a/client/src/test/scala/org/zalando/nakadi/client/scala/SerializerDeserializerTest.scala b/client/src/test/scala/org/zalando/nakadi/client/scala/SerializerDeserializerTest.scala index 3789bdc..6a0b3b9 100644 --- a/client/src/test/scala/org/zalando/nakadi/client/scala/SerializerDeserializerTest.scala +++ b/client/src/test/scala/org/zalando/nakadi/client/scala/SerializerDeserializerTest.scala @@ -28,6 +28,7 @@ import JacksonJsonMarshaller._ val testName = "always result in the same entity" s"$testName(eventMetadata)" in { checkSerializationDeserializationProcess("eventMetadata", eventMetadata) + } "EventType" in { println(" ####### " + EventTypeCategory.withName("business")) diff --git a/client/src/test/scala/org/zalando/nakadi/client/scala/TestFactory.scala b/client/src/test/scala/org/zalando/nakadi/client/scala/TestFactory.scala index fb58893..4f97819 100644 --- a/client/src/test/scala/org/zalando/nakadi/client/scala/TestFactory.scala +++ b/client/src/test/scala/org/zalando/nakadi/client/scala/TestFactory.scala @@ -18,26 +18,43 @@ import org.zalando.nakadi.client.model.JacksonJsonMarshaller import org.zalando.nakadi.client.Serializer import org.zalando.nakadi.client.model.JacksonJsonMarshaller import org.zalando.nakadi.client.Deserializer +import org.zalando.nakadi.client.model.SchemaType +import org.zalando.nakadi.client.model.PartitionStrategy +import org.zalando.nakadi.client.Deserializer +import org.zalando.nakadi.client.model.EventTypeSchema +import org.zalando.nakadi.client.model.EventMetadata +import org.zalando.nakadi.client.model.EventTypeCategory +import org.zalando.nakadi.client.model.EventType +import org.zalando.nakadi.client.Serializer -trait ClientFactory { - val host = "nakadi-sandbox.aruha-test.zalan.do" - val OAuth2Token = () => "" - val port = 443 - val connection = Connection.newConnection(host, port, OAuth2Token, true, false) - val client = new ClientImpl(connection, "UTF-8") - +object ClientFactory { + def host():String = "nakadi-sandbox.my-test.fernan.do" + def OAuth2Token(): () => String = () => "" + def getToken():String = OAuth2Token().apply() + def port():Integer = 443 + def connection():Connection = Connection.newConnection(host, port, OAuth2Token(), true, false) + def client():Client = new ClientImpl(connection, "UTF-8") } +//trait ClientFactory { +// def host():String +// def OAuth2Token : () => String +// def getToken:String +// def port(): Int +// def connection(): Connection +// def client(): Client +// +//} -case class EventActions(client: Client) { -import JacksonJsonMarshaller._ +case class EventActions(client: Client) { + import JacksonJsonMarshaller._ def create[T](name: String, event: Seq[T])(implicit ser: Serializer[Seq[T]]) = { client.publishEvents[T](name, event) } } -case class EventTypesActions(client: Client) { -import JacksonJsonMarshaller._ +case class EventTypesActions(client: Client) { + import JacksonJsonMarshaller._ def create(event: EventType)(implicit ser: Serializer[EventType]) = { executeCall(client.createEventType(event)) } diff --git a/client/src/test/scala/org/zalando/nakadi/client/utils/ConversionsTest.scala b/client/src/test/scala/org/zalando/nakadi/client/utils/ConversionsTest.scala index 0bf14e9..8663341 100644 --- a/client/src/test/scala/org/zalando/nakadi/client/utils/ConversionsTest.scala +++ b/client/src/test/scala/org/zalando/nakadi/client/utils/ConversionsTest.scala @@ -1,4 +1,4 @@ -package org.zalando.nakadi.client.scala +package org.zalando.nakadi.client.scala.utils import scala.concurrent.Future import scala.concurrent.Await @@ -28,7 +28,7 @@ class ConversionsTest extends WordSpec with Matchers with MockitoSugar { "Conversions" should { "be implemented" in { - fail() +// fail() } } diff --git a/client/src/test/scala/org/zalando/nakadi/client/utils/TestScalaEntity.scala b/client/src/test/scala/org/zalando/nakadi/client/utils/TestScalaEntity.scala index 452a384..c338291 100644 --- a/client/src/test/scala/org/zalando/nakadi/client/utils/TestScalaEntity.scala +++ b/client/src/test/scala/org/zalando/nakadi/client/utils/TestScalaEntity.scala @@ -23,7 +23,7 @@ object TestScalaEntity { val problem = new Problem("problemType", "title", 312, Option("detail"), Option("instance")) val metrics = new Metrics(Map("metrics"->"test")) val partition = new Partition(0, 132, 4423) - val cursor = new Cursor(0, Some(0)) + val cursor = new Cursor(0, 120) val eventTypeSchema = new EventTypeSchema(SchemaType.JSON, "schema") val eventValidationStrategy = EventValidationStrategy.NONE val partitionResolutionStrategy = PartitionStrategy.HASH @@ -35,7 +35,7 @@ object TestScalaEntity { val eventMetadata = new EventMetadata("eid", Option(eventType), "occurredAt", Option("receivedAt"), List("parentEids"), Option("flowId"), Option("partition")) case class MyEvent(name:String, metadata:Option[EventMetadata]) extends Event val myEvent = new MyEvent("test",Some(eventMetadata)) - val eventStreamBatch = new EventStreamBatch(cursor, List(myEvent)) + val eventStreamBatch = new EventStreamBatch(cursor, Option(List(myEvent))) val batchItemResponse = new BatchItemResponse(Option("eid"), BatchItemPublishingStatus.SUBMITTED, Option(BatchItemStep.PUBLISHING), Option("detail")) // custom event diff --git a/it/src/test/java/org/zalando/nakadi/client/examples/java/ClientExample.java b/it/src/test/java/org/zalando/nakadi/client/examples/java/ClientExample.java new file mode 100644 index 0000000..aa5a70e --- /dev/null +++ b/it/src/test/java/org/zalando/nakadi/client/examples/java/ClientExample.java @@ -0,0 +1,48 @@ +package org.zalando.nakadi.client.examples.java; + +import java.util.List; +import java.util.Optional; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.function.Consumer; + +import org.zalando.nakadi.client.java.Client; +import org.zalando.nakadi.client.java.model.PartitionStrategy; +import org.zalando.nakadi.client.utils.ClientBuilder; +import org.zalando.nakadi.client.scala.ClientFactory; + +public class ClientExample { + private static final String token = ClientFactory.getToken(); + + private static Optional> unwrap( + Future>> result) + throws InterruptedException, ExecutionException { + return result.get(); + } + + public static void main(String[] args) throws InterruptedException, + ExecutionException { + final Client client = new ClientBuilder()// + .withHost("nakadi-sandbox.aruha-test.zalan.do")// + .withSecuredConnection(true) // s + .withVerifiedSslCertificate(false) // s + .withTokenProvider4Java(() -> token)// + .buildJavaClient(); + + Future>> result = client + .getPartitioningStrategies(); + + Optional> opt = ClientExample.unwrap(result); + + opt.ifPresent(new Consumer>() { + @Override + public void accept(List t) { + System.out.println(">>>>" + t); + } + }); + while (true) { + + } + } + +} diff --git a/it/src/test/java/org/zalando/nakadi/client/examples/java/EventCreationExample.java b/it/src/test/java/org/zalando/nakadi/client/examples/java/EventCreationExample.java new file mode 100644 index 0000000..e9abd12 --- /dev/null +++ b/it/src/test/java/org/zalando/nakadi/client/examples/java/EventCreationExample.java @@ -0,0 +1,88 @@ +package org.zalando.nakadi.client.examples.java; + +import java.util.ArrayList; +import java.util.List; + +import org.zalando.nakadi.client.java.Client; +import org.zalando.nakadi.client.java.model.Event; +import org.zalando.nakadi.client.java.model.EventEnrichmentStrategy; +import org.zalando.nakadi.client.java.model.EventType; +import org.zalando.nakadi.client.java.model.EventTypeCategory; +import org.zalando.nakadi.client.java.model.EventTypeSchema; +import org.zalando.nakadi.client.java.model.EventValidationStrategy; +import org.zalando.nakadi.client.java.model.PartitionStrategy; +import org.zalando.nakadi.client.java.model.SchemaType; +import org.zalando.nakadi.client.scala.ClientFactory; +import org.zalando.nakadi.client.utils.ClientBuilder; + +public class EventCreationExample { + + public class MeetingsEvent implements Event { + private final String date; + private final String topic; + + public MeetingsEvent(String date, String topic) { + this.date = date; + this.topic = topic; + } + + public String getDate() { + return date; + } + + public String getTopic() { + return topic; + } + + } + + public Client createClient() { + return new ClientBuilder()// + .withHost("nakadi-sandbox.aruha-test.zalan.do")// + .withSecuredConnection(true) // s + .withVerifiedSslCertificate(false) // s + .withTokenProvider4Java(() -> ClientFactory.getToken())// + .buildJavaClient(); + } + + public EventTypeSchema createEventTypeSchema(String schema) { + return new EventTypeSchema(SchemaType.JSON, schema); + } + + public EventType createEventType(String name,EventTypeSchema eventTypeSchema) { + String owner = "team-laas"; + EventTypeCategory category = EventTypeCategory.UNDEFINED; + EventValidationStrategy validationStrategies = EventValidationStrategy.NONE; + EventEnrichmentStrategy enrichmentStrategies = null; + PartitionStrategy partitionStrategy = PartitionStrategy.RANDOM; + List paritionKeyFields = new ArrayList("date", "topic"); + + return new EventType(name,// eventTypeName + owner,// owner + category,// category + validationStrategies,// validationStrategies + enrichmentStrategies,// enrichmentStrategies + partitionStrategy,// partitionStrategy + eventTypeSchema,// eventTypeSchema + null,// dataKeyFields + null,// partitionKeyFields + null);// statistics + + } + + public static void main(String[] args) { + EventCreationExample example = new EventCreationExample(); + // 1. Create client + final Client client = example.createClient(); + + // 2. Create a simple Meeting instance + EventCreationExample.MeetingsEvent meeting = example.new MeetingsEvent( + "2016-04-28T13:28:15+00:00", "Hackthon"); + + // Create the EventType + String schema = " { \"properties\":\" { \"date\": { \"type\": \"string\" }, \"topic\": { \"type\": \"string\"} } }"; + + EventTypeSchema eventSchame = example.createEventTypeSchema(schema); + + } +} diff --git a/it/src/test/scala/org/zalando/nakadi/client/ClientSubscriptionTest.scala b/it/src/test/scala/org/zalando/nakadi/client/ClientSubscriptionTest.scala index 0adfa4d..204e760 100644 --- a/it/src/test/scala/org/zalando/nakadi/client/ClientSubscriptionTest.scala +++ b/it/src/test/scala/org/zalando/nakadi/client/ClientSubscriptionTest.scala @@ -2,9 +2,15 @@ package org.zalando.nakadi.client import org.scalatest.{ Matchers, WordSpec } import org.zalando.nakadi.client.model._ import com.fasterxml.jackson.core.`type`.TypeReference - -class ClientSubscriptionTest extends WordSpec with Matchers with JacksonJsonMarshaller with ModelFactory with ClientFactory { - +import org.zalando.nakadi.client.model.JacksonJsonMarshaller +import org.zalando.nakadi.client.scala.ModelFactory +import org.zalando.nakadi.client.scala.EventTypesActions +import org.zalando.nakadi.client.scala.ClientFactory +import org.zalando.nakadi.client.scala.EventActions + +class ClientSubscriptionTest extends WordSpec with Matchers with ModelFactory { + import ClientFactory._ + import JacksonJsonMarshaller._ case class MyEventExample(orderNumber: String) implicit def myEventExampleTR: TypeReference[MyEventExample] = new TypeReference[MyEventExample] {} val eventAction = new EventActions(client) @@ -22,7 +28,7 @@ class ClientSubscriptionTest extends WordSpec with Matchers with JacksonJsonMars } def onSubscribed(): Unit = ??? def onUnsubscribed(): Unit = ??? - def onReceive(sourceUrl: String, cursor: Cursor, event: MyEventExample): Unit = ??? + def onReceive(sourceUrl: String, cursor: Cursor, event: Seq[MyEventExample]): Unit = ??? } val url = "/event-types/test-client-integration-event-1936085527-148383828851369665/events" val eve = "test-client-integration-event-1936085527-148383828851369665" @@ -30,17 +36,17 @@ class ClientSubscriptionTest extends WordSpec with Matchers with JacksonJsonMars client.subscribe(eve, params, listener) // eventAction.create("test-client-integration-event-1936085527-148383828851369665", List(MyEventExample("test-1"))) // eventAction.create("test-client-integration-event-1936085527-148383828851369665", events) -// while(true){ -// -// } + // while(true){ + // + // } } - + "Create" in { - val events = for { + val events = for { a <- 0 to 4005 - } yield MyEventExample("order-"+a) -// eventAction.create("test-client-integration-event-1936085527-148383828851369665", List(MyEventExample("test-1"))) + } yield MyEventExample("order-" + a) + // eventAction.create("test-client-integration-event-1936085527-148383828851369665", List(MyEventExample("test-1"))) eventAction.create("test-client-integration-event-1936085527-148383828851369665", events) } diff --git a/it/src/test/scala/org/zalando/nakadi/client/EventTypesIntegrationTest.scala b/it/src/test/scala/org/zalando/nakadi/client/EventTypesIntegrationTest.scala index 8d7bcd4..67921f6 100644 --- a/it/src/test/scala/org/zalando/nakadi/client/EventTypesIntegrationTest.scala +++ b/it/src/test/scala/org/zalando/nakadi/client/EventTypesIntegrationTest.scala @@ -3,9 +3,14 @@ package org.zalando.nakadi.client import org.scalatest.{ Matchers, WordSpec } import org.zalando.nakadi.client.model._ import com.fasterxml.jackson.core.`type`.TypeReference - -class EventTypeTest extends WordSpec with Matchers with JacksonJsonMarshaller with ModelFactory with ClientFactory { - +import org.zalando.nakadi.client.scala.ClientFactory +import org.zalando.nakadi.client.scala.EventTypesActions +import org.zalando.nakadi.client.scala.EventActions +import org.zalando.nakadi.client.scala.ModelFactory + +class EventTypeTest extends WordSpec with Matchers with ModelFactory{ + import ClientFactory._ +import JacksonJsonMarshaller._ val eventAction = new EventActions(client) val eventTypeAction = new EventTypesActions(client) "POST/PUT/GET/DELETE single EventType " in { diff --git a/it/src/test/scala/org/zalando/nakadi/client/KlientIntegrationTest.scala b/it/src/test/scala/org/zalando/nakadi/client/KlientIntegrationTest.scala index f50b924..2a67625 100644 --- a/it/src/test/scala/org/zalando/nakadi/client/KlientIntegrationTest.scala +++ b/it/src/test/scala/org/zalando/nakadi/client/KlientIntegrationTest.scala @@ -3,50 +3,52 @@ package org.zalando.nakadi.client.config import scala.concurrent.Await import scala.concurrent.Future import scala.concurrent.duration.DurationInt + import org.scalatest.Matchers import org.scalatest.WordSpec import org.zalando.nakadi.client._ import org.zalando.nakadi.client.model._ -import spray.json._ +import org.zalando.nakadi.client.scala.ClientFactory +import org.zalando.nakadi.client.scala.ModelFactory + import com.fasterxml.jackson.core.`type`.TypeReference -import org.zalando.nakadi.client.ClientFactory -import org.zalando.nakadi.client.ModelFactory -import scala.Right -class KlientIntegrationTest extends WordSpec with Matchers with JacksonJsonMarshaller with ModelFactory with ClientFactory { +class KlientIntegrationTest extends WordSpec with Matchers with ModelFactory { + import ClientFactory._ + import JacksonJsonMarshaller._ "Nakadi Client" should { "parse multiple PartitionResolutionStrategy" in { - val Right(result) = executeCall(client.partitionStrategies()) + val Right(result) = executeCall(client.getPartitioningStrategies()) result.size should be > 0 } //TODO: Change it when this endpoint is implemented by nakadi "parse exsiting validationStrategies" in { - val result = executeCall(client.validationStrategies()) + val result = executeCall(client.getValidationStrategies()) result shouldBe Right(None) } //TODO: Change it when this endpoint is implemented by nakadi "parse exsiting enrishment-strategies" in { - val result = executeCall(client.enrichmentStrategies()) + val result = executeCall(client.getEnrichmentStrategies()) result shouldBe Right(None) } //TODO: Change when all events are valid "parse existing eventTypes" in { - val Right(result) = executeCall(client.eventTypes()) + val Right(result) = executeCall(client.getEventTypes()) result.size should be > 0 } "create a new eventType" in { val eventType = createUniqueEventType() - executeCall(client.newEventType(eventType)) shouldBe None + executeCall(client.createEventType(eventType)) shouldBe None } "get EventType" in { val eventType = createUniqueEventType() - executeCall(client.newEventType(eventType)) shouldBe None - executeCall(client.eventType(eventType.name)) shouldBe Right(Some(eventType)) + executeCall(client.createEventType(eventType)) shouldBe None + executeCall(client.getEventType(eventType.name)) shouldBe Right(Some(eventType)) } "delete EventType" in { val eventType = createUniqueEventType() - executeCall(client.newEventType(eventType)) shouldBe None + executeCall(client.createEventType(eventType)) shouldBe None executeCall(client.deleteEventType(eventType.name)) shouldBe None } "Create the event itself" in { @@ -58,8 +60,8 @@ class KlientIntegrationTest extends WordSpec with Matchers with JacksonJsonMarsh val event = new EventExample("22301982837", Some(createEventMetadata())) val eventType = createUniqueEventType() - executeCall(client.newEventType(eventType)) shouldBe None - executeCall(client.newEvents[EventExample](eventType.name, List(event))) shouldBe None + executeCall(client.createEventType(eventType)) shouldBe None + executeCall(client.publishEvents[EventExample](eventType.name, List(event))) shouldBe None } } private def assertIsNotImplementedYet[T](input: Either[ClientError, Option[List[T]]]) = { diff --git a/it/src/test/scala/org/zalando/nakadi/client/example/ClientExample.scala b/it/src/test/scala/org/zalando/nakadi/client/example/ClientExample.scala index 91adcaf..8318849 100644 --- a/it/src/test/scala/org/zalando/nakadi/client/example/ClientExample.scala +++ b/it/src/test/scala/org/zalando/nakadi/client/example/ClientExample.scala @@ -3,9 +3,10 @@ package org.zalando.nakadi.client.example import java.math.BigInteger import akka.actor._ import akka.stream.actor._ -import org.zalando.nakadi.client.ClientImpl -import org.zalando.nakadi.client.Connection -import org.zalando.nakadi.client.ClientFactory +import org.zalando.nakadi.client.scala.ClientImpl +import org.zalando.nakadi.client.scala.Connection +import org.zalando.nakadi.client.scala.ClientFactory +import org.zalando.nakadi.client.scala.ClientFactory object Main extends App { @@ -15,7 +16,8 @@ object Main extends App { } -object ClientExample extends ClientFactory { +object ClientExample { + import ClientFactory._ def nakadiClient() = client def startSimplePubSubExample(system: ActorSystem) { diff --git a/it/src/test/scala/org/zalando/nakadi/client/example2/ClientExample.scala b/it/src/test/scala/org/zalando/nakadi/client/example2/ClientExample.scala index d50a2b8..3e5fbb1 100644 --- a/it/src/test/scala/org/zalando/nakadi/client/example2/ClientExample.scala +++ b/it/src/test/scala/org/zalando/nakadi/client/example2/ClientExample.scala @@ -7,7 +7,7 @@ import scala.concurrent.Future import akka.actor.ActorSystem import akka.stream.ActorMaterializer import akka.http.scaladsl.Http -import org.zalando.nakadi.client.Connection +import org.zalando.nakadi.client.scala.Connection import java.net.InetSocketAddress import scala.concurrent.Future import akka.stream.scaladsl.Tcp diff --git a/it/src/test/scala/org/zalando/nakadi/client/example2/Ecample3.scala b/it/src/test/scala/org/zalando/nakadi/client/example2/Ecample3.scala index 6c05ea2..0b723e6 100644 --- a/it/src/test/scala/org/zalando/nakadi/client/example2/Ecample3.scala +++ b/it/src/test/scala/org/zalando/nakadi/client/example2/Ecample3.scala @@ -14,15 +14,15 @@ import akka.http.scaladsl.model.HttpResponse import scala.util.Try import java.net.URI import akka.stream.scaladsl.Flow -import org.zalando.nakadi.client.Connection +import org.zalando.nakadi.client.scala.Connection import akka.http.scaladsl.model.HttpRequest import scala.concurrent.Future -import org.zalando.nakadi.client.HttpFactory +import org.zalando.nakadi.client.scala.HttpFactory import akka.http.scaladsl.unmarshalling._ import scala.concurrent.ExecutionContext.Implicits.global import akka.http.scaladsl.Http import org.zalando.nakadi.client.model.JacksonJsonMarshaller -import org.zalando.nakadi.client.ClientFactory +import org.zalando.nakadi.client.scala.ClientFactory import org.zalando.nakadi.client.StreamParameters import akka.stream.ActorMaterializer import akka.http.scaladsl.model.headers.RawHeader @@ -44,8 +44,9 @@ class ProducerActor(httpRequest: HttpRequest) extends Actor with ActorPublisher[ case class Url(url: String, depth: Long) -object Test extends App with ClientFactory with HttpFactory with JacksonJsonMarshaller { - +object Test extends App with HttpFactory { + import ClientFactory._ + import JacksonJsonMarshaller._ private implicit val actorSystem = ActorSystem("Nakadi-Client-Connections") private implicit val http = Http(actorSystem) implicit val materializer = ActorMaterializer() @@ -54,7 +55,7 @@ object Test extends App with ClientFactory with HttpFactory with JacksonJsonMars val eventName = "/event-types/test-client-integration-event-1936085527-148383828851369665/events" val params = Some(StreamParameters()) val headers = RawHeader("Accept", "application/x-json-stream") :: withHeaders(params) - val request = withHttpRequest(eventName, HttpMethods.GET, headers, OAuth2Token) + val request = withHttpRequest(eventName, HttpMethods.GET, headers, OAuth2Token(),None) //Model case class MyEventExample(orderNumber: String) diff --git a/it/src/test/scala/org/zalando/nakadi/client/example2/HttpClientExample.scala b/it/src/test/scala/org/zalando/nakadi/client/example2/HttpClientExample.scala index 83bd71f..41ecf55 100644 --- a/it/src/test/scala/org/zalando/nakadi/client/example2/HttpClientExample.scala +++ b/it/src/test/scala/org/zalando/nakadi/client/example2/HttpClientExample.scala @@ -2,10 +2,10 @@ package org.zalando.nakadi.client.example2 import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.Future -import org.zalando.nakadi.client.ClientFactory -import org.zalando.nakadi.client.Connection -import org.zalando.nakadi.client.HttpFactory -import org.zalando.nakadi.client.NakadiDeserializer +import org.zalando.nakadi.client.scala.ClientFactory +import org.zalando.nakadi.client.scala.Connection +import org.zalando.nakadi.client.scala.HttpFactory +import org.zalando.nakadi.client.Deserializer import org.zalando.nakadi.client.StreamParameters import org.zalando.nakadi.client.model.JacksonJsonMarshaller import com.fasterxml.jackson.core.`type`.TypeReference @@ -40,15 +40,17 @@ import akka.util.ByteString import org.zalando.nakadi.client.done.EventConsumingActor import org.zalando.nakadi.client.model.Cursor -object HttpClient extends App with ClientFactory with HttpFactory with JacksonJsonMarshaller { +object HttpClient extends App with HttpFactory { + import ClientFactory._ + import JacksonJsonMarshaller._ private implicit val actorSystem = ActorSystem("Nakadi-Client-Connections") private implicit val http = Http(actorSystem) implicit val materializer = ActorMaterializer() val eventName = "/event-types/test-client-integration-event-1936085527-148383828851369665/events" - val cursor = Cursor(0,Some(30115)) + val cursor = Cursor(0,30115) val params = Some(StreamParameters()) val headers = RawHeader("Accept", "application/x-json-stream") :: withHeaders(params) - val request = withHttpRequest(eventName, HttpMethods.GET, headers, OAuth2Token) + val request = withHttpRequest(eventName, HttpMethods.GET, headers, OAuth2Token(),None) case class MyEventExample(orderNumber: String) implicit val myEvent = new TypeReference[MyEventExample] {} val receiver = new ReceiverGraph[MyEventExample](eventName, connection, request, headers) @@ -56,7 +58,7 @@ object HttpClient extends App with ClientFactory with HttpFactory with JacksonJs } class ReceiverGraph[T](eventName: String, connection: Connection, request: HttpRequest, headers: List[HttpHeader]) // -(implicit system: ActorSystem, m: ActorMaterializer, des: NakadiDeserializer[T]) { +(implicit system: ActorSystem, m: ActorMaterializer, des: Deserializer[T]) { import GraphDSL.Implicits._ def listen() = { @@ -103,9 +105,9 @@ object Transformers { def failure(s: String) = Future.failed(new IllegalArgumentException(s"Error $s")) def toByteString(implicit m: ActorMaterializer): Flow[HttpResponse, String, NotUsed] = Flow[HttpResponse].mapAsync(1)(Unmarshal(_).to[String]) val validRequestFilter: Flow[HttpResponse, HttpResponse, NotUsed] = Flow[HttpResponse].filter(_.status.isSuccess()) - def transformToObject[T](implicit des: NakadiDeserializer[T]): Flow[String, T, NotUsed] = Flow[String].map { x => + def transformToObject[T](implicit des: Deserializer[T]): Flow[String, T, NotUsed] = Flow[String].map { x => println(" x " + x) - des.fromJson(x) + des.from(x) } } \ No newline at end of file diff --git a/it/src/test/scala/org/zalando/nakadi/client/example2/ServerExample.scala b/it/src/test/scala/org/zalando/nakadi/client/example2/ServerExample.scala index 7546ecb..d5e522f 100644 --- a/it/src/test/scala/org/zalando/nakadi/client/example2/ServerExample.scala +++ b/it/src/test/scala/org/zalando/nakadi/client/example2/ServerExample.scala @@ -7,7 +7,7 @@ import scala.concurrent.Future import akka.actor.ActorSystem import akka.stream.ActorMaterializer import akka.http.scaladsl.Http -import org.zalando.nakadi.client.Connection +import org.zalando.nakadi.client.scala.Connection import java.net.InetSocketAddress import scala.concurrent.Future import akka.stream.scaladsl.Tcp diff --git a/it/src/test/scala/org/zalando/nakadi/client/examples/scala/ClientCreationExample.scala b/it/src/test/scala/org/zalando/nakadi/client/examples/scala/ClientCreationExample.scala new file mode 100644 index 0000000..0005a7b --- /dev/null +++ b/it/src/test/scala/org/zalando/nakadi/client/examples/scala/ClientCreationExample.scala @@ -0,0 +1,26 @@ +package org.zalando.nakadi.client.scala + +import org.zalando.nakadi.client.model.JacksonJsonMarshaller +import scala.concurrent.Await +import scala.concurrent.duration.DurationInt +import org.zalando.nakadi.client.utils.ClientBuilder + + + + +object ClientCreationExample extends App { + import JacksonJsonMarshaller._ + val a = ClientBuilder() + .withHost("nakadi-sandbox.aruha-test.zalan.do") + .withSecuredConnection(true) //s + .withVerifiedSslCertificate(false) //s + .withTokenProvider(ClientFactory.OAuth2Token()) // + // .build(); + .build() + val eventTypeName = "test-client-integration-event-1936085527-148383828851369665" + val url = "/event-types/test-client-integration-event-1936085527-148383828851369665/events" + val result = Await.result(a.getPartitions(eventTypeName), 5.seconds) + println("########################################") + println("" + result) + println("########################################") +} \ No newline at end of file diff --git a/it/src/test/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala b/it/src/test/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala new file mode 100644 index 0000000..706f13e --- /dev/null +++ b/it/src/test/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala @@ -0,0 +1,82 @@ +package org.zalando.nakadi.client.examples.scala + +import org.joda.time.DateTime +import org.joda.time.format.DateTimeFormatter +import org.zalando.nakadi.client.model.EventType +import org.zalando.nakadi.client.model.EventTypeCategory +import org.zalando.nakadi.client.model.EventTypeSchema +import org.zalando.nakadi.client.model.JacksonJsonMarshaller +import org.zalando.nakadi.client.model.PartitionStrategy +import org.zalando.nakadi.client.model.SchemaType +import org.zalando.nakadi.client.scala.ClientFactory +import com.fasterxml.jackson.databind.util.ISO8601DateFormat +import org.zalando.nakadi.client.model.Event + +object EventCreationExample extends App { + + + + // 1. Create the client + + import org.zalando.nakadi.client.utils.ClientBuilder + import org.zalando.nakadi.client.scala.Client + val client: Client = ClientBuilder() + .withHost("nakadi-sandbox.aruha-test.zalan.do") + .withSecuredConnection(true) //s + .withVerifiedSslCertificate(false) //s + .withTokenProvider(ClientFactory.OAuth2Token()) // + // .build(); + .build() + + // 2. Create a simple Meeting Event class + case class MeetingsEvent(date: String, topic: String) extends Event + + //We define the Json representation of our Meeting Event. + val schema: String = """ + { + "properties": + { + "date": { "type": "string" }, + "topic": { "type": "string"} + } + }""" + + // 3. Create the EventType, + // We need to create an eventType(a topic), where listeners + // can subscribe to, to get our published events. + val eventTypeSchema = new EventTypeSchema(SchemaType.JSON, schema) + + //First the eventType name, wich will be part of the URL: https://nakadi.test.io/event-types/{eventTypeName} + //See the API for more information on the EventType model + //https://github.com/zalando/nakadi/blob/nakadi-jvm/api/nakadi-event-bus-api.yaml#L1240 + val eventTypeName = "org.zalando.laas.meetings-1" + + val owner = "team-laas" + val category = EventTypeCategory.UNDEFINED // We want just to pass data without through Nakadi, simple schema-validation is enough! + val validationStrategies = None // Validation strategies are not defined yet! + val enrichmentStrategies = Nil + val partitionStrategy = PartitionStrategy.RANDOM + def paritionKeyFields() = List("date", "topic") + + val eventType = new EventType(eventTypeName, + owner, + category, + validationStrategies, + enrichmentStrategies, + Some(partitionStrategy), + Some(eventTypeSchema), + None, + Option(paritionKeyFields()), + None) + + //You need to import the default Serializer if you don't sepecify your own! + import JacksonJsonMarshaller._ + + client.createEventType(eventType) + + // 4. Publish the EventType + + val event = new MeetingsEvent("2016-04-28T13:28:15+00:00", "Hackaton") + client.publishEvent(eventTypeName, event) + +} \ No newline at end of file diff --git a/it/src/test/scala/org/zalando/nakadi/client/logic/ReceiverGraph.scala b/it/src/test/scala/org/zalando/nakadi/client/logic/ReceiverGraph.scala index 0a8e72b..9b048d2 100644 --- a/it/src/test/scala/org/zalando/nakadi/client/logic/ReceiverGraph.scala +++ b/it/src/test/scala/org/zalando/nakadi/client/logic/ReceiverGraph.scala @@ -2,7 +2,7 @@ package org.zalando.nakadi.client.logic import scala.concurrent.Future -import org.zalando.nakadi.client.Connection +import org.zalando.nakadi.client.scala.Connection import akka.actor.ActorSystem import akka.http.scaladsl.Http diff --git a/it/src/test/scala/org/zalando/nakadi/client/subscription/LeClient.scala b/it/src/test/scala/org/zalando/nakadi/client/subscription/LeClient.scala new file mode 100644 index 0000000..9776d4a --- /dev/null +++ b/it/src/test/scala/org/zalando/nakadi/client/subscription/LeClient.scala @@ -0,0 +1,26 @@ +package org.zalando.nakadi.client.subscription + +import org.zalando.nakadi.client.scala.ClientFactory +import com.fasterxml.jackson.core.`type`.TypeReference +import org.zalando.nakadi.client.model.JacksonJsonMarshaller +import org.zalando.nakadi.client.model.Event + +object Client extends App { + val client = new LeClient + client.send(100) +} + +class LeClient { + import ClientFactory._ + case class MyEventExample(orderNumber: String) extends Event + implicit def myEventExampleTR: TypeReference[MyEventExample] = new TypeReference[MyEventExample] {} + import JacksonJsonMarshaller._ + val eventType = "test-client-integration-event-1936085527-148383828851369665" + def send(in: Int) { + val events = for { + a <- 1 to in + } yield MyEventExample("order-" + a) + + client.publishEvents(eventType, events) + } +} \ No newline at end of file diff --git a/it/src/test/scala/org/zalando/nakadi/client/example2/Subscriber.scala b/it/src/test/scala/org/zalando/nakadi/client/subscription/Subscriber.scala similarity index 56% rename from it/src/test/scala/org/zalando/nakadi/client/example2/Subscriber.scala rename to it/src/test/scala/org/zalando/nakadi/client/subscription/Subscriber.scala index 59f9e75..11c5634 100644 --- a/it/src/test/scala/org/zalando/nakadi/client/example2/Subscriber.scala +++ b/it/src/test/scala/org/zalando/nakadi/client/subscription/Subscriber.scala @@ -1,12 +1,7 @@ -package org.zalando.nakadi.client.example2 +package org.zalando.nakadi.client.subscription -import com.fasterxml -import org.zalando -import org.zalando -import org.zalando -import scala.concurrent.Future import org.zalando.nakadi.client.model.JacksonJsonMarshaller -import org.zalando.nakadi.client.ClientFactory +import org.zalando.nakadi.client.scala.ClientFactory import org.zalando.nakadi.client.StreamParameters import org.zalando.nakadi.client.Listener import org.zalando.nakadi.client.ClientError @@ -18,12 +13,15 @@ import scala.concurrent.duration.DurationInt case class MyEventExample(orderNumber: String) object Subscriber extends App { val a = new A() - // a.startListening() - // a.sendEvents() - a.printPartitions() + a.startListening() +// a.printPartitions() +// a.printEventTypes() +// a.sendEvents(30) } -class A extends ClientFactory with JacksonJsonMarshaller { +class A { + import ClientFactory._ + import JacksonJsonMarshaller._ val eventType = "test-client-integration-event-1936085527-148383828851369665" implicit def myEventExampleTR: TypeReference[MyEventExample] = new TypeReference[MyEventExample] {} def startListening() = { @@ -35,25 +33,41 @@ class A extends ClientFactory with JacksonJsonMarshaller { } def onSubscribed(): Unit = ??? def onUnsubscribed(): Unit = ??? - def onReceive(sourceUrl: String, cursor: Cursor, event: MyEventExample): Unit = ??? + def onReceive(sourceUrl: String, cursor: Cursor, events: Seq[MyEventExample]): Unit = ??? } val url = "/event-types/test-client-integration-event-1936085527-148383828851369665/events" - val c = Cursor(0, Some(1)) - val params = new StreamParameters(cursor = Some(c)) + val cr = Cursor(0, 170000) + val params = new StreamParameters( + cursor = Some(cr) + ,batchLimit = Some(10) +// ,streamLimit=Some(10) +// ,streamTimeout=Some(10) +// ,streamKeepAliveLimit =Some(10) + ) client.subscribe(eventType, params, listener) } def printPartitions() = { - val result = Await.result(client.partitions(eventType), 5.seconds) + val result = Await.result(client.getPartitions(eventType), 5.seconds) + print("partitions", result) + } + + def printEventTypes() = { + val result = Await.result(client.getEventTypes(), 5.seconds) + print("eventTypes", result) + } + + def print(msg: String, obj: Any) = { println("###########################") - println("partitions" + result) + println(s"$msg - " + obj) println("###########################") - } - def sendEvents() = { + def sendEvents(in:Int) = { val events = for { - a <- 0 to 1 + a <- 1 to in } yield MyEventExample("order-" + a) - client.newEvents(eventType, events) + client.publishEvents(eventType, events) } -} \ No newline at end of file +} + + \ No newline at end of file diff --git a/model/src/test/resources/application.conf b/model/src/test/resources/application.conf deleted file mode 100644 index d9376e3..0000000 --- a/model/src/test/resources/application.conf +++ /dev/null @@ -1,29 +0,0 @@ -akka { - loglevel = "DEBUG" - stdout-loglevel = "DEBUG" - - actor { - debug { - # enable DEBUG logging of actor lifecycle changes - lifecycle = on - unhandled = on - } - } - - cluster { - seed-nodes = [ - "akka://ClusterSystem@127.0.0.1:2551", - "akka://ClusterSystem@127.0.0.1:2552"] - - auto-down = on - } -} - -akka.cluster.metrics.enabled=off - -# Enable metrics extension in akka-cluster-metrics. -akka.extensions=["akka.cluster.metrics.ClusterMetricsExtension"] - -# Sigar native library extract location during tests. -# Note: use per-jvm-instance folder when running multiple jvm on one host. -akka.cluster.metrics.native-library-extract-folder=${user.dir}/target/native diff --git a/model/src/test/resources/logback.xml b/model/src/test/resources/logback.xml deleted file mode 100644 index faa8e20..0000000 --- a/model/src/test/resources/logback.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n - - - - - - - - - - - \ No newline at end of file diff --git a/project/Build.scala b/project/Build.scala index f62ba36..9343579 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -23,36 +23,31 @@ val defaultOptions= Seq( "-Ywarn-numeric-widen" // Warn when numerics are widened. ) lazy val commonSettings = Seq( - organization := "com.example", + organization := "org.zalando", version := "0.1.0", scalaVersion := "2.11.7" ) -lazy val model = withDefaults( - "nakadi-klients-model", - project.in(file("model")) - ).settings(libraryDependencies ++= modelDeps) - lazy val api = withDefaults( "nakadi-klients-api", - project.in(file("api")).dependsOn(model) - ) + project.in(file("api")) + ).settings(libraryDependencies ++= apiDeps) lazy val client = withDefaults( "nakadi-klients", - project.in(file("client")).dependsOn(api, model) + project.in(file("client")).dependsOn(api) ).settings(libraryDependencies ++= clientDeps) lazy val it = withDefaults( "nakadi-integration-test", - project.in(file("it")).dependsOn(model, api, client) + project.in(file("it")).dependsOn(api, client) ).settings(libraryDependencies ++= clientDeps) lazy val e2e = withDefaults( "nakadi-end-2-end-test", - project.in(file("e2e")).dependsOn(model, api, client, it) + project.in(file("e2e")).dependsOn(api, client, it) ).settings(libraryDependencies ++= clientDeps) diff --git a/project/Dependencies.scala b/project/Dependencies.scala index c51c8f4..b2954ef 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -4,9 +4,12 @@ object Dependencies { val akkaVersion = "2.4.2" - val modelDeps = {Seq( - "com.fasterxml.jackson.module" %% "jackson-module-scala" % "2.6.3" withSources() withJavadoc(), - "com.fasterxml.jackson.core" % "jackson-core" % "2.7.0" withSources() withJavadoc())} + val apiDeps = { + Seq( + "com.fasterxml.jackson.module" %% "jackson-module-scala" % "2.6.3" withSources() withJavadoc(), + "com.fasterxml.jackson.core" % "jackson-core" % "2.7.0" withSources() withJavadoc()) + } + val clientDeps = { Seq( "com.typesafe.akka" %% "akka-http-spray-json-experimental" % akkaVersion withSources() withJavadoc(), @@ -24,6 +27,8 @@ object Dependencies { "com.fasterxml.jackson.core" % "jackson-core" % "2.7.0" withSources() withJavadoc(), "com.fasterxml.jackson.module" %% "jackson-module-scala" % "2.6.3" withSources() withJavadoc(), "ch.qos.logback" % "logback-classic" % "1.1.3" withSources() withJavadoc(), + "joda-time" % "joda-time" % "2.9.3" withSources() withJavadoc(), + "org.joda" % "joda-convert" % "1.8.1" withSources() withJavadoc(), "org.scalatest" %% "scalatest" % "2.2.6" % "test" withSources() withJavadoc(), //"io.undertow" % "undertow-core" % "1.2.12.Final" % "test" withSources() withJavadoc(), //"io.undertow" % "undertow-servlet" % "1.2.12.Final" % "test" withSources() withJavadoc(), From 24213439c420cee025db9ee42411da25fb2307ad Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Thu, 28 Apr 2016 22:56:56 +0200 Subject: [PATCH 027/183] techjira:LAAS-60 Java Logic is done!! --- .../zalando/nakadi/client/java/Client.java | 10 +- .../client/java/EventEnrichmentStrategy.java | 37 --- .../zalando/nakadi/client/java/Listener.java | 19 ++ .../zalando/nakadi/client/java/Metrics.java | 5 - .../nakadi/client/java/StreamParameters.java | 20 ++ .../BatchItemPublishingStatus.java | 3 +- .../java/{ => enumerator}/BatchItemStep.java | 3 +- .../java/{ => enumerator}/DataOperation.java | 3 +- .../enumerator/EventEnrichmentStrategy.java | 37 +++ .../{ => enumerator}/EventTypeCategory.java | 6 +- .../EventValidationStrategy.java | 6 +- .../{ => enumerator}/PartitionStrategy.java | 6 +- .../java/{ => enumerator}/SchemaType.java | 4 +- .../client/java/model/BusinessEvent.java | 6 + .../nakadi/client/java/model/Cursor.java | 26 ++ .../client/java/model/DataChangeEvent.java | 30 +++ .../java/model/DataChangeEventQualifier.java | 8 + .../nakadi/client/java/model/Event.java | 5 + .../client/java/model/EventMetadata.java | 94 ++++++++ .../client/java/model/EventStreamBatch.java | 54 +++++ .../nakadi/client/java/model/EventType.java | 160 ++++++++++++ .../client/java/model/EventTypeSchema.java | 30 +++ .../java/model/EventTypeStatistics.java | 32 +++ .../nakadi/client/java/model/Metrics.java | 17 ++ .../nakadi/client/java/model/Partition.java | 51 ++++ .../nakadi/client/java/model/Problem.java | 42 ++++ .../zalando/nakadi/client/ClientError.scala | 3 + .../org/zalando/nakadi/client/Listener.scala | 23 -- .../zalando/nakadi/client/Serialization.scala | 3 +- .../zalando/nakadi/client/StreamCursor.scala | 9 + .../zalando/nakadi/client/scala/Client.scala | 20 +- .../nakadi/client/scala/Listener.scala | 16 ++ .../client/scala/StreamParameters.scala | 12 + .../client/scala/{ => model}/Model.scala | 18 +- .../nakadi/client/java/ClientImpl.java | 228 +++++++++--------- .../client/actor/EventConsumingActor.scala | 15 +- .../client/actor/EventReceivingActor.scala | 2 +- .../nakadi/client/scala/ClientImpl.scala | 40 ++- .../nakadi/client/scala/Connection.scala | 23 +- .../nakadi/client/scala/HttpFactory.scala | 37 +-- .../model/JacksonJsonMarshaller.scala | 20 +- .../model/SprayJsonMarshaller.scala | 0 .../client/utils/FutureConversions.scala | 120 ++++----- .../nakadi/client/utils/ImplicitHelper.scala | 34 +-- .../nakadi/client/utils/ParseHelper.scala | 1 - .../nakadi/client/scala/ClientTest.scala | 2 +- .../scala/DeserializerSerializerTest.scala | 4 +- .../nakadi/client/scala/EnumModule.scala | 12 +- .../scala/SerializerDeserializerTest.scala | 5 +- .../nakadi/client/scala/TestFactory.scala | 22 +- .../nakadi/client/utils/ConversionsTest.scala | 2 +- .../nakadi/client/utils/TestScalaEntity.scala | 35 +-- .../shared/.js/.gitignore | 15 ++ .../client/ClientSubscriptionTest.scala | 7 +- .../client/EventTypesIntegrationTest.scala | 4 +- .../nakadi/client/KlientIntegrationTest.scala | 2 +- .../nakadi/client/example2/Ecample3.scala | 4 +- .../client/example2/HttpClientExample.scala | 39 +-- .../scala/ClientCreationExample.scala | 2 +- .../examples/scala/EventCreationExample.scala | 8 +- .../nakadi/client/subscription/LeClient.scala | 4 +- .../client/subscription/Subscriber.scala | 38 ++- 62 files changed, 1025 insertions(+), 518 deletions(-) delete mode 100644 api/src/main/java/org/zalando/nakadi/client/java/EventEnrichmentStrategy.java create mode 100644 api/src/main/java/org/zalando/nakadi/client/java/Listener.java delete mode 100644 api/src/main/java/org/zalando/nakadi/client/java/Metrics.java create mode 100644 api/src/main/java/org/zalando/nakadi/client/java/StreamParameters.java rename api/src/main/java/org/zalando/nakadi/client/java/{ => enumerator}/BatchItemPublishingStatus.java (90%) rename api/src/main/java/org/zalando/nakadi/client/java/{ => enumerator}/BatchItemStep.java (92%) rename api/src/main/java/org/zalando/nakadi/client/java/{ => enumerator}/DataOperation.java (88%) create mode 100644 api/src/main/java/org/zalando/nakadi/client/java/enumerator/EventEnrichmentStrategy.java rename api/src/main/java/org/zalando/nakadi/client/java/{ => enumerator}/EventTypeCategory.java (86%) rename api/src/main/java/org/zalando/nakadi/client/java/{ => enumerator}/EventValidationStrategy.java (88%) rename api/src/main/java/org/zalando/nakadi/client/java/{ => enumerator}/PartitionStrategy.java (89%) rename api/src/main/java/org/zalando/nakadi/client/java/{ => enumerator}/SchemaType.java (83%) create mode 100644 api/src/main/java/org/zalando/nakadi/client/java/model/BusinessEvent.java create mode 100644 api/src/main/java/org/zalando/nakadi/client/java/model/Cursor.java create mode 100644 api/src/main/java/org/zalando/nakadi/client/java/model/DataChangeEvent.java create mode 100644 api/src/main/java/org/zalando/nakadi/client/java/model/DataChangeEventQualifier.java create mode 100644 api/src/main/java/org/zalando/nakadi/client/java/model/Event.java create mode 100644 api/src/main/java/org/zalando/nakadi/client/java/model/EventMetadata.java create mode 100644 api/src/main/java/org/zalando/nakadi/client/java/model/EventStreamBatch.java create mode 100644 api/src/main/java/org/zalando/nakadi/client/java/model/EventType.java create mode 100644 api/src/main/java/org/zalando/nakadi/client/java/model/EventTypeSchema.java create mode 100644 api/src/main/java/org/zalando/nakadi/client/java/model/EventTypeStatistics.java create mode 100644 api/src/main/java/org/zalando/nakadi/client/java/model/Metrics.java create mode 100644 api/src/main/java/org/zalando/nakadi/client/java/model/Partition.java create mode 100644 api/src/main/java/org/zalando/nakadi/client/java/model/Problem.java create mode 100644 api/src/main/scala/org/zalando/nakadi/client/ClientError.scala delete mode 100644 api/src/main/scala/org/zalando/nakadi/client/Listener.scala create mode 100644 api/src/main/scala/org/zalando/nakadi/client/StreamCursor.scala create mode 100644 api/src/main/scala/org/zalando/nakadi/client/scala/Listener.scala create mode 100644 api/src/main/scala/org/zalando/nakadi/client/scala/StreamParameters.scala rename api/src/main/scala/org/zalando/nakadi/client/scala/{ => model}/Model.scala (99%) rename client/src/main/scala/org/zalando/nakadi/client/{ => scala}/model/JacksonJsonMarshaller.scala (88%) rename client/src/main/scala/org/zalando/nakadi/client/{ => scala}/model/SprayJsonMarshaller.scala (100%) diff --git a/api/src/main/java/org/zalando/nakadi/client/java/Client.java b/api/src/main/java/org/zalando/nakadi/client/java/Client.java index 6748766..2751295 100644 --- a/api/src/main/java/org/zalando/nakadi/client/java/Client.java +++ b/api/src/main/java/org/zalando/nakadi/client/java/Client.java @@ -5,9 +5,15 @@ import java.util.concurrent.Future; import org.zalando.nakadi.client.Deserializer; -import org.zalando.nakadi.client.Listener; import org.zalando.nakadi.client.Serializer; -import org.zalando.nakadi.client.StreamParameters; +import org.zalando.nakadi.client.java.enumerator.EventEnrichmentStrategy; +import org.zalando.nakadi.client.java.enumerator.EventValidationStrategy; +import org.zalando.nakadi.client.java.enumerator.PartitionStrategy; +import org.zalando.nakadi.client.java.model.Event; +import org.zalando.nakadi.client.java.model.EventType; +import org.zalando.nakadi.client.java.model.Metrics; +import org.zalando.nakadi.client.java.model.Partition; +import org.zalando.nakadi.client.scala.StreamParameters; public interface Client { diff --git a/api/src/main/java/org/zalando/nakadi/client/java/EventEnrichmentStrategy.java b/api/src/main/java/org/zalando/nakadi/client/java/EventEnrichmentStrategy.java deleted file mode 100644 index 01ad879..0000000 --- a/api/src/main/java/org/zalando/nakadi/client/java/EventEnrichmentStrategy.java +++ /dev/null @@ -1,37 +0,0 @@ -package org.zalando.nakadi.client.java; - -import org.zalando.nakadi.client.model.EventEnrichmentStrategy; - -import com.fasterxml.jackson.annotation.JsonValue; - -import scala.Option; - -/** - * Defines a rule for transformation of an incoming Event. No existing fields - * might be modified. In practice this is used to set automatically values in - * the Event upon reception (i.e. set a reception timestamp on the Event). Rules - * might require additional parameters; see the `doc` field of the existing - * rules for details. See GET /registry/enrichment-strategies for a list of - * available rules. - */ - -public enum EventEnrichmentStrategy { - METADATA("metadata_enrichment"); - - private final String metadata; - - private EventEnrichmentStrategy(String metadata) { - this.metadata = metadata; - } - @JsonValue - public String getMetadata() { - return metadata; - } - public static Option withName(String code) { - for (EventEnrichmentStrategy e : EventEnrichmentStrategy.values()) { - if (e.metadata.equals(code)) - return Option.apply(e); - } - return Option.empty(); - } -} diff --git a/api/src/main/java/org/zalando/nakadi/client/java/Listener.java b/api/src/main/java/org/zalando/nakadi/client/java/Listener.java new file mode 100644 index 0000000..bc06d9c --- /dev/null +++ b/api/src/main/java/org/zalando/nakadi/client/java/Listener.java @@ -0,0 +1,19 @@ +package org.zalando.nakadi.client.java; + +import java.util.List; + +import org.zalando.nakadi.client.ClientError; +import org.zalando.nakadi.client.java.model.Cursor; +import org.zalando.nakadi.client.java.model.Event; + +public interface Listener { + String id(); + + void onSubscribed(); + + void onUnsubscribed(); + + void onReceive(String sourceUrl, Cursor cursor, List event); + + void onError(String sourceUrl, Cursor cursor, ClientError error); +} diff --git a/api/src/main/java/org/zalando/nakadi/client/java/Metrics.java b/api/src/main/java/org/zalando/nakadi/client/java/Metrics.java deleted file mode 100644 index 10f48ba..0000000 --- a/api/src/main/java/org/zalando/nakadi/client/java/Metrics.java +++ /dev/null @@ -1,5 +0,0 @@ -package org.zalando.nakadi.client.java; - -public class Metrics { - -} diff --git a/api/src/main/java/org/zalando/nakadi/client/java/StreamParameters.java b/api/src/main/java/org/zalando/nakadi/client/java/StreamParameters.java new file mode 100644 index 0000000..14fdf60 --- /dev/null +++ b/api/src/main/java/org/zalando/nakadi/client/java/StreamParameters.java @@ -0,0 +1,20 @@ +package org.zalando.nakadi.client.java; + +public class StreamParameters { + private final Integer partition; + private final Integer offset; + + public StreamParameters(Integer partition, Integer offset) { + this.partition = partition; + this.offset = offset; + } + + public Integer getPartition() { + return partition; + } + + public Integer getOffset() { + return offset; + } + +} diff --git a/api/src/main/java/org/zalando/nakadi/client/java/BatchItemPublishingStatus.java b/api/src/main/java/org/zalando/nakadi/client/java/enumerator/BatchItemPublishingStatus.java similarity index 90% rename from api/src/main/java/org/zalando/nakadi/client/java/BatchItemPublishingStatus.java rename to api/src/main/java/org/zalando/nakadi/client/java/enumerator/BatchItemPublishingStatus.java index 4c28225..0622e92 100644 --- a/api/src/main/java/org/zalando/nakadi/client/java/BatchItemPublishingStatus.java +++ b/api/src/main/java/org/zalando/nakadi/client/java/enumerator/BatchItemPublishingStatus.java @@ -1,6 +1,5 @@ -package org.zalando.nakadi.client.java; +package org.zalando.nakadi.client.java.enumerator; -import org.zalando.nakadi.client.model.BatchItemPublishingStatus; import scala.Option; diff --git a/api/src/main/java/org/zalando/nakadi/client/java/BatchItemStep.java b/api/src/main/java/org/zalando/nakadi/client/java/enumerator/BatchItemStep.java similarity index 92% rename from api/src/main/java/org/zalando/nakadi/client/java/BatchItemStep.java rename to api/src/main/java/org/zalando/nakadi/client/java/enumerator/BatchItemStep.java index e9b9553..71d466c 100644 --- a/api/src/main/java/org/zalando/nakadi/client/java/BatchItemStep.java +++ b/api/src/main/java/org/zalando/nakadi/client/java/enumerator/BatchItemStep.java @@ -1,6 +1,5 @@ -package org.zalando.nakadi.client.java; +package org.zalando.nakadi.client.java.enumerator; -import org.zalando.nakadi.client.model.BatchItemStep; import com.fasterxml.jackson.annotation.JsonValue; diff --git a/api/src/main/java/org/zalando/nakadi/client/java/DataOperation.java b/api/src/main/java/org/zalando/nakadi/client/java/enumerator/DataOperation.java similarity index 88% rename from api/src/main/java/org/zalando/nakadi/client/java/DataOperation.java rename to api/src/main/java/org/zalando/nakadi/client/java/enumerator/DataOperation.java index 60fd0f4..68444db 100644 --- a/api/src/main/java/org/zalando/nakadi/client/java/DataOperation.java +++ b/api/src/main/java/org/zalando/nakadi/client/java/enumerator/DataOperation.java @@ -1,6 +1,5 @@ -package org.zalando.nakadi.client.java; +package org.zalando.nakadi.client.java.enumerator; -import org.zalando.nakadi.client.model.DataOperation; import com.fasterxml.jackson.annotation.JsonValue; diff --git a/api/src/main/java/org/zalando/nakadi/client/java/enumerator/EventEnrichmentStrategy.java b/api/src/main/java/org/zalando/nakadi/client/java/enumerator/EventEnrichmentStrategy.java new file mode 100644 index 0000000..d7d00a4 --- /dev/null +++ b/api/src/main/java/org/zalando/nakadi/client/java/enumerator/EventEnrichmentStrategy.java @@ -0,0 +1,37 @@ +package org.zalando.nakadi.client.java.enumerator; + +import scala.Option; + +import com.fasterxml.jackson.annotation.JsonValue; + +/** + * Defines a rule for transformation of an incoming Event. No existing fields + * might be modified. In practice this is used to set automatically values in + * the Event upon reception (i.e. set a reception timestamp on the Event). Rules + * might require additional parameters; see the `doc` field of the existing + * rules for details. See GET /registry/enrichment-strategies for a list of + * available rules. + */ + +public enum EventEnrichmentStrategy { + METADATA("metadata_enrichment"); + + private final String metadata; + + private EventEnrichmentStrategy(String metadata) { + this.metadata = metadata; + } + + @JsonValue + public String getMetadata() { + return metadata; + } + + public static Option withName(String code) { + for (EventEnrichmentStrategy e : EventEnrichmentStrategy.values()) { + if (e.metadata.equals(code)) + return Option.apply(e); + } + return Option.empty(); + } +} diff --git a/api/src/main/java/org/zalando/nakadi/client/java/EventTypeCategory.java b/api/src/main/java/org/zalando/nakadi/client/java/enumerator/EventTypeCategory.java similarity index 86% rename from api/src/main/java/org/zalando/nakadi/client/java/EventTypeCategory.java rename to api/src/main/java/org/zalando/nakadi/client/java/enumerator/EventTypeCategory.java index acde559..1c0e768 100644 --- a/api/src/main/java/org/zalando/nakadi/client/java/EventTypeCategory.java +++ b/api/src/main/java/org/zalando/nakadi/client/java/enumerator/EventTypeCategory.java @@ -1,11 +1,9 @@ -package org.zalando.nakadi.client.java; +package org.zalando.nakadi.client.java.enumerator; -import org.zalando.nakadi.client.model.EventTypeCategory; +import scala.Option; import com.fasterxml.jackson.annotation.JsonValue; -import scala.Option; - /** * Defines the category of an EventType.
      */ diff --git a/api/src/main/java/org/zalando/nakadi/client/java/EventValidationStrategy.java b/api/src/main/java/org/zalando/nakadi/client/java/enumerator/EventValidationStrategy.java similarity index 88% rename from api/src/main/java/org/zalando/nakadi/client/java/EventValidationStrategy.java rename to api/src/main/java/org/zalando/nakadi/client/java/enumerator/EventValidationStrategy.java index f1c3a52..de99c6d 100644 --- a/api/src/main/java/org/zalando/nakadi/client/java/EventValidationStrategy.java +++ b/api/src/main/java/org/zalando/nakadi/client/java/enumerator/EventValidationStrategy.java @@ -1,11 +1,9 @@ -package org.zalando.nakadi.client.java; +package org.zalando.nakadi.client.java.enumerator; -import org.zalando.nakadi.client.model.EventValidationStrategy; +import scala.Option; import com.fasterxml.jackson.annotation.JsonValue; -import scala.Option; - /** * Defines a rule for validation of an incoming Event. Rules might require * additional parameters; see the `doc` field of the existing rules for details. diff --git a/api/src/main/java/org/zalando/nakadi/client/java/PartitionStrategy.java b/api/src/main/java/org/zalando/nakadi/client/java/enumerator/PartitionStrategy.java similarity index 89% rename from api/src/main/java/org/zalando/nakadi/client/java/PartitionStrategy.java rename to api/src/main/java/org/zalando/nakadi/client/java/enumerator/PartitionStrategy.java index d39f4fa..2d87927 100644 --- a/api/src/main/java/org/zalando/nakadi/client/java/PartitionStrategy.java +++ b/api/src/main/java/org/zalando/nakadi/client/java/enumerator/PartitionStrategy.java @@ -1,11 +1,9 @@ -package org.zalando.nakadi.client.java; +package org.zalando.nakadi.client.java.enumerator; -import org.zalando.nakadi.client.model.PartitionStrategy; +import scala.Option; import com.fasterxml.jackson.annotation.JsonValue; -import scala.Option; - /** * Defines a rule for the resolution of incoming Events into partitions. Rules * might require additional parameters; see the `doc` field of the existing diff --git a/api/src/main/java/org/zalando/nakadi/client/java/SchemaType.java b/api/src/main/java/org/zalando/nakadi/client/java/enumerator/SchemaType.java similarity index 83% rename from api/src/main/java/org/zalando/nakadi/client/java/SchemaType.java rename to api/src/main/java/org/zalando/nakadi/client/java/enumerator/SchemaType.java index 134d4cf..9bfc46b 100644 --- a/api/src/main/java/org/zalando/nakadi/client/java/SchemaType.java +++ b/api/src/main/java/org/zalando/nakadi/client/java/enumerator/SchemaType.java @@ -1,6 +1,4 @@ -package org.zalando.nakadi.client.java; - -import org.zalando.nakadi.client.model.SchemaType; +package org.zalando.nakadi.client.java.enumerator; import scala.Option; diff --git a/api/src/main/java/org/zalando/nakadi/client/java/model/BusinessEvent.java b/api/src/main/java/org/zalando/nakadi/client/java/model/BusinessEvent.java new file mode 100644 index 0000000..b818b8f --- /dev/null +++ b/api/src/main/java/org/zalando/nakadi/client/java/model/BusinessEvent.java @@ -0,0 +1,6 @@ +package org.zalando.nakadi.client.java.model; + +public interface BusinessEvent extends Event{ + + EventMetadata metadata(); +} diff --git a/api/src/main/java/org/zalando/nakadi/client/java/model/Cursor.java b/api/src/main/java/org/zalando/nakadi/client/java/model/Cursor.java new file mode 100644 index 0000000..92dd2f1 --- /dev/null +++ b/api/src/main/java/org/zalando/nakadi/client/java/model/Cursor.java @@ -0,0 +1,26 @@ +package org.zalando.nakadi.client.java.model; + +public class Cursor { + private final Integer partition; + private final Integer offset; + + /** + * @param partition + * Id of the partition pointed to by this cursor. + * @param offset + * Offset of the event being pointed to. + */ + public Cursor(Integer partition, Integer offset) { + this.partition = partition; + this.offset = offset; + } + + public Integer getPartition() { + return partition; + } + + public Integer getOffset() { + return offset; + } + +} diff --git a/api/src/main/java/org/zalando/nakadi/client/java/model/DataChangeEvent.java b/api/src/main/java/org/zalando/nakadi/client/java/model/DataChangeEvent.java new file mode 100644 index 0000000..5f4270c --- /dev/null +++ b/api/src/main/java/org/zalando/nakadi/client/java/model/DataChangeEvent.java @@ -0,0 +1,30 @@ +package org.zalando.nakadi.client.java.model; + +import org.zalando.nakadi.client.java.enumerator.DataOperation; + +public class DataChangeEvent implements DataChangeEventQualifier, Event { + private final T data; + private final String dataType; + private final DataOperation dataOperation; + + public DataChangeEvent(T data, String dataType, DataOperation dataOperation) { + this.data = data; + this.dataType = dataType; + this.dataOperation = dataOperation; + } + + public T getData() { + return data; + } + + @Override + public String getDataType() { + return dataType; + } + + @Override + public DataOperation getDataOperation() { + return dataOperation; + } + +} diff --git a/api/src/main/java/org/zalando/nakadi/client/java/model/DataChangeEventQualifier.java b/api/src/main/java/org/zalando/nakadi/client/java/model/DataChangeEventQualifier.java new file mode 100644 index 0000000..7918864 --- /dev/null +++ b/api/src/main/java/org/zalando/nakadi/client/java/model/DataChangeEventQualifier.java @@ -0,0 +1,8 @@ +package org.zalando.nakadi.client.java.model; + +import org.zalando.nakadi.client.java.enumerator.DataOperation; + +public interface DataChangeEventQualifier { + String getDataType(); + DataOperation getDataOperation(); +} diff --git a/api/src/main/java/org/zalando/nakadi/client/java/model/Event.java b/api/src/main/java/org/zalando/nakadi/client/java/model/Event.java new file mode 100644 index 0000000..c3c707c --- /dev/null +++ b/api/src/main/java/org/zalando/nakadi/client/java/model/Event.java @@ -0,0 +1,5 @@ +package org.zalando.nakadi.client.java.model; + +public interface Event { + +} diff --git a/api/src/main/java/org/zalando/nakadi/client/java/model/EventMetadata.java b/api/src/main/java/org/zalando/nakadi/client/java/model/EventMetadata.java new file mode 100644 index 0000000..cb3688a --- /dev/null +++ b/api/src/main/java/org/zalando/nakadi/client/java/model/EventMetadata.java @@ -0,0 +1,94 @@ +package org.zalando.nakadi.client.java.model; + +import java.util.List; + +/** + * Metadata for this Event. Contains commons fields for both Business and + * DataChange Events. Most are enriched by Nakadi upon reception, but they in + * general MIGHT be set by the client. + */ +public class EventMetadata { + private final String eid; + private final EventType eventType; + private final String occurredAt; + private final String receivedAt; + private final List parentEids; + private final String flowId; + private final String partition; + + /** + * + * Metadata for this Event. Contains commons fields for both Business and + * DataChange Events. Most are enriched by Nakadi upon reception, but they + * in general MIGHT be set by the client. + * + * @param eid + * Identifier of this Event. Clients are allowed to generate this + * and this SHOULD be guaranteed to be unique from the + * perspective of the producer. Consumers MIGHT use this value to + * assert uniqueness of reception of the Event. + * @param eventType + * The EventType of this Event. This is enriched by Nakadi on + * reception of the Event based on the endpoint where the + * Producer sent the Event to. If provided MUST match the + * endpoint. Failure to do so will cause rejection of the Event. + * @param occurredAt + * Timestamp of creation of the Event generated by the producer. + * @param receivedAt + * Timestamp of the reception of the Event by Nakadi. This is + * enriched upon reception of the Event. If set by the producer + * Event will be rejected. + * @param parentEids + * Event identifier of the Event that caused the generation of + * this Event. Set by the producer. + * @param flowId + * The flow-id of the producer of this Event. As this is usually + * a HTTP header, this is enriched from the header into the + * metadata by Nakadi to avoid clients having to explicitly copy + * this. + * @param metadata + * This Metadata contains common fields unrelated to Nakadi + * logic. Should be mainly enriched by the Consumer. + * + */ + public EventMetadata(String eid, EventType eventType, String occurredAt, String receivedAt, + List parentEids, String flowId, String partition) { + super(); + this.eid = eid; + this.eventType = eventType; + this.occurredAt = occurredAt; + this.receivedAt = receivedAt; + this.parentEids = parentEids; + this.flowId = flowId; + this.partition = partition; + } + + public String getEid() { + return eid; + } + + public EventType getEventType() { + return eventType; + } + + public String getOccurredAt() { + return occurredAt; + } + + public String getReceivedAt() { + return receivedAt; + } + + public List getParentEids() { + return parentEids; + } + + public String getFlowId() { + return flowId; + } + + public String getPartition() { + return partition; + } + +} diff --git a/api/src/main/java/org/zalando/nakadi/client/java/model/EventStreamBatch.java b/api/src/main/java/org/zalando/nakadi/client/java/model/EventStreamBatch.java new file mode 100644 index 0000000..caecdf7 --- /dev/null +++ b/api/src/main/java/org/zalando/nakadi/client/java/model/EventStreamBatch.java @@ -0,0 +1,54 @@ +package org.zalando.nakadi.client.java.model; + +import java.util.List; + +/** + * One chunk of events in a stream. A batch consists of an array of `Event`s + * plus a `Cursor` pointing to the offset of the last Event in the stream. The + * size of the array of Event is limited by the parameters used to initialize a + * Stream. If acting as a keep alive message (see `GET + * /event-type/{name}/events`) the events array will be omitted. Sequential + * batches might repeat the cursor if no new events arrive. + */ +public class EventStreamBatch { + private final Cursor cursor; + private final List events; + + /** + * One chunk of events in a stream. A batch consists of an array of `Event`s + * plus a `Cursor` pointing to the offset of the last Event in the stream. + * The size of the array of Event is limited by the parameters used to + * initialize a Stream. If acting as a keep alive message (see `GET + * /event-type/{name}/events`) the events array will be omitted. Sequential + * batches might repeat the cursor if no new events arrive. + * + * @param cursor + * The cursor point to an event in the stream. + * @param events + * The Event definition will be externalized in future versions + * of this document. A basic payload of an Event. The actual + * schema is dependent on the information configured for the + * EventType, as is its enforcement (see POST /event-types). + * Setting of metadata properties are dependent on the configured + * enrichment as well. For explanation on default configurations + * of validation and enrichment, see documentation of + * `EventType.type`. For concrete examples of what will be + * enforced by Nakadi see the objects sEvent and DataChangeEvent + * below. + */ + + public EventStreamBatch(Cursor cursor, List events) { + super(); + this.cursor = cursor; + this.events = events; + } + + public Cursor getCursor() { + return cursor; + } + + public List getEvents() { + return events; + } + +} diff --git a/api/src/main/java/org/zalando/nakadi/client/java/model/EventType.java b/api/src/main/java/org/zalando/nakadi/client/java/model/EventType.java new file mode 100644 index 0000000..a59600d --- /dev/null +++ b/api/src/main/java/org/zalando/nakadi/client/java/model/EventType.java @@ -0,0 +1,160 @@ +package org.zalando.nakadi.client.java.model; + +import java.util.List; + +import org.zalando.nakadi.client.java.enumerator.EventEnrichmentStrategy; +import org.zalando.nakadi.client.java.enumerator.EventTypeCategory; +import org.zalando.nakadi.client.java.enumerator.EventValidationStrategy; +import org.zalando.nakadi.client.java.enumerator.PartitionStrategy; + +/** + * An event type defines the schema and its runtime properties. + * + * + */ +public class EventType { + private final String name; + private final String owningApplication; + private final EventTypeCategory category; + private final List validationStrategies; + private final List enrichmentStrategies; + private final PartitionStrategy partitionStrategy; + private final String schema; + private final List dataKeyFields; + private final List partitionKeyFields; + private final EventTypeStatistics statistics; + + /** + * An event type defines the schema and its runtime properties. + * + * @param name + * Name of this EventType. Encodes the owner/responsible for this + * EventType. The name for the EventType SHOULD follow the + * pattern, but is not enforced + * 'stups_owning_application.event-type', for example + * 'gizig.price-change'. The components of the name are: * + * Organization: the organizational unit where the team is + * located; can be omitted. * Team name: name of the team + * responsible for owning application; can be omitted. * Owning + * application: SHOULD match the field owning_application; + * indicates * EventType name: name of this EventType; SHOULD end + * in ChangeEvent for DataChangeEvents; MUST be in the past tense + * for BusinessEvents. (TBD: how to deal with organizational + * changes? Should be reflected on the name of the EventType? + * Isn't it better to omit [organization:team] completely?) + * @param owningApplication + * Indicator of the Application owning this `EventType`. + * @param category + * Defines the category of this EventType. The value set will + * influence, if not set otherwise, the default set of + * validation-strategies, enrichment-strategies, and the + * effective_schema in the following way: - `undefined`: No + * predefined changes apply. `effective_schema` is exactly the + * same as the `EventTypeSchema`. Default validation_strategy for + * this `EventType` is `[{name: 'schema-validation'}]`. - `data`: + * Events of this category will be DataChangeEvents. + * `effective_schema` contains `metadata`, and adds fields + * `data_op` and `data_type`. The passed EventTypeSchema defines + * the schema of `data`. Default validation_strategy for this + * `EventType` is `[{name: 'datachange-schema-validation'}]`. - + * `business`: Events of this category will be BusinessEvents. + * `effective_schema` contains `metadata` and any additionally + * defined properties passed in the `EventTypeSchema` directly on + * top level of the Event. If name conflicts arise, creation of + * this EventType will be rejected. Default validation_strategy + * for this `EventType` is `[{name: 'schema-validation'}]`. + * @param validationStrategies + * Determines the validation that has to be executed upon + * reception of Events of this type. Events are rejected if any + * of the rules fail (see details of Problem response on the + * Event publishing methods). Rule evaluation order is the same + * as in this array. If not explicitly set, default value will + * respect the definition of the `EventType.category`. + * @param enrichmentStrategies + * Determines the enrichment to be performed on an Event upon + * reception. Enrichment is performed once upon reception (and + * after validation) of an Event and is only possible on fields + * that are not defined on the incoming Event. See documentation + * for the write operation for details on behaviour in case of + * unsuccessful enrichment. + * @param partitionStrategy + * Determines how the assignment of the event to a Partition + * should be handled. + * @param schema + * The schema for this EventType. This is expected to be a json + * schema in yaml format (other formats might be added in the + * future). + * @param dataKeyFields + * Indicators of the path of the properties that constitute the + * primary key (identifier) of the data within this Event. If set + * MUST be a valid required field as defined in the schema. (TBD + * should be required? Is applicable only to both Business and + * DataChange Events?) + * @param partitioningKeyFields + * Indicator of the field used for guaranteeing the ordering of + * Events of this type (used by the PartitionResolutionStrategy). + * If set MUST be a valid required field as defined in the + * schema. + * @param statistics + * Statistics of this EventType used for optimization purposes. + * Internal use of these values might change over time. (TBD: + * measured statistics). + * + */ + public EventType(String name, String owningApplication, EventTypeCategory category, + List validationStrategies, List enrichmentStrategies, + PartitionStrategy partitionStrategy, String schema, List dataKeyFields, + List partitionKeyFields, EventTypeStatistics statistics) { + this.name = name; + this.owningApplication = owningApplication; + this.category = category; + this.validationStrategies = validationStrategies; + this.enrichmentStrategies = enrichmentStrategies; + this.partitionStrategy = partitionStrategy; + this.schema = schema; + this.dataKeyFields = dataKeyFields; + this.partitionKeyFields = partitionKeyFields; + this.statistics = statistics; + } + + public String getName() { + return name; + } + + public String getOwningApplication() { + return owningApplication; + } + + public EventTypeCategory getCategory() { + return category; + } + + public List getValidationStrategies() { + return validationStrategies; + } + + public List getEnrichmentStrategies() { + return enrichmentStrategies; + } + + public PartitionStrategy getPartitionStrategy() { + return partitionStrategy; + } + + public String getSchema() { + return schema; + } + + public List getDataKeyFields() { + return dataKeyFields; + } + + public List getPartitionKeyFields() { + return partitionKeyFields; + } + + public EventTypeStatistics getStatistics() { + return statistics; + } + +} diff --git a/api/src/main/java/org/zalando/nakadi/client/java/model/EventTypeSchema.java b/api/src/main/java/org/zalando/nakadi/client/java/model/EventTypeSchema.java new file mode 100644 index 0000000..15db5a2 --- /dev/null +++ b/api/src/main/java/org/zalando/nakadi/client/java/model/EventTypeSchema.java @@ -0,0 +1,30 @@ +package org.zalando.nakadi.client.java.model; + +import org.zalando.nakadi.client.java.enumerator.SchemaType; + +/** + * The schema for an EventType, expected to be a json schema in yaml + * format (other formats might be added in the future). + * @param type The type of schema definition (avro, json-schema, etc). + * @param schema The schema as string in the syntax defined in the field type. + * Failure to respect the syntax will fail any operation on an EventType. + */ + +public class EventTypeSchema { + private final SchemaType type; + private final String schema; + + public EventTypeSchema(SchemaType type, String schema) { + this.type = type; + this.schema = schema; + } + + public SchemaType getType() { + return type; + } + + public String getSchema() { + return schema; + } + +} diff --git a/api/src/main/java/org/zalando/nakadi/client/java/model/EventTypeStatistics.java b/api/src/main/java/org/zalando/nakadi/client/java/model/EventTypeStatistics.java new file mode 100644 index 0000000..ba56e12 --- /dev/null +++ b/api/src/main/java/org/zalando/nakadi/client/java/model/EventTypeStatistics.java @@ -0,0 +1,32 @@ +package org.zalando.nakadi.client.java.model; + +public class EventTypeStatistics { + private final Integer expectedWriteRate; + private final Integer messageSize; + private final Integer readParallelism; + private final Integer writeParallelism; + + + + public EventTypeStatistics(Integer expectedWriteRate, Integer messageSize, Integer readParallelism, Integer writeParallelism) { + this.expectedWriteRate = expectedWriteRate; + this.messageSize = messageSize; + this.readParallelism = readParallelism; + this.writeParallelism = writeParallelism; + } + public Integer getExpectedWriteRate() { + return expectedWriteRate; + } + public Integer getMessageSize() { + return messageSize; + } + public Integer getReadParallelism() { + return readParallelism; + } + public Integer getWriteParallelism() { + return writeParallelism; + } + + + +} diff --git a/api/src/main/java/org/zalando/nakadi/client/java/model/Metrics.java b/api/src/main/java/org/zalando/nakadi/client/java/model/Metrics.java new file mode 100644 index 0000000..bb1fb1c --- /dev/null +++ b/api/src/main/java/org/zalando/nakadi/client/java/model/Metrics.java @@ -0,0 +1,17 @@ +package org.zalando.nakadi.client.java.model; + +import java.util.Map; + +public class Metrics { + private final Map metrics; + + public Metrics(Map metrics) { + this.metrics = metrics; + } + + public Map getMetrics() { + return metrics; + } + + +} diff --git a/api/src/main/java/org/zalando/nakadi/client/java/model/Partition.java b/api/src/main/java/org/zalando/nakadi/client/java/model/Partition.java new file mode 100644 index 0000000..b99abd2 --- /dev/null +++ b/api/src/main/java/org/zalando/nakadi/client/java/model/Partition.java @@ -0,0 +1,51 @@ +package org.zalando.nakadi.client.java.model; + +/** + * Partition information. Can be helpful when trying to start a stream using an + * unmanaged API. This information is not related to the state of the consumer + * clients. + */ +public class Partition { + private final Integer partition; + private final Integer oldestAvailableOffset; + private final Integer newestAvailableOffset; + + /** + * Partition information. Can be helpful when trying to start a stream using + * an unmanaged API. This information is not related to the state of the + * consumer clients. + * + * @param partition + * The partition's id. + * @param oldestAvailableOffset + * An offset of the oldest available Event in that partition. + * This value will be changing upon removal of Events from the + * partition by the background archiving/cleanup mechanism. + * @param newestAvailableOffset + * An offset of the newest available Event in that partition. + * This value will be changing upon reception of new events for + * this partition by Nakadi. This value can be used to construct + * a cursor when opening streams (see `GET + * /event-type/{name}/events` for details). Might assume the + * special name BEGIN, meaning a pointer to the offset of the + * oldest available event in the partition. + */ + public Partition(Integer partition, Integer oldestAvailableOffset, Integer newestAvailableOffset) { + this.partition = partition; + this.oldestAvailableOffset = oldestAvailableOffset; + this.newestAvailableOffset = newestAvailableOffset; + } + + public Integer getPartition() { + return partition; + } + + public Integer getOldestAvailableOffset() { + return oldestAvailableOffset; + } + + public Integer getNewestAvailableOffset() { + return newestAvailableOffset; + } + +} diff --git a/api/src/main/java/org/zalando/nakadi/client/java/model/Problem.java b/api/src/main/java/org/zalando/nakadi/client/java/model/Problem.java new file mode 100644 index 0000000..764fc12 --- /dev/null +++ b/api/src/main/java/org/zalando/nakadi/client/java/model/Problem.java @@ -0,0 +1,42 @@ +package org.zalando.nakadi.client.java.model; + +public class Problem { + private final String problemType; + private final String title; + private final Integer status; + private final String detail; + private final String instance; + + /** + * @ param problemType An absolute URI that identifies the problem type. When dereferenced, it SHOULD provide human-readable API documentation for the problem type (e.g., using HTML). This Problem object is the same as provided by https://github.com/zalando/problem + * @ param title A short, summary of the problem type. Written in English and readable for engineers (usually not suited for non technical stakeholders and not localized) + * @ param status The HTTP status code generated by the origin server for this occurrence of the problem. + * @ param detail A human readable explanation specific to this occurrence of the problem. + * @ param instance An absolute URI that identifies the specific occurrence of the problem. It may or may not yield further information if dereferenced. + */ + public Problem(String problemType, String title, Integer status, String detail, String instance) { + this.problemType = problemType; + this.title = title; + this.status = status; + this.detail = detail; + this.instance = instance; + } + public String getProblemType() { + return problemType; + } + public String getTitle() { + return title; + } + public Integer getStatus() { + return status; + } + public String getDetail() { + return detail; + } + public String getInstance() { + return instance; + } + + + +} diff --git a/api/src/main/scala/org/zalando/nakadi/client/ClientError.scala b/api/src/main/scala/org/zalando/nakadi/client/ClientError.scala new file mode 100644 index 0000000..903fb83 --- /dev/null +++ b/api/src/main/scala/org/zalando/nakadi/client/ClientError.scala @@ -0,0 +1,3 @@ +package org.zalando.nakadi.client + +case class ClientError(msg: String, status: Option[Int]) \ No newline at end of file diff --git a/api/src/main/scala/org/zalando/nakadi/client/Listener.scala b/api/src/main/scala/org/zalando/nakadi/client/Listener.scala deleted file mode 100644 index adca3a3..0000000 --- a/api/src/main/scala/org/zalando/nakadi/client/Listener.scala +++ /dev/null @@ -1,23 +0,0 @@ -package org.zalando.nakadi.client - -import org.zalando.nakadi.client.scala.Cursor - -case class ClientError(msg: String, status: Option[Int]) - - -case class StreamParameters(cursor: Option[Cursor] = None, // - batchLimit: Option[Integer] = None, - streamLimit: Option[Integer] = None, - batchFlushTimeout: Option[Integer] = None, - streamTimeout: Option[Integer] = None, - streamKeepAliveLimit: Option[Integer] = None, - flowId: Option[String] = None) { -} - -trait Listener[T] { - def id: String - def onSubscribed(): Unit - def onUnsubscribed(): Unit - def onReceive(sourceUrl: String, cursor: Cursor, event: Seq[T]): Unit - def onError(sourceUrl: String, cursor: Cursor, error: ClientError): Unit -} \ No newline at end of file diff --git a/api/src/main/scala/org/zalando/nakadi/client/Serialization.scala b/api/src/main/scala/org/zalando/nakadi/client/Serialization.scala index 111ae8b..463bdac 100644 --- a/api/src/main/scala/org/zalando/nakadi/client/Serialization.scala +++ b/api/src/main/scala/org/zalando/nakadi/client/Serialization.scala @@ -1,6 +1,5 @@ package org.zalando.nakadi.client - /** * */ @@ -11,7 +10,7 @@ trait Deserializer[T] { /** * */ -trait Serializer[T] { +trait Serializer[T] { def to(from: T): String } diff --git a/api/src/main/scala/org/zalando/nakadi/client/StreamCursor.scala b/api/src/main/scala/org/zalando/nakadi/client/StreamCursor.scala new file mode 100644 index 0000000..397417b --- /dev/null +++ b/api/src/main/scala/org/zalando/nakadi/client/StreamCursor.scala @@ -0,0 +1,9 @@ +package org.zalando.nakadi.client + +/** + * @param partition Id of the partition pointed to by this cursor. + * @param offset Offset of the event being pointed to. + */ +case class Cursor( + partition: Integer, + offset: Integer) diff --git a/api/src/main/scala/org/zalando/nakadi/client/scala/Client.scala b/api/src/main/scala/org/zalando/nakadi/client/scala/Client.scala index c0e26dc..18bf5fa 100644 --- a/api/src/main/scala/org/zalando/nakadi/client/scala/Client.scala +++ b/api/src/main/scala/org/zalando/nakadi/client/scala/Client.scala @@ -1,12 +1,10 @@ package org.zalando.nakadi.client.scala import scala.concurrent.Future - -import org.zalando.nakadi.client.ClientError -import org.zalando.nakadi.client.Listener import org.zalando.nakadi.client.Deserializer import org.zalando.nakadi.client.Serializer -import org.zalando.nakadi.client.StreamParameters +import org.zalando.nakadi.client.scala.model._ +import org.zalando.nakadi.client._ @@ -82,16 +80,6 @@ trait Client { */ def publishEvents[T <: Event](eventTypeName: String, events: Seq[T])(implicit ser: Serializer[Seq[T]]): Future[Option[ClientError]] - /** - * Publishes multiple Events for the given EventType. - * {{{ - * curl --request POST -d @fileWithEvent /event-types/{name}/events - * }}} - * @param eventTypeName - Name of the EventType - * @param event - Event to publish - */ - def publishEvents[T <: Event](eventTypeName: String, events: java.util.List[T])(implicit ser: Serializer[T]): Future[Option[ClientError]] - /** * Publishes a single Events for the given EventType. * {{{ @@ -151,14 +139,14 @@ trait Client { * @parameters - Parameters for the streaming of events. * @listener - Listener to pass the event to when it is received. */ - def subscribe[T](eventTypeName: String, parameters: StreamParameters, listener: Listener[T])(implicit des: Deserializer[T]): Future[Option[ClientError]] + def subscribe[T <: Event](eventTypeName: String, parameters: StreamParameters, listener: Listener[T])(implicit des: Deserializer[T]): Future[Option[ClientError]] /** * Removes the subscription of a listener, to stop streaming events from a partition. * * @eventType - Name of the EventType. * @listener - Listener to unsubscribe from the streaming events. */ - def unsubscribe[T](eventTypeName: String, listener: Listener[T]): Future[Option[ClientError]] + def unsubscribe[T <: Event](eventTypeName: String, listener: Listener[T]): Future[Option[ClientError]] } diff --git a/api/src/main/scala/org/zalando/nakadi/client/scala/Listener.scala b/api/src/main/scala/org/zalando/nakadi/client/scala/Listener.scala new file mode 100644 index 0000000..e04a65a --- /dev/null +++ b/api/src/main/scala/org/zalando/nakadi/client/scala/Listener.scala @@ -0,0 +1,16 @@ +package org.zalando.nakadi.client.scala + +import org.zalando.nakadi.client.scala.model.Event +import org.zalando.nakadi.client.ClientError +import org.zalando.nakadi.client.Cursor + + + + +trait Listener[T <: Event] { + def id: String + def onSubscribed(): Unit + def onUnsubscribed(): Unit + def onReceive(sourceUrl: String, cursor: Cursor, event: Seq[T]): Unit + def onError(sourceUrl: String, cursor: Cursor, error: ClientError): Unit +} \ No newline at end of file diff --git a/api/src/main/scala/org/zalando/nakadi/client/scala/StreamParameters.scala b/api/src/main/scala/org/zalando/nakadi/client/scala/StreamParameters.scala new file mode 100644 index 0000000..4a5e7b4 --- /dev/null +++ b/api/src/main/scala/org/zalando/nakadi/client/scala/StreamParameters.scala @@ -0,0 +1,12 @@ +package org.zalando.nakadi.client.scala + +import org.zalando.nakadi.client.scala.model.Cursor + +case class StreamParameters( + cursor: Option[Cursor]=None, + batchLimit: Option[Integer] = None, + streamLimit: Option[Integer] = None, + batchFlushTimeout: Option[Integer] = None, + streamTimeout: Option[Integer] = None, + streamKeepAliveLimit: Option[Integer] = None, + flowId: Option[String] = None) \ No newline at end of file diff --git a/api/src/main/scala/org/zalando/nakadi/client/scala/Model.scala b/api/src/main/scala/org/zalando/nakadi/client/scala/model/Model.scala similarity index 99% rename from api/src/main/scala/org/zalando/nakadi/client/scala/Model.scala rename to api/src/main/scala/org/zalando/nakadi/client/scala/model/Model.scala index 48bb70c..979139b 100644 --- a/api/src/main/scala/org/zalando/nakadi/client/scala/Model.scala +++ b/api/src/main/scala/org/zalando/nakadi/client/scala/model/Model.scala @@ -1,4 +1,4 @@ -package org.zalando.nakadi.client.scala +package org.zalando.nakadi.client.scala.model import com.fasterxml.jackson.annotation.JsonProperty import scala.collection.JavaConversions._ @@ -28,6 +28,13 @@ import com.fasterxml.jackson.core.`type`.TypeReference trait Event { } +/** + * @param partition Id of the partition pointed to by this cursor. + * @param offset Offset of the event being pointed to. + */ +case class Cursor( + partition: Integer, + offset: Integer) /** * @@ -107,13 +114,6 @@ case class Partition( partition: Integer, oldestAvailableOffset: Integer, newestAvailableOffset: Integer) -/** - * @param partition Id of the partition pointed to by this cursor. - * @param offset Offset of the event being pointed to. - */ -case class Cursor( - partition: Integer, - offset: Option[Integer]) /** * One chunk of events in a stream. A batch consists of an array of `Event`s plus a `Cursor` pointing to the offset of the last Event in the stream. The size of the array of Event is limited by the parameters used to initialize a Stream. If acting as a keep alive message (see `GET /event-type/{name}/events`) the events array will be omitted. Sequential batches might repeat the cursor if no new events arrive. @@ -123,7 +123,7 @@ case class Cursor( */ case class EventStreamBatch[T <: Event]( cursor: Cursor, - events: Seq[T]) + events: Option[Seq[T]]) /** * An event type defines the schema and its runtime properties. diff --git a/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java b/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java index ecec401..bf55b7e 100644 --- a/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java +++ b/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java @@ -7,16 +7,11 @@ import scala.collection.JavaConverters; import org.zalando.nakadi.client.Deserializer; -import org.zalando.nakadi.client.Listener; +import org.zalando.nakadi.client.java.Listener; import org.zalando.nakadi.client.Serializer; -import org.zalando.nakadi.client.StreamParameters; -import org.zalando.nakadi.client.java.model.Event; -import org.zalando.nakadi.client.java.model.EventEnrichmentStrategy; -import org.zalando.nakadi.client.java.model.EventType; -import org.zalando.nakadi.client.java.model.EventValidationStrategy; -import org.zalando.nakadi.client.java.model.Metrics; -import org.zalando.nakadi.client.java.model.Partition; -import org.zalando.nakadi.client.java.model.PartitionStrategy; +import org.zalando.nakadi.client.java.StreamParameters; +import org.zalando.nakadi.client.java.model.*; +import org.zalando.nakadi.client.java.enumerator.*; import org.zalando.nakadi.client.utils.FutureConversions; import org.zalando.nakadi.client.utils.Serialization; @@ -26,111 +21,128 @@ public class ClientImpl implements Client { private final org.zalando.nakadi.client.scala.Client client; - //Deserializers - private final Deserializer metricsDeserializer =Serialization.metricsDeserializer(); - private final Deserializer partitionDeserializer = Serialization.partitionDeserializer(); - //Seq Deserializers - private final Deserializer> seqOfEventTypeDeserializer =Serialization.seqOfEventTypeDeserializer(); - private final Deserializer> seqOfPartitionDeserializer =Serialization.seqOfPartitionDeserializer(); - private final Deserializer> seqOfEventValidationStrategy =Serialization.seqOfEventValidationStrategy(); - private final Deserializer> seqOfEventEnrichmentStrategy =Serialization.seqOfEventEnrichmentStrategy(); - private final Deserializer> seqOfPartitionStrategy =Serialization.seqOfPartitionStrategy(); - //Serializers - private final Serializer eventTypeSerializer =Serialization.defaultSerializer(); - private final Deserializer eventTypeDeserializer = Serialization.eventTypeDeserializer(); +// //Deserializers +// private final Deserializer metricsDeserializer =Serialization.metricsDeserializer(); +// private final Deserializer partitionDeserializer = Serialization.partitionDeserializer(); +// //Seq Deserializers +// private final Deserializer> seqOfEventTypeDeserializer =Serialization.seqOfEventTypeDeserializer(); +// private final Deserializer> seqOfPartitionDeserializer =Serialization.seqOfPartitionDeserializer(); +// private final Deserializer> seqOfEventValidationStrategy =Serialization.seqOfEventValidationStrategy(); +// private final Deserializer> seqOfEventEnrichmentStrategy =Serialization.seqOfEventEnrichmentStrategy(); +// private final Deserializer> seqOfPartitionStrategy =Serialization.seqOfPartitionStrategy(); +// //Serializers +// private final Serializer eventTypeSerializer =Serialization.defaultSerializer(); +// private final Deserializer eventTypeDeserializer = Serialization.eventTypeDeserializer(); public ClientImpl(org.zalando.nakadi.client.scala.Client client) { this.client = client; } +@Override +public Future> getMetrics() { + + return null; +} + +@Override +public Future>> getEventTypes() { + // TODO Auto-generated method stub + return null; +} + +@Override +public Future createEventType(EventType eventType) { + // TODO Auto-generated method stub + return null; +} + +@Override +public Future> getEventType(String eventTypeName) { + // TODO Auto-generated method stub + return null; +} + +@Override +public Future updateEventType(String eventTypeName, EventType eventType) { + // TODO Auto-generated method stub + return null; +} + +@Override +public Future deleteEventType(String eventTypeName) { + // TODO Auto-generated method stub + return null; +} + +@Override +public Future publishEvent(String eventTypeName, T event, Serializer serializer) { + // TODO Auto-generated method stub + return null; +} + +@Override +public Future publishEvent(String eventTypeName, T event) { + // TODO Auto-generated method stub + return null; +} + +@Override +public Future publishEvents(String eventTypeName, List events, Serializer serializer) { + // TODO Auto-generated method stub + return null; +} + +@Override +public Future publishEvents(String eventTypeName, List events) { + // TODO Auto-generated method stub + return null; +} + +@Override +public Future>> getPartitions(String eventTypeName) { + // TODO Auto-generated method stub + return null; +} + +@Override +public Future>> getValidationStrategies() { + // TODO Auto-generated method stub + return null; +} + +@Override +public Future>> getEnrichmentStrategies() { + // TODO Auto-generated method stub + return null; +} + +@Override +public Future>> getPartitioningStrategies() { + // TODO Auto-generated method stub + return null; +} + +@Override +public Future stop() { + // TODO Auto-generated method stub + return null; +} + +@Override +public Future subscribe(String eventTypeName, + org.zalando.nakadi.client.scala.StreamParameters parameters, Listener listener, Deserializer deserializer) { + // TODO Auto-generated method stub + return null; +} + +@Override +public Future unsubscribe(String eventTypeName, Listener listener) { + // TODO Auto-generated method stub + return null; +} + + - @Override - public Future> getMetrics() { - return FutureConversions.fromOptionOfEither2Optional(client.getMetrics(metricsDeserializer)); - } - - @Override - public Future>> getEventTypes() { - return FutureConversions.fromSeqOfOptionalEither2OptionalList(client.getEventTypes(seqOfEventTypeDeserializer)); - } - - @Override - public Future createEventType(EventType eventType) { - - return FutureConversions.fromOptional2Future(client.createEventType(eventType,eventTypeSerializer)); - } - - @Override - public Future> getEventType(String eventTypeName) { - return FutureConversions.fromOptionOfEither2Optional(client.getEventType(eventTypeName,eventTypeDeserializer)); - } - - @Override - public Future updateEventType(String eventTypeName,EventType eventType) { - return FutureConversions.fromOptional2Future(client.updateEventType(eventTypeName,eventType,eventTypeSerializer)); - } - - @Override - public Future deleteEventType(String eventTypeName) { - return FutureConversions.fromOptional2Future(client.deleteEventType(eventTypeName)); - } - - @Override - public Future publishEvent(String eventTypeName, T event, Serializer serializer) { - return FutureConversions.fromOptional2Future(client.publishEvent(eventTypeName,event,serializer)); - } - - @Override - public Future publishEvent(String eventTypeName, T event) { - Serializer serializer =Serialization.defaultSerializer(); - return FutureConversions.fromOptional2Future(client.publishEvent(eventTypeName,event,serializer)); - } - - @Override - public Future publishEvents(String eventTypeName, List events, Serializer serializer) { - return FutureConversions.fromOptional2Future(client.publishEvents(eventTypeName,events,serializer)); - } - - @Override - public Future publishEvents(String eventTypeName, List events) { - Serializer serializer =Serialization.defaultSerializer(); - return FutureConversions.fromOptional2Future(client.publishEvents(eventTypeName,events,serializer)); - } - - @Override - public Future>> getPartitions(String eventTypeName) { - return FutureConversions.fromSeqOfOptionalEither2OptionalList(client.getPartitions(eventTypeName,seqOfPartitionDeserializer)); - } - - @Override - public Future>> getValidationStrategies() { - return FutureConversions.fromSeqOfOptionalEither2OptionalList(client.getValidationStrategies(seqOfEventValidationStrategy)); - } - - @Override - public Future>> getEnrichmentStrategies() { - return FutureConversions.fromSeqOfOptionalEither2OptionalList(client.getEnrichmentStrategies(seqOfEventEnrichmentStrategy)); - } - - @Override - public Future>> getPartitioningStrategies() { - return FutureConversions.fromSeqOfOptionalEither2OptionalList(client.getPartitioningStrategies(seqOfPartitionStrategy)); - } - - @Override - public Future stop() { - return FutureConversions.fromOptional2Future(client.stop()); - } - @Override - public Future subscribe(String eventTypeName, StreamParameters parameters, Listener listener, Deserializer deserializer) { - return FutureConversions.fromOptional2Future(client.subscribe(eventTypeName,parameters,listener,deserializer)); - } - - @Override - public Future unsubscribe(String eventTypeName, Listener listener) { - return FutureConversions.fromOptional2Future(client.unsubscribe(eventTypeName,listener)); - } - } \ No newline at end of file diff --git a/client/src/main/scala/org/zalando/nakadi/client/actor/EventConsumingActor.scala b/client/src/main/scala/org/zalando/nakadi/client/actor/EventConsumingActor.scala index d4298ff..ba996c8 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/actor/EventConsumingActor.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/actor/EventConsumingActor.scala @@ -6,17 +6,12 @@ import akka.stream.actor.ActorSubscriber import akka.stream.actor.ActorSubscriberMessage.OnNext import akka.stream.actor.RequestStrategy import akka.util.ByteString -import org.zalando.nakadi.client.Listener import org.zalando.nakadi.client.Deserializer -import org.zalando.nakadi.client.model.Cursor -import scala.util.Try -import scala.util.Success -import scala.util.Failure import org.zalando.nakadi.client.ClientError -import org.zalando.nakadi.client.model.JacksonJsonMarshaller -import org.zalando.nakadi.client.model.EventStreamBatch -import org.zalando.nakadi.client.model.Event +import org.zalando.nakadi.client.scala.model.JacksonJsonMarshaller import com.fasterxml.jackson.core.`type`.TypeReference +import org.zalando.nakadi.client.scala.model._ +import org.zalando.nakadi.client.scala.Listener object EventConsumer { @@ -27,7 +22,7 @@ object EventConsumer { case class MyEventExample(orderNumber: String) extends Event -class EventConsumer[T](url: String, listener: Listener[T], des: Deserializer[T]) extends Actor with ActorLogging with ActorSubscriber { +class EventConsumer[T <: Event](url: String, listener: Listener[T], des: Deserializer[T]) extends Actor with ActorLogging with ActorSubscriber { import EventConsumer._ var count = 0 @@ -59,7 +54,7 @@ class EventConsumer[T](url: String, listener: Listener[T], des: Deserializer[T]) trait MessageSplitter { - def deserializeMsg[T<:Event](msg: String)(implicit des: Deserializer[EventStreamBatch[T]]): EventStreamBatch[T] = des.from(msg) + def deserializeMsg[T <: Event](msg: String)(implicit des: Deserializer[EventStreamBatch[T]]): EventStreamBatch[T] = des.from(msg) } diff --git a/client/src/main/scala/org/zalando/nakadi/client/actor/EventReceivingActor.scala b/client/src/main/scala/org/zalando/nakadi/client/actor/EventReceivingActor.scala index 3af08b8..d1730c6 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/actor/EventReceivingActor.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/actor/EventReceivingActor.scala @@ -5,7 +5,7 @@ import akka.actor.Actor import akka.stream.actor.ActorPublisher import EventReceivingActor._ import scala.collection.mutable.Queue -import org.zalando.nakadi.client.model.Cursor +import org.zalando.nakadi.client.scala.model.Cursor diff --git a/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala index 7226670..f602c0e 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala @@ -12,17 +12,15 @@ import akka.http.scaladsl.model.MediaTypes.`application/json` import akka.http.scaladsl.model.StatusCodes import akka.http.scaladsl.model.headers.{ Accept, RawHeader } import akka.http.scaladsl.unmarshalling.Unmarshal -import org.zalando.nakadi.client.StreamParameters -import org.zalando.nakadi.client.Serializer -import org.zalando.nakadi.client.scala._ -import org.zalando.nakadi.client.Deserializer -import org.zalando.nakadi.client.Listener -import org.zalando.nakadi.client.ClientError -import akka.dispatch.sysmsg.Failed import scala.concurrent.Await import scala.concurrent.duration.Duration -import org.zalando.nakadi.client.model.JacksonJsonMarshaller - +import org.zalando.nakadi.client.scala.model.JacksonJsonMarshaller +import org.zalando.nakadi.client.Deserializer +import org.zalando.nakadi.client.Serializer +import org.zalando.nakadi.client.scala.model._ +import org.zalando.nakadi.client.ClientError + + private[client] class ClientImpl(connection: Connection, charSet: String = "UTF-8") extends Client with HttpFactory { implicit val materializer = connection.materializer @@ -56,11 +54,6 @@ private[client] class ClientImpl(connection: Connection, charSet: String = "UTF- def publishEvents[T <: Event](eventTypeName: String, events: Seq[T])(implicit ser: Serializer[Seq[T]]): Future[Option[ClientError]] = { logFutureOption(connection.post(URI_EVENTS_OF_EVENT_TYPE.format(eventTypeName), events).flatMap(in => mapToOption(in))) } - def publishEvents[T <: Event](eventTypeName: String, events: java.util.List[T])(implicit ser: Serializer[T]): Future[Option[ClientError]] = { - import scala.collection.JavaConversions._ - import JacksonJsonMarshaller._ - publishEvents(eventTypeName, events) - } def publishEvent[T <: Event](name: String, event: T)(implicit ser: Serializer[T]): Future[Option[ClientError]] = { logFutureOption(connection.post(URI_EVENTS_OF_EVENT_TYPE.format(name), event).flatMap(in => mapToOption(in))) @@ -70,15 +63,15 @@ private[client] class ClientImpl(connection: Connection, charSet: String = "UTF- logFutureEither(connection.get(URI_PARTITIONS_BY_EVENT_TYPE.format(name)).flatMap(in => mapToEither(in))) } - def getValidationStrategies()(implicit des: Deserializer[Seq[EventValidationStrategy.Value]]): Future[Either[ClientError, Option[Seq[EventValidationStrategy]]]] = { + def getValidationStrategies()(implicit des: Deserializer[Seq[EventValidationStrategy.Value]]): Future[Either[ClientError, Option[Seq[EventValidationStrategy.Value]]]] = { logFutureEither(connection.get(URI_VALIDATION_STRATEGIES).flatMap(mapToEither(_))) } - def getEnrichmentStrategies()(implicit des: Deserializer[Seq[EventEnrichmentStrategy]]): Future[Either[ClientError, Option[Seq[EventEnrichmentStrategy]]]] = { + def getEnrichmentStrategies()(implicit des: Deserializer[Seq[EventEnrichmentStrategy.Value]]): Future[Either[ClientError, Option[Seq[EventEnrichmentStrategy.Value]]]] = { logFutureEither(connection.get(URI_ENRICHMENT_STRATEGIES).flatMap(mapToEither(_))) } - def getPartitioningStrategies()(implicit des: Deserializer[Seq[PartitionStrategy]]): Future[Either[ClientError, Option[Seq[PartitionStrategy]]]] = + def getPartitioningStrategies()(implicit des: Deserializer[Seq[PartitionStrategy.Value]]): Future[Either[ClientError, Option[Seq[PartitionStrategy.Value]]]] = logFutureEither(connection.get(URI_PARTITIONING_STRATEGIES).flatMap(mapToEither(_))) def stop(): Future[Option[ClientError]] = { @@ -86,7 +79,7 @@ private[client] class ClientImpl(connection: Connection, charSet: String = "UTF- Future.successful(None) } - def subscribe[T](eventType: String, params: StreamParameters, listener: Listener[T])(implicit des: Deserializer[T]): Future[Option[ClientError]] = { + def subscribe[T <: Event](eventType: String, params: StreamParameters, listener: Listener[T])(implicit des: Deserializer[T]): Future[Option[ClientError]] = { (eventType, params, listener) match { case (_, _, listener) if listener == null => @@ -98,20 +91,19 @@ private[client] class ClientImpl(connection: Connection, charSet: String = "UTF- Future.successful(Option(ClientError("Eventype may not be empty(null)!", None))) case (eventType, StreamParameters(cursor, _, _, _, _, _, _), listener) if Option(eventType).isDefined => - val url = URI_EVENTS_OF_EVENT_TYPE.format(eventType) - + val url = URI_EVENTS_OF_EVENT_TYPE.format(eventType) val request = withHttpRequest(url, HttpMethods.GET, RawHeader("Accept", "application/x-json-stream") :: withHeaders(Option(params)), //Headers - connection.tokenProvider(),Option(params)) + connection.tokenProvider(), Option(params)) - logger.debug("Subscribing listener {} - cursor {} - parameters {} - eventType {} ", listener.id, cursor,params, eventType) + logger.debug("Subscribing listener {} - cursor {} - parameters {} - eventType {} ", listener.id, cursor, params, eventType) connection.subscribe(url, request, listener) Future.successful(None) } } - - def unsubscribe[T](eventType: String, listener: Listener[T]): Future[Option[ClientError]] = ??? + + def unsubscribe[T <: Event](eventType: String, listener: Listener[T]): Future[Option[ClientError]] = ??? //#################### //# HELPER METHODS # diff --git a/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala index 3e70ebb..c94b1fa 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala @@ -8,7 +8,6 @@ import scala.concurrent.Future import scala.concurrent.duration.DurationInt import org.slf4j.LoggerFactory import org.zalando.nakadi.client.actor.EventConsumer -import org.zalando.nakadi.client.model.Cursor import com.typesafe.scalalogging.Logger import akka.actor.{ Props, ActorSystem, ActorLogging, _ } import akka.http.scaladsl.{ Http, HttpsConnectionContext } @@ -21,7 +20,7 @@ import javax.net.ssl.{ SSLContext, TrustManager, X509TrustManager } import org.zalando.nakadi.client.actor.EventConsumer.ShutdownMsg import org.zalando.nakadi.client.Deserializer import org.zalando.nakadi.client.Serializer -import org.zalando.nakadi.client.Listener +import org.zalando.nakadi.client.scala.model.Event trait Connection extends HttpFactory { @@ -37,7 +36,8 @@ trait Connection extends HttpFactory { def delete(endpoint: String): Future[HttpResponse] def post[T](endpoint: String, model: T)(implicit serializer: Serializer[T]): Future[HttpResponse] def put[T](endpoint: String, model: T)(implicit serializer: Serializer[T]): Future[HttpResponse] - def subscribe[T](url: String, request: HttpRequest, listener: Listener[T])(implicit des: Deserializer[T]) + def subscribe[T <: Event](url: String, request: HttpRequest, listener: Listener[T])(implicit des: Deserializer[T]) + def subscribeJava[T <: org.zalando.nakadi.client.java.model.Event](url: String, request: HttpRequest, listener: org.zalando.nakadi.client.java.Listener[T])(implicit des: Deserializer[T]) def stop(): Future[Terminated] def materializer(): ActorMaterializer @@ -83,7 +83,6 @@ sealed class ConnectionImpl(val host: String, val port: Int, val tokenProvider: private implicit val http = Http(actorSystem) implicit val materializer = ActorMaterializer() private val actors: Map[String, Actor] = Map() - val logger = Logger(LoggerFactory.getLogger(this.getClass)) @@ -96,20 +95,20 @@ sealed class ConnectionImpl(val host: String, val port: Int, val tokenProvider: def get(endpoint: String): Future[HttpResponse] = { logger.info("Get - URL {}", endpoint) - executeCall(withHttpRequest(endpoint, HttpMethods.GET, Nil, tokenProvider,None)) + executeCall(withHttpRequest(endpoint, HttpMethods.GET, Nil, tokenProvider, None)) } def get(endpoint: String, headers: Seq[HttpHeader]): Future[HttpResponse] = { - logger.info("Get - URL {} - Headers {}", endpoint,headers) - executeCall(withHttpRequest(endpoint, HttpMethods.GET, headers, tokenProvider,None)) + logger.info("Get - URL {} - Headers {}", endpoint, headers) + executeCall(withHttpRequest(endpoint, HttpMethods.GET, headers, tokenProvider, None)) } def stream(endpoint: String, headers: Seq[HttpHeader]): Future[HttpResponse] = { logger.info("Streaming on Get: {}", endpoint) - executeCall(withHttpRequest(endpoint, HttpMethods.GET, headers, tokenProvider,None)) //TODO: Change to stream single event + executeCall(withHttpRequest(endpoint, HttpMethods.GET, headers, tokenProvider, None)) //TODO: Change to stream single event } def put[T](endpoint: String, model: T)(implicit serializer: Serializer[T]): Future[HttpResponse] = { logger.info("Get: {}", endpoint) - executeCall(withHttpRequest(endpoint, HttpMethods.GET, Nil, tokenProvider,None)) + executeCall(withHttpRequest(endpoint, HttpMethods.GET, Nil, tokenProvider, None)) } def post[T](endpoint: String, model: T)(implicit serializer: Serializer[T]): Future[HttpResponse] = { @@ -121,7 +120,7 @@ sealed class ConnectionImpl(val host: String, val port: Int, val tokenProvider: def delete(endpoint: String): Future[HttpResponse] = { logger.info("Delete: {}", endpoint) - executeCall(withHttpRequest(endpoint, HttpMethods.DELETE, Nil, tokenProvider,None)) + executeCall(withHttpRequest(endpoint, HttpMethods.DELETE, Nil, tokenProvider, None)) } private def executeCall(request: HttpRequest): Future[HttpResponse] = { @@ -141,7 +140,7 @@ sealed class ConnectionImpl(val host: String, val port: Int, val tokenProvider: def stop(): Future[Terminated] = actorSystem.terminate() - def subscribe[T](url: String, request: HttpRequest, listener: Listener[T])(implicit des: Deserializer[T]) = { + def subscribe[T <: Event](url: String, request: HttpRequest, listener: Listener[T])(implicit des: Deserializer[T]) = { logger.info("Subscribing listener {} with request {}", listener.id, request.uri) import EventConsumer._ case class MyEventExample(orderNumber: String) @@ -161,5 +160,7 @@ sealed class ConnectionImpl(val host: String, val port: Int, val tokenProvider: } } + def subscribeJava[T <: org.zalando.nakadi.client.java.model.Event](url: String, request: HttpRequest, listener: org.zalando.nakadi.client.java.Listener[T])(implicit des: Deserializer[T]) = ??? + } diff --git a/client/src/main/scala/org/zalando/nakadi/client/scala/HttpFactory.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/HttpFactory.scala index ff6e89c..92c2010 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/scala/HttpFactory.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/scala/HttpFactory.scala @@ -1,41 +1,12 @@ package org.zalando.nakadi.client.scala -import akka.http.scaladsl.model.HttpHeader -import akka.http.scaladsl.model.headers.RawHeader -import akka.http.scaladsl.model.headers.Accept +import scala.collection.immutable.Seq -import java.security.SecureRandom -import java.security.cert.X509Certificate -import scala.concurrent.ExecutionContext.Implicits.global -import scala.concurrent.Future -import scala.concurrent.duration.DurationInt -import org.slf4j.LoggerFactory -import com.typesafe.scalalogging.Logger -import akka.actor.ActorSystem -import akka.actor.Terminated -import akka.http.scaladsl.Http -import akka.http.scaladsl.HttpsConnectionContext -import akka.http.scaladsl.model.ContentType -import akka.http.scaladsl.model.HttpMethod -import akka.http.scaladsl.model.HttpMethods -import akka.http.scaladsl.model.HttpMethods.POST -import akka.http.scaladsl.model.HttpRequest -import akka.http.scaladsl.model.HttpResponse -import akka.http.scaladsl.model.MediaRange +import akka.http.scaladsl.model.{ ContentType, HttpHeader, HttpMethod, HttpRequest, MediaRange } import akka.http.scaladsl.model.MediaTypes.`application/json` import akka.http.scaladsl.model.Uri.apply import akka.http.scaladsl.model.headers -import akka.http.scaladsl.model.headers.OAuth2BearerToken -import akka.stream.ActorMaterializer -import akka.stream.scaladsl.Flow -import akka.stream.scaladsl.Sink -import akka.stream.scaladsl.Source -import javax.net.ssl.SSLContext -import javax.net.ssl.TrustManager -import javax.net.ssl.X509TrustManager -import akka.http.scaladsl.model.HttpHeader -import scala.collection.immutable.Seq -import org.zalando.nakadi.client.StreamParameters +import akka.http.scaladsl.model.headers.{ OAuth2BearerToken, RawHeader } // trait HttpFactory { @@ -67,7 +38,7 @@ trait HttpFactory { def withHttpRequest(url: String, httpMethod: HttpMethod, additionalHeaders: Seq[HttpHeader], tokenProvider: TokenProvider, params: Option[StreamParameters]): HttpRequest = { val allHeaders: Seq[HttpHeader] = additionalHeaders :+ headers.Authorization(OAuth2BearerToken(tokenProvider())) - val paramsList =withQueryParams(params) + val paramsList = withQueryParams(params) val urlParams = if (!paramsList.isEmpty) paramsList.mkString("?", "&", "") else "" val finalUrl = url + urlParams HttpRequest(uri = finalUrl, method = httpMethod).withHeaders(allHeaders) diff --git a/client/src/main/scala/org/zalando/nakadi/client/model/JacksonJsonMarshaller.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/model/JacksonJsonMarshaller.scala similarity index 88% rename from client/src/main/scala/org/zalando/nakadi/client/model/JacksonJsonMarshaller.scala rename to client/src/main/scala/org/zalando/nakadi/client/scala/model/JacksonJsonMarshaller.scala index b7cf18c..0316baf 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/model/JacksonJsonMarshaller.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/scala/model/JacksonJsonMarshaller.scala @@ -1,13 +1,10 @@ -package org.zalando.nakadi.client.model +package org.zalando.nakadi.client.scala.model -import scala.reflect.runtime.universe -import scala.reflect.runtime.universe import scala.reflect.runtime.universe._ - import org.slf4j.LoggerFactory import org.zalando.nakadi.client.Deserializer import org.zalando.nakadi.client.Serializer - +import org.zalando.nakadi.client.scala._ import com.fasterxml.jackson.annotation.JsonInclude import com.fasterxml.jackson.core.JsonFactory import com.fasterxml.jackson.core.JsonParser @@ -21,6 +18,7 @@ import com.fasterxml.jackson.databind.SerializationFeature import com.fasterxml.jackson.databind.deser.DeserializationProblemHandler import com.fasterxml.jackson.module.scala.DefaultScalaModule import com.typesafe.scalalogging.Logger +import org.zalando.nakadi.client.scala.model._ object JacksonJsonMarshaller { val logger = Logger(LoggerFactory.getLogger(this.getClass)) @@ -32,9 +30,9 @@ object JacksonJsonMarshaller { implicit def partitionTR: TypeReference[Partition] = new TypeReference[Partition] {} implicit def cursorTR: TypeReference[Cursor] = new TypeReference[Cursor] {} implicit def eventTypeSchemaTR: TypeReference[EventTypeSchema] = new TypeReference[EventTypeSchema] {} - implicit def eventValidationStrategyTR: TypeReference[EventValidationStrategy] = new TypeReference[EventValidationStrategy] {} - implicit def partitionResolutionStrategyTR: TypeReference[PartitionStrategy] = new TypeReference[PartitionStrategy] {} - implicit def eventEnrichmentStrategyTR: TypeReference[EventEnrichmentStrategy] = new TypeReference[EventEnrichmentStrategy] {} + implicit def eventValidationStrategyTR: TypeReference[EventValidationStrategy.Value] = new TypeReference[EventValidationStrategy.Value] {} + implicit def partitionResolutionStrategyTR: TypeReference[PartitionStrategy.Value] = new TypeReference[PartitionStrategy.Value] {} + implicit def eventEnrichmentStrategyTR: TypeReference[EventEnrichmentStrategy.Value] = new TypeReference[EventEnrichmentStrategy.Value] {} implicit def dataChangeEventQualifierTR: TypeReference[DataChangeEventQualifier] = new TypeReference[DataChangeEventQualifier] {} implicit def eventTypeStatisticsTR: TypeReference[EventTypeStatistics] = new TypeReference[EventTypeStatistics] {} implicit def eventTypeTR: TypeReference[EventType] = new TypeReference[EventType] {} @@ -47,9 +45,9 @@ object JacksonJsonMarshaller { implicit def dataChangeEventTR: TypeReference[DataChangeEvent[Any]] = new TypeReference[DataChangeEvent[Any]] {} //Lists - implicit def listOfPartitionStrategyTR: TypeReference[Seq[PartitionStrategy]] = new TypeReference[Seq[PartitionStrategy]] {} - implicit def listOfEventValidationStrategyTR: TypeReference[Seq[EventValidationStrategy]] = new TypeReference[Seq[EventValidationStrategy]] {} - implicit def listOfEventEnrichmentStrategyTR: TypeReference[Seq[EventEnrichmentStrategy]] = new TypeReference[Seq[EventEnrichmentStrategy]] {} + implicit def listOfPartitionStrategyTR: TypeReference[Seq[PartitionStrategy.Value]] = new TypeReference[Seq[PartitionStrategy.Value]] {} + implicit def listOfEventValidationStrategyTR: TypeReference[Seq[EventValidationStrategy.Value]] = new TypeReference[Seq[EventValidationStrategy.Value]] {} + implicit def listOfEventEnrichmentStrategyTR: TypeReference[Seq[EventEnrichmentStrategy.Value]] = new TypeReference[Seq[EventEnrichmentStrategy.Value]] {} implicit def listOfEventTypeTR: TypeReference[Seq[EventType]] = new TypeReference[Seq[EventType]] {} implicit def listOfPartitionTR: TypeReference[Seq[Partition]] = new TypeReference[Seq[Partition]] {} diff --git a/client/src/main/scala/org/zalando/nakadi/client/model/SprayJsonMarshaller.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/model/SprayJsonMarshaller.scala similarity index 100% rename from client/src/main/scala/org/zalando/nakadi/client/model/SprayJsonMarshaller.scala rename to client/src/main/scala/org/zalando/nakadi/client/scala/model/SprayJsonMarshaller.scala diff --git a/client/src/main/scala/org/zalando/nakadi/client/utils/FutureConversions.scala b/client/src/main/scala/org/zalando/nakadi/client/utils/FutureConversions.scala index 3c903d4..b5724a1 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/utils/FutureConversions.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/utils/FutureConversions.scala @@ -1,70 +1,70 @@ package org.zalando.nakadi.client.utils +import java.util.Optional import java.util.concurrent.TimeUnit + +import scala.collection.JavaConversions._ import scala.concurrent.Await import scala.concurrent.duration.Duration -import collection.JavaConversions._ -import org.zalando.nakadi.client.ClientError -import java.util.Optional object FutureConversions { - - private def extractEither[T](either: Either[String, T]): T = either match { - case Left(error) => throw new RuntimeException(error) - case Right(t) => t - } - private def extractOption[T >: Null](option: Option[ClientError]): T = option match { - case Some(v) => throw new RuntimeException(v.msg) - case None => null - } - - /** - * Transforms the value in the option wrapped in the either right inside a - * future into a java Optional wrapped in a Java future. - * If either left inside the future contains a client error, - * then a RuntimeException is thrown with the error! - */ - def fromOptionOfEither2Optional[T](in: scala.concurrent.Future[Either[ClientError, Option[T]]]): java.util.concurrent.Future[Optional[T]] = { - new MFuture[Either[ClientError, Option[T]], Optional[T]](in, a => fromRightOptionOfEither2Option(a)) - } - - /** - * Transforms the sequence in the option wrapped in the either right inside a - * future into a java List wrapped in an Optional wrapped in a Java future. - * If either left inside the future contains a client error, - * then a RuntimeException is thrown with the error! - */ - def fromSeqOfOptionalEither2OptionalList[T](in: scala.concurrent.Future[Either[ClientError, Option[Seq[T]]]]): java.util.concurrent.Future[Optional[java.util.List[T]]] = { - new MFuture[Either[ClientError, Option[Seq[T]]], Optional[java.util.List[T]]](in, a => fromSeqOfOptionalEither2OptionalList(a)) - } - - /** - * Transforms an optional into a Void if it is empty, else RuntimeException is thrown with the error! - */ - def fromOptional2Future(in: scala.concurrent.Future[Option[ClientError]]): java.util.concurrent.Future[Void] = { - new MFuture[Option[ClientError], Void](in, a => extractOption(a)) - } - - private def fromSequenceToList[T](in: Seq[T]): Optional[java.util.List[T]] = in match { - case Nil => Optional.empty() - case seq => Optional.of(new java.util.ArrayList[T](seq)) - - } - - private def fromRightOptionOfEither2Option[R](in: Either[ClientError, Option[R]]): Optional[R] = in match { - case Left(e) => throw new RuntimeException(e.msg) - case Right(Some(value)) => Optional.of(value) - case Right(None) => Optional.empty() - } - private def fromSeqOfOptionalEither2OptionalList[R](in: Either[ClientError, Option[Seq[R]]]): Optional[java.util.List[R]] = in match { - case Left(e) => throw new RuntimeException(e.msg) - case Right(None) => Optional.empty() - case Right(Some(Nil)) => Optional.of(new java.util.ArrayList[R]()) - case Right(Some(seq)) => Optional.of(new java.util.ArrayList[R](seq)) - } - - private def convert[T](x: scala.concurrent.Future[Either[String, T]]): java.util.concurrent.Future[T] = - new MFuture[Either[String, T], T](x, a => extractEither(a)) +// +// private def extractEither[T](either: Either[String, T]): T = either match { +// case Left(error) => throw new RuntimeException(error) +// case Right(t) => t +// } +// private def extractOption[T >: Null](option: Option[ClientError]): T = option match { +// case Some(v) => throw new RuntimeException(v.msg) +// case None => null +// } +// +// /** +// * Transforms the value in the option wrapped in the either right inside a +// * future into a java Optional wrapped in a Java future. +// * If either left inside the future contains a client error, +// * then a RuntimeException is thrown with the error! +// */ +// def fromOptionOfEither2Optional[T](in: scala.concurrent.Future[Either[ClientError, Option[T]]]): java.util.concurrent.Future[Optional[T]] = { +// new MFuture[Either[ClientError, Option[T]], Optional[T]](in, a => fromRightOptionOfEither2Option(a)) +// } +// +// /** +// * Transforms the sequence in the option wrapped in the either right inside a +// * future into a java List wrapped in an Optional wrapped in a Java future. +// * If either left inside the future contains a client error, +// * then a RuntimeException is thrown with the error! +// */ +// def fromSeqOfOptionalEither2OptionalList[T](in: scala.concurrent.Future[Either[ClientError, Option[Seq[T]]]]): java.util.concurrent.Future[Optional[java.util.List[T]]] = { +// new MFuture[Either[ClientError, Option[Seq[T]]], Optional[java.util.List[T]]](in, a => fromSeqOfOptionalEither2OptionalList(a)) +// } +// +// /** +// * Transforms an optional into a Void if it is empty, else RuntimeException is thrown with the error! +// */ +// def fromOptional2Future(in: scala.concurrent.Future[Option[ClientError]]): java.util.concurrent.Future[Void] = { +// new MFuture[Option[ClientError], Void](in, a => extractOption(a)) +// } +// +// private def fromSequenceToList[T](in: Seq[T]): Optional[java.util.List[T]] = in match { +// case Nil => Optional.empty() +// case seq => Optional.of(new java.util.ArrayList[T](seq)) +// +// } +// +// private def fromRightOptionOfEither2Option[R](in: Either[ClientError, Option[R]]): Optional[R] = in match { +// case Left(e) => throw new RuntimeException(e.msg) +// case Right(Some(value)) => Optional.of(value) +// case Right(None) => Optional.empty() +// } +// private def fromSeqOfOptionalEither2OptionalList[R](in: Either[ClientError, Option[Seq[R]]]): Optional[java.util.List[R]] = in match { +// case Left(e) => throw new RuntimeException(e.msg) +// case Right(None) => Optional.empty() +// case Right(Some(Nil)) => Optional.of(new java.util.ArrayList[R]()) +// case Right(Some(seq)) => Optional.of(new java.util.ArrayList[R](seq)) +// } +// +// private def convert[T](x: scala.concurrent.Future[Either[String, T]]): java.util.concurrent.Future[T] = +// new MFuture[Either[String, T], T](x, a => extractEither(a)) } diff --git a/client/src/main/scala/org/zalando/nakadi/client/utils/ImplicitHelper.scala b/client/src/main/scala/org/zalando/nakadi/client/utils/ImplicitHelper.scala index 4f8b362..96579e0 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/utils/ImplicitHelper.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/utils/ImplicitHelper.scala @@ -1,15 +1,5 @@ package org.zalando.nakadi.client.utils -import org.zalando.nakadi.client.Deserializer -import org.zalando.nakadi.client.model.Metrics -import org.zalando.nakadi.client.model.JacksonJsonMarshaller -import org.zalando.nakadi.client.model.EventType -import org.zalando.nakadi.client.Serializer -import com.fasterxml.jackson.core.`type`.TypeReference -import org.zalando.nakadi.client.model.Partition -import org.zalando.nakadi.client.model.EventValidationStrategy -import org.zalando.nakadi.client.model.EventEnrichmentStrategy -import org.zalando.nakadi.client.model.PartitionStrategy /** @@ -18,16 +8,16 @@ import org.zalando.nakadi.client.model.PartitionStrategy object Serialization { - def defaultSerializer[T]():Serializer[T] = JacksonJsonMarshaller.serializer[T] - def metricsDeserializer():Deserializer[Metrics] = JacksonJsonMarshaller.deserializer(JacksonJsonMarshaller.metricsTR) - def eventTypeDeserializer():Deserializer[EventType] = JacksonJsonMarshaller.deserializer(JacksonJsonMarshaller.eventTypeTR) - def customDeserializer[T](tr:TypeReference[T]):Deserializer[T] = JacksonJsonMarshaller.deserializer(tr) - def partitionDeserializer():Deserializer[Partition] = JacksonJsonMarshaller.deserializer(JacksonJsonMarshaller.partitionTR) - - //Sequence - def seqOfEventTypeDeserializer(): Deserializer[Seq[EventType]] = JacksonJsonMarshaller.deserializer(JacksonJsonMarshaller.listOfEventTypeTR) - def seqOfPartitionDeserializer():Deserializer[Seq[Partition]] = JacksonJsonMarshaller.deserializer(JacksonJsonMarshaller.listOfPartitionTR) - def seqOfEventValidationStrategy():Deserializer[Seq[EventValidationStrategy]] = JacksonJsonMarshaller.deserializer(JacksonJsonMarshaller.listOfEventValidationStrategyTR) - def seqOfEventEnrichmentStrategy():Deserializer[Seq[EventEnrichmentStrategy]] = JacksonJsonMarshaller.deserializer(JacksonJsonMarshaller.listOfEventEnrichmentStrategyTR) - def seqOfPartitionStrategy():Deserializer[Seq[PartitionStrategy]] = JacksonJsonMarshaller.deserializer(JacksonJsonMarshaller.listOfPartitionStrategyTR) +// def defaultSerializer[T]():Serializer[T] = JacksonJsonMarshaller.serializer[T] +// def metricsDeserializer():Deserializer[Metrics] = JacksonJsonMarshaller.deserializer(JacksonJsonMarshaller.metricsTR) +// def eventTypeDeserializer():Deserializer[EventType] = JacksonJsonMarshaller.deserializer(JacksonJsonMarshaller.eventTypeTR) +// def customDeserializer[T](tr:TypeReference[T]):Deserializer[T] = JacksonJsonMarshaller.deserializer(tr) +// def partitionDeserializer():Deserializer[Partition] = JacksonJsonMarshaller.deserializer(JacksonJsonMarshaller.partitionTR) +// +// //Sequence +// def seqOfEventTypeDeserializer(): Deserializer[Seq[EventType]] = JacksonJsonMarshaller.deserializer(JacksonJsonMarshaller.listOfEventTypeTR) +// def seqOfPartitionDeserializer():Deserializer[Seq[Partition]] = JacksonJsonMarshaller.deserializer(JacksonJsonMarshaller.listOfPartitionTR) +// def seqOfEventValidationStrategy():Deserializer[Seq[EventValidationStrategy]] = JacksonJsonMarshaller.deserializer(JacksonJsonMarshaller.listOfEventValidationStrategyTR) +// def seqOfEventEnrichmentStrategy():Deserializer[Seq[EventEnrichmentStrategy]] = JacksonJsonMarshaller.deserializer(JacksonJsonMarshaller.listOfEventEnrichmentStrategyTR) +// def seqOfPartitionStrategy():Deserializer[Seq[PartitionStrategy]] = JacksonJsonMarshaller.deserializer(JacksonJsonMarshaller.listOfPartitionStrategyTR) } \ No newline at end of file diff --git a/client/src/main/scala/org/zalando/nakadi/client/utils/ParseHelper.scala b/client/src/main/scala/org/zalando/nakadi/client/utils/ParseHelper.scala index 419254c..146e9ca 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/utils/ParseHelper.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/utils/ParseHelper.scala @@ -2,7 +2,6 @@ package org.zalando.nakadi.client.utils import spray.json._ import DefaultJsonProtocol._ -import org.zalando.nakadi.client.model.DataOperation object ParseHelper { /* diff --git a/client/src/test/scala/org/zalando/nakadi/client/scala/ClientTest.scala b/client/src/test/scala/org/zalando/nakadi/client/scala/ClientTest.scala index e27d652..47a2f96 100644 --- a/client/src/test/scala/org/zalando/nakadi/client/scala/ClientTest.scala +++ b/client/src/test/scala/org/zalando/nakadi/client/scala/ClientTest.scala @@ -22,7 +22,7 @@ import akka.http.scaladsl.model.HttpProtocol import akka.http.scaladsl.model.HttpProtocols import akka.http.scaladsl.model.HttpEntity import akka.http.scaladsl.model.ContentTypes -import org.zalando.nakadi.client.model.JacksonJsonMarshaller +import org.zalando.nakadi.client.scala.model.JacksonJsonMarshaller class ClientTest extends WordSpec with Matchers with MockitoSugar with BeforeAndAfter { import JacksonJsonMarshaller._ diff --git a/client/src/test/scala/org/zalando/nakadi/client/scala/DeserializerSerializerTest.scala b/client/src/test/scala/org/zalando/nakadi/client/scala/DeserializerSerializerTest.scala index aea5c16..2328086 100644 --- a/client/src/test/scala/org/zalando/nakadi/client/scala/DeserializerSerializerTest.scala +++ b/client/src/test/scala/org/zalando/nakadi/client/scala/DeserializerSerializerTest.scala @@ -4,13 +4,13 @@ import scala.concurrent.Await import scala.concurrent.duration.DurationInt import org.scalatest.Matchers import org.scalatest.WordSpec -import org.zalando.nakadi.client.model.EventType +import org.zalando.nakadi.client.scala.model.EventType import org.zalando.nakadi.client.utils.AkkaConfig import org.zalando.nakadi.client.utils.TestJsonEntity import akka.http.scaladsl.unmarshalling.Unmarshaller import akka.stream.Materializer import spray.json.JsonFormat -import org.zalando.nakadi.client.model.JacksonJsonMarshaller +import org.zalando.nakadi.client.scala.model.JacksonJsonMarshaller class DeserializerSerializerTest extends WordSpec with Matchers with AkkaConfig { import TestJsonEntity._ diff --git a/client/src/test/scala/org/zalando/nakadi/client/scala/EnumModule.scala b/client/src/test/scala/org/zalando/nakadi/client/scala/EnumModule.scala index 5c3f928..0d99b62 100644 --- a/client/src/test/scala/org/zalando/nakadi/client/scala/EnumModule.scala +++ b/client/src/test/scala/org/zalando/nakadi/client/scala/EnumModule.scala @@ -2,7 +2,7 @@ package org.zalando.nakadi.client.scala import scala.reflect._ import scala.reflect.runtime.universe._ -import org.zalando.nakadi.client.model.JacksonJsonMarshaller +import org.zalando.nakadi.client.scala.model.JacksonJsonMarshaller import com.fasterxml.jackson.core.JsonGenerator import com.fasterxml.jackson.core.JsonParser import com.fasterxml.jackson.core.Version @@ -15,11 +15,11 @@ import com.fasterxml.jackson.databind.JsonSerializer import com.fasterxml.jackson.databind.JsonSerializer import com.fasterxml.jackson.databind.SerializerProvider import com.fasterxml.jackson.databind.module.SimpleModule -import org.zalando.nakadi.client.model.DataOperation -import org.zalando.nakadi.client.model.BatchItemStep -import org.zalando.nakadi.client.model.BatchItemPublishingStatus -import org.zalando.nakadi.client.model.EventTypeCategory -import org.zalando.nakadi.client.model.SchemaType +import org.zalando.nakadi.client.scala.model.DataOperation +import org.zalando.nakadi.client.scala.model.BatchItemStep +import org.zalando.nakadi.client.scala.model.BatchItemPublishingStatus +import org.zalando.nakadi.client.scala.model.EventTypeCategory +import org.zalando.nakadi.client.scala.model.SchemaType import com.fasterxml.jackson.module.scala.JsonScalaEnumeration case object EnumModule extends SimpleModule() { diff --git a/client/src/test/scala/org/zalando/nakadi/client/scala/SerializerDeserializerTest.scala b/client/src/test/scala/org/zalando/nakadi/client/scala/SerializerDeserializerTest.scala index 6a0b3b9..aee1798 100644 --- a/client/src/test/scala/org/zalando/nakadi/client/scala/SerializerDeserializerTest.scala +++ b/client/src/test/scala/org/zalando/nakadi/client/scala/SerializerDeserializerTest.scala @@ -3,14 +3,13 @@ package org.zalando.nakadi.client.scala import scala.concurrent.duration.DurationInt import org.scalatest.Matchers import org.scalatest.WordSpec -import org.zalando.nakadi.client.model._ -import org.zalando.nakadi.client.model.JacksonJsonMarshaller +import org.zalando.nakadi.client.scala.model._ +import org.zalando.nakadi.client.scala.model.JacksonJsonMarshaller import org.zalando.nakadi.client.utils.AkkaConfig import org.zalando.nakadi.client.utils.TestScalaEntity import com.fasterxml.jackson.core.`type`.TypeReference import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.module.scala.JsonScalaEnumeration -import org.zalando.nakadi.client.model._ import org.zalando.nakadi.client.Deserializer import org.zalando.nakadi.client.Serializer diff --git a/client/src/test/scala/org/zalando/nakadi/client/scala/TestFactory.scala b/client/src/test/scala/org/zalando/nakadi/client/scala/TestFactory.scala index 4f97819..073cbea 100644 --- a/client/src/test/scala/org/zalando/nakadi/client/scala/TestFactory.scala +++ b/client/src/test/scala/org/zalando/nakadi/client/scala/TestFactory.scala @@ -5,26 +5,12 @@ import scala.concurrent.Future import scala.concurrent.duration.DurationInt import scala.util.Random import org.joda.time.DateTime -import org.zalando.nakadi.client.model.Event -import org.zalando.nakadi.client.model.EventMetadata -import org.zalando.nakadi.client.model.EventMetadata -import org.zalando.nakadi.client.model.EventType -import org.zalando.nakadi.client.model.EventTypeCategory -import org.zalando.nakadi.client.model.EventTypeSchema -import org.zalando.nakadi.client.model.JacksonJsonMarshaller -import org.zalando.nakadi.client.model.PartitionStrategy -import org.zalando.nakadi.client.model.SchemaType -import org.zalando.nakadi.client.model.JacksonJsonMarshaller +import org.zalando.nakadi.client.scala.model._ +import org.zalando.nakadi.client.scala.model.JacksonJsonMarshaller import org.zalando.nakadi.client.Serializer -import org.zalando.nakadi.client.model.JacksonJsonMarshaller +import org.zalando.nakadi.client.scala.model.JacksonJsonMarshaller import org.zalando.nakadi.client.Deserializer -import org.zalando.nakadi.client.model.SchemaType -import org.zalando.nakadi.client.model.PartitionStrategy import org.zalando.nakadi.client.Deserializer -import org.zalando.nakadi.client.model.EventTypeSchema -import org.zalando.nakadi.client.model.EventMetadata -import org.zalando.nakadi.client.model.EventTypeCategory -import org.zalando.nakadi.client.model.EventType import org.zalando.nakadi.client.Serializer object ClientFactory { @@ -48,7 +34,7 @@ object ClientFactory { case class EventActions(client: Client) { import JacksonJsonMarshaller._ - def create[T](name: String, event: Seq[T])(implicit ser: Serializer[Seq[T]]) = { + def create[T <: Event](name: String, event: Seq[T])(implicit ser: Serializer[Seq[T]]) = { client.publishEvents[T](name, event) } } diff --git a/client/src/test/scala/org/zalando/nakadi/client/utils/ConversionsTest.scala b/client/src/test/scala/org/zalando/nakadi/client/utils/ConversionsTest.scala index 8663341..ef7c899 100644 --- a/client/src/test/scala/org/zalando/nakadi/client/utils/ConversionsTest.scala +++ b/client/src/test/scala/org/zalando/nakadi/client/utils/ConversionsTest.scala @@ -22,7 +22,7 @@ import akka.http.scaladsl.model.HttpProtocol import akka.http.scaladsl.model.HttpProtocols import akka.http.scaladsl.model.HttpEntity import akka.http.scaladsl.model.ContentTypes -import org.zalando.nakadi.client.model.JacksonJsonMarshaller +import org.zalando.nakadi.client.scala.model.JacksonJsonMarshaller class ConversionsTest extends WordSpec with Matchers with MockitoSugar { "Conversions" should { diff --git a/client/src/test/scala/org/zalando/nakadi/client/utils/TestScalaEntity.scala b/client/src/test/scala/org/zalando/nakadi/client/utils/TestScalaEntity.scala index c338291..41afb18 100644 --- a/client/src/test/scala/org/zalando/nakadi/client/utils/TestScalaEntity.scala +++ b/client/src/test/scala/org/zalando/nakadi/client/utils/TestScalaEntity.scala @@ -1,27 +1,12 @@ package org.zalando.nakadi.client.utils -import org.zalando.nakadi.client.model._ -import org.zalando.nakadi.client.model.Problem -import org.zalando.nakadi.client.model.Partition -import org.zalando.nakadi.client.model.Metrics -import org.zalando.nakadi.client.model.EventValidationStrategy -import org.zalando.nakadi.client.model.EventTypeSchema -import org.zalando.nakadi.client.model.EventType -import org.zalando.nakadi.client.model.EventStreamBatch -import org.zalando.nakadi.client.model.EventMetadata -import org.zalando.nakadi.client.model.EventEnrichmentStrategy -import org.zalando.nakadi.client.model.Event -import org.zalando.nakadi.client.model.DataOperation -import org.zalando.nakadi.client.model.DataChangeEventQualifier -import org.zalando.nakadi.client.model.DataChangeEvent -import org.zalando.nakadi.client.model.Cursor -import org.zalando.nakadi.client.model.BusinessEvent +import org.zalando.nakadi.client.scala.model._ object TestScalaEntity { // -// Simple bjects composed out of scalar typers only (without dependency to other Object-models) + // Simple bjects composed out of scalar typers only (without dependency to other Object-models) val problem = new Problem("problemType", "title", 312, Option("detail"), Option("instance")) - val metrics = new Metrics(Map("metrics"->"test")) + val metrics = new Metrics(Map("metrics" -> "test")) val partition = new Partition(0, 132, 4423) val cursor = new Cursor(0, 120) val eventTypeSchema = new EventTypeSchema(SchemaType.JSON, "schema") @@ -33,13 +18,13 @@ object TestScalaEntity { val eventTypeStatistics = new EventTypeStatistics(Option(9281002), Option(19283), Option(21), Option(312)) val eventType = new EventType("name", "owner", EventTypeCategory.BUSINESS, Option(List(EventValidationStrategy.NONE)), List(EventEnrichmentStrategy.METADATA), Some(partitionResolutionStrategy), Option(eventTypeSchema), Option(List("dataKeyFields")), Option(List("partitioningKeyFields")), Option(eventTypeStatistics)) val eventMetadata = new EventMetadata("eid", Option(eventType), "occurredAt", Option("receivedAt"), List("parentEids"), Option("flowId"), Option("partition")) - case class MyEvent(name:String, metadata:Option[EventMetadata]) extends Event - val myEvent = new MyEvent("test",Some(eventMetadata)) - val eventStreamBatch = new EventStreamBatch(cursor, Option(List(myEvent))) + case class MyEvent(name: String, metadata: Option[EventMetadata]) extends Event + val myEvent = new MyEvent("test", Some(eventMetadata)) + val eventStreamBatch = new EventStreamBatch[MyEvent](cursor, Option(List(myEvent))) val batchItemResponse = new BatchItemResponse(Option("eid"), BatchItemPublishingStatus.SUBMITTED, Option(BatchItemStep.PUBLISHING), Option("detail")) - + // custom event - case class CommissionEntity(id:String,ql:List[String]) - val commissionEntity=new CommissionEntity("id2",List("ql1","ql2")) - val dataChangeEvent = new DataChangeEvent[CommissionEntity](commissionEntity,"Critical", DataOperation.DELETE, Some(eventMetadata)) + case class CommissionEntity(id: String, ql: List[String]) + val commissionEntity = new CommissionEntity("id2", List("ql1", "ql2")) + val dataChangeEvent = new DataChangeEvent[CommissionEntity](commissionEntity, "Critical", DataOperation.DELETE, Some(eventMetadata)) } \ No newline at end of file diff --git a/examples/scalajs-play-core-react/shared/.js/.gitignore b/examples/scalajs-play-core-react/shared/.js/.gitignore index 801cc40..2a83fcb 100644 --- a/examples/scalajs-play-core-react/shared/.js/.gitignore +++ b/examples/scalajs-play-core-react/shared/.js/.gitignore @@ -5,3 +5,18 @@ /scala/ /scala/ /scala/ +/scala/ +/scala/ +/scala/ +/scala/ +/scala/ +/scala/ +/scala/ +/scala/ +/scala/ +/scala/ +/scala/ +/scala/ +/scala/ +/scala/ +/scala/ diff --git a/it/src/test/scala/org/zalando/nakadi/client/ClientSubscriptionTest.scala b/it/src/test/scala/org/zalando/nakadi/client/ClientSubscriptionTest.scala index 204e760..fdd070c 100644 --- a/it/src/test/scala/org/zalando/nakadi/client/ClientSubscriptionTest.scala +++ b/it/src/test/scala/org/zalando/nakadi/client/ClientSubscriptionTest.scala @@ -1,17 +1,18 @@ package org.zalando.nakadi.client import org.scalatest.{ Matchers, WordSpec } -import org.zalando.nakadi.client.model._ +import org.zalando.nakadi.client.scala.model._ import com.fasterxml.jackson.core.`type`.TypeReference -import org.zalando.nakadi.client.model.JacksonJsonMarshaller +import org.zalando.nakadi.client.scala.model.JacksonJsonMarshaller import org.zalando.nakadi.client.scala.ModelFactory import org.zalando.nakadi.client.scala.EventTypesActions import org.zalando.nakadi.client.scala.ClientFactory import org.zalando.nakadi.client.scala.EventActions +import org.zalando.nakadi.client.scala.StreamParameters class ClientSubscriptionTest extends WordSpec with Matchers with ModelFactory { import ClientFactory._ import JacksonJsonMarshaller._ - case class MyEventExample(orderNumber: String) + case class MyEventExample(orderNumber: String) extends Event implicit def myEventExampleTR: TypeReference[MyEventExample] = new TypeReference[MyEventExample] {} val eventAction = new EventActions(client) val eventTypeAction = new EventTypesActions(client) diff --git a/it/src/test/scala/org/zalando/nakadi/client/EventTypesIntegrationTest.scala b/it/src/test/scala/org/zalando/nakadi/client/EventTypesIntegrationTest.scala index 67921f6..605f38b 100644 --- a/it/src/test/scala/org/zalando/nakadi/client/EventTypesIntegrationTest.scala +++ b/it/src/test/scala/org/zalando/nakadi/client/EventTypesIntegrationTest.scala @@ -1,7 +1,7 @@ package org.zalando.nakadi.client import org.scalatest.{ Matchers, WordSpec } -import org.zalando.nakadi.client.model._ +import org.zalando.nakadi.client.scala.model._ import com.fasterxml.jackson.core.`type`.TypeReference import org.zalando.nakadi.client.scala.ClientFactory import org.zalando.nakadi.client.scala.EventTypesActions @@ -23,7 +23,7 @@ import JacksonJsonMarshaller._ //Check the created EventType checkEventTypeExists(eventType) - case class MyEventExample(orderNumber: String) + case class MyEventExample(orderNumber: String)extends Event implicit def problemTR: TypeReference[MyEventExample] = new TypeReference[MyEventExample] {} val events = for { a <- 0 to 4005 diff --git a/it/src/test/scala/org/zalando/nakadi/client/KlientIntegrationTest.scala b/it/src/test/scala/org/zalando/nakadi/client/KlientIntegrationTest.scala index 2a67625..afaeae6 100644 --- a/it/src/test/scala/org/zalando/nakadi/client/KlientIntegrationTest.scala +++ b/it/src/test/scala/org/zalando/nakadi/client/KlientIntegrationTest.scala @@ -7,7 +7,7 @@ import scala.concurrent.duration.DurationInt import org.scalatest.Matchers import org.scalatest.WordSpec import org.zalando.nakadi.client._ -import org.zalando.nakadi.client.model._ +import org.zalando.nakadi.client.scala.model._ import org.zalando.nakadi.client.scala.ClientFactory import org.zalando.nakadi.client.scala.ModelFactory diff --git a/it/src/test/scala/org/zalando/nakadi/client/example2/Ecample3.scala b/it/src/test/scala/org/zalando/nakadi/client/example2/Ecample3.scala index 0b723e6..7eadf9b 100644 --- a/it/src/test/scala/org/zalando/nakadi/client/example2/Ecample3.scala +++ b/it/src/test/scala/org/zalando/nakadi/client/example2/Ecample3.scala @@ -21,9 +21,9 @@ import org.zalando.nakadi.client.scala.HttpFactory import akka.http.scaladsl.unmarshalling._ import scala.concurrent.ExecutionContext.Implicits.global import akka.http.scaladsl.Http -import org.zalando.nakadi.client.model.JacksonJsonMarshaller +import org.zalando.nakadi.client.scala.model.JacksonJsonMarshaller import org.zalando.nakadi.client.scala.ClientFactory -import org.zalando.nakadi.client.StreamParameters +import org.zalando.nakadi.client.scala.StreamParameters import akka.stream.ActorMaterializer import akka.http.scaladsl.model.headers.RawHeader import akka.http.scaladsl.model.HttpMethods diff --git a/it/src/test/scala/org/zalando/nakadi/client/example2/HttpClientExample.scala b/it/src/test/scala/org/zalando/nakadi/client/example2/HttpClientExample.scala index 41ecf55..1cc644d 100644 --- a/it/src/test/scala/org/zalando/nakadi/client/example2/HttpClientExample.scala +++ b/it/src/test/scala/org/zalando/nakadi/client/example2/HttpClientExample.scala @@ -2,43 +2,26 @@ package org.zalando.nakadi.client.example2 import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.Future -import org.zalando.nakadi.client.scala.ClientFactory -import org.zalando.nakadi.client.scala.Connection -import org.zalando.nakadi.client.scala.HttpFactory + import org.zalando.nakadi.client.Deserializer -import org.zalando.nakadi.client.StreamParameters -import org.zalando.nakadi.client.model.JacksonJsonMarshaller +import org.zalando.nakadi.client.done.EventConsumingActor +import org.zalando.nakadi.client.scala.{ ClientFactory, Connection, HttpFactory } +import org.zalando.nakadi.client.scala.model.{ Cursor, JacksonJsonMarshaller } +import org.zalando.nakadi.client.scala.StreamParameters + import com.fasterxml.jackson.core.`type`.TypeReference + import akka.NotUsed -import akka.actor.Actor -import akka.actor.ActorLogging -import akka.actor.ActorSystem -import akka.actor.Props -import akka.actor.actorRef2Scala +import akka.actor.{ ActorLogging, ActorSystem, Props, actorRef2Scala } import akka.http.scaladsl.Http -import akka.http.scaladsl.model.HttpHeader -import akka.http.scaladsl.model.HttpMethods -import akka.http.scaladsl.model.HttpRequest -import akka.http.scaladsl.model.HttpResponse +import akka.http.scaladsl.model.{ HttpHeader, HttpMethods, HttpRequest, HttpResponse } import akka.http.scaladsl.model.headers.RawHeader import akka.http.scaladsl.unmarshalling.Unmarshal -import akka.stream.ActorMaterializer -import akka.stream.ClosedShape -import akka.stream.FlowShape -import akka.stream.Outlet -import akka.stream.OverflowStrategy -import akka.stream.actor.ActorPublisher +import akka.stream.{ ActorMaterializer, ClosedShape, FlowShape, Outlet, OverflowStrategy } import akka.stream.actor.ActorSubscriber -import akka.stream.actor.ActorSubscriberMessage.OnNext import akka.stream.actor.RequestStrategy -import akka.stream.scaladsl.Flow -import akka.stream.scaladsl.GraphDSL -import akka.stream.scaladsl.RunnableGraph -import akka.stream.scaladsl.Sink -import akka.stream.scaladsl.Source +import akka.stream.scaladsl.{ Flow, GraphDSL, RunnableGraph, Sink, Source } import akka.util.ByteString -import org.zalando.nakadi.client.done.EventConsumingActor -import org.zalando.nakadi.client.model.Cursor object HttpClient extends App with HttpFactory { import ClientFactory._ diff --git a/it/src/test/scala/org/zalando/nakadi/client/examples/scala/ClientCreationExample.scala b/it/src/test/scala/org/zalando/nakadi/client/examples/scala/ClientCreationExample.scala index 0005a7b..cb3acba 100644 --- a/it/src/test/scala/org/zalando/nakadi/client/examples/scala/ClientCreationExample.scala +++ b/it/src/test/scala/org/zalando/nakadi/client/examples/scala/ClientCreationExample.scala @@ -1,6 +1,6 @@ package org.zalando.nakadi.client.scala -import org.zalando.nakadi.client.model.JacksonJsonMarshaller +import org.zalando.nakadi.client.scala.model.JacksonJsonMarshaller import scala.concurrent.Await import scala.concurrent.duration.DurationInt import org.zalando.nakadi.client.utils.ClientBuilder diff --git a/it/src/test/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala b/it/src/test/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala index 706f13e..5d5f304 100644 --- a/it/src/test/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala +++ b/it/src/test/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala @@ -2,15 +2,9 @@ package org.zalando.nakadi.client.examples.scala import org.joda.time.DateTime import org.joda.time.format.DateTimeFormatter -import org.zalando.nakadi.client.model.EventType -import org.zalando.nakadi.client.model.EventTypeCategory -import org.zalando.nakadi.client.model.EventTypeSchema -import org.zalando.nakadi.client.model.JacksonJsonMarshaller -import org.zalando.nakadi.client.model.PartitionStrategy -import org.zalando.nakadi.client.model.SchemaType +import org.zalando.nakadi.client.scala.model._ import org.zalando.nakadi.client.scala.ClientFactory import com.fasterxml.jackson.databind.util.ISO8601DateFormat -import org.zalando.nakadi.client.model.Event object EventCreationExample extends App { diff --git a/it/src/test/scala/org/zalando/nakadi/client/subscription/LeClient.scala b/it/src/test/scala/org/zalando/nakadi/client/subscription/LeClient.scala index 9776d4a..7b7f53f 100644 --- a/it/src/test/scala/org/zalando/nakadi/client/subscription/LeClient.scala +++ b/it/src/test/scala/org/zalando/nakadi/client/subscription/LeClient.scala @@ -2,8 +2,8 @@ package org.zalando.nakadi.client.subscription import org.zalando.nakadi.client.scala.ClientFactory import com.fasterxml.jackson.core.`type`.TypeReference -import org.zalando.nakadi.client.model.JacksonJsonMarshaller -import org.zalando.nakadi.client.model.Event +import org.zalando.nakadi.client.scala.model.JacksonJsonMarshaller +import org.zalando.nakadi.client.scala.model.Event object Client extends App { val client = new LeClient diff --git a/it/src/test/scala/org/zalando/nakadi/client/subscription/Subscriber.scala b/it/src/test/scala/org/zalando/nakadi/client/subscription/Subscriber.scala index 11c5634..95afd03 100644 --- a/it/src/test/scala/org/zalando/nakadi/client/subscription/Subscriber.scala +++ b/it/src/test/scala/org/zalando/nakadi/client/subscription/Subscriber.scala @@ -1,25 +1,25 @@ package org.zalando.nakadi.client.subscription -import org.zalando.nakadi.client.model.JacksonJsonMarshaller -import org.zalando.nakadi.client.scala.ClientFactory -import org.zalando.nakadi.client.StreamParameters -import org.zalando.nakadi.client.Listener -import org.zalando.nakadi.client.ClientError -import com.fasterxml.jackson.core.`type`.TypeReference -import org.zalando.nakadi.client.model.Cursor import scala.concurrent.Await import scala.concurrent.duration.DurationInt +import org.zalando.nakadi.client.ClientError +import org.zalando.nakadi.client.scala.StreamParameters +import org.zalando.nakadi.client.scala.model.{ Cursor, Event } +import com.fasterxml.jackson.core.`type`.TypeReference +import org.zalando.nakadi.client.scala.model.JacksonJsonMarshaller +import org.zalando.nakadi.client.scala.ClientFactory +import org.zalando.nakadi.client.scala.Listener -case class MyEventExample(orderNumber: String) +case class MyEventExample(orderNumber: String) extends Event object Subscriber extends App { val a = new A() - a.startListening() -// a.printPartitions() -// a.printEventTypes() -// a.sendEvents(30) + a.startListening() + // a.printPartitions() + // a.printEventTypes() + // a.sendEvents(30) } -class A { +class A { import ClientFactory._ import JacksonJsonMarshaller._ val eventType = "test-client-integration-event-1936085527-148383828851369665" @@ -38,12 +38,10 @@ class A { val url = "/event-types/test-client-integration-event-1936085527-148383828851369665/events" val cr = Cursor(0, 170000) val params = new StreamParameters( - cursor = Some(cr) - ,batchLimit = Some(10) -// ,streamLimit=Some(10) -// ,streamTimeout=Some(10) -// ,streamKeepAliveLimit =Some(10) - ) + cursor = Some(cr), batchLimit = Some(10) // ,streamLimit=Some(10) + // ,streamTimeout=Some(10) + // ,streamKeepAliveLimit =Some(10) + ) client.subscribe(eventType, params, listener) } @@ -62,7 +60,7 @@ class A { println(s"$msg - " + obj) println("###########################") } - def sendEvents(in:Int) = { + def sendEvents(in: Int) = { val events = for { a <- 1 to in } yield MyEventExample("order-" + a) From 870717c0463b2ac4af03776c9b71f07dce7ac6cf Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Fri, 29 Apr 2016 01:40:59 +0200 Subject: [PATCH 028/183] techjira:LAAS-60 Java Client almost done!! --- .../client/java/model/BatchItemResponse.java | 69 +++++ .../nakadi/client/java/model/EventType.java | 46 ++- .../client/java/model/EventTypeSchema.java | 11 +- .../zalando/nakadi/client/StreamCursor.scala | 9 - .../nakadi/client/scala/Listener.scala | 4 +- .../nakadi/client/java/ClientImpl.java | 271 +++++++++--------- .../model/JavaJacksonJsonMarshaller.scala | 82 ++++++ .../nakadi/client/scala/ClientImpl.scala | 2 + .../nakadi/client/scala/Connection.scala | 29 ++ ...scala => ScalaJacksonJsonMarshaller.scala} | 5 +- .../zalando/nakadi/client/scala/package.scala | 19 -- .../nakadi/client/utils/ClientBuilder.scala | 5 +- .../client/utils/FutureConversions.scala | 115 ++++---- .../nakadi/client/utils/ImplicitHelper.scala | 37 ++- .../org/zalando/nakadi/client/utils/Uri.scala | 19 ++ .../nakadi/client/scala/ClientTest.scala | 2 + .../client/examples/java/ClientExample.java | 18 +- .../examples/java/EventCreationExample.java | 51 ++-- .../scala/ClientCreationExample.scala | 0 .../examples/scala/EventCreationExample.scala | 0 .../nakadi/client/scala/ClientFactoryy.scala | 11 + .../nakadi/client/subscription/LeClient.scala | 0 .../client/subscription/MyEventExample.scala | 5 + .../client/subscription/MyListener.scala | 16 ++ .../client/subscription/Subscriber.scala | 17 +- .../client/ClientSubscriptionTest.scala | 1 + 26 files changed, 553 insertions(+), 291 deletions(-) create mode 100644 api/src/main/java/org/zalando/nakadi/client/java/model/BatchItemResponse.java delete mode 100644 api/src/main/scala/org/zalando/nakadi/client/StreamCursor.scala create mode 100644 client/src/main/scala/org/zalando/nakadi/client/java/model/JavaJacksonJsonMarshaller.scala rename client/src/main/scala/org/zalando/nakadi/client/scala/model/{JacksonJsonMarshaller.scala => ScalaJacksonJsonMarshaller.scala} (96%) delete mode 100644 client/src/main/scala/org/zalando/nakadi/client/scala/package.scala create mode 100644 client/src/main/scala/org/zalando/nakadi/client/utils/Uri.scala rename it/src/{test => main}/java/org/zalando/nakadi/client/examples/java/ClientExample.java (68%) rename it/src/{test => main}/java/org/zalando/nakadi/client/examples/java/EventCreationExample.java (61%) rename it/src/{test => main}/scala/org/zalando/nakadi/client/examples/scala/ClientCreationExample.scala (100%) rename it/src/{test => main}/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala (100%) create mode 100644 it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactoryy.scala rename it/src/{test => main}/scala/org/zalando/nakadi/client/subscription/LeClient.scala (100%) create mode 100644 it/src/main/scala/org/zalando/nakadi/client/subscription/MyEventExample.scala create mode 100644 it/src/main/scala/org/zalando/nakadi/client/subscription/MyListener.scala rename it/src/{test => main}/scala/org/zalando/nakadi/client/subscription/Subscriber.scala (74%) diff --git a/api/src/main/java/org/zalando/nakadi/client/java/model/BatchItemResponse.java b/api/src/main/java/org/zalando/nakadi/client/java/model/BatchItemResponse.java new file mode 100644 index 0000000..9e5b67d --- /dev/null +++ b/api/src/main/java/org/zalando/nakadi/client/java/model/BatchItemResponse.java @@ -0,0 +1,69 @@ +package org.zalando.nakadi.client.java.model; + +import org.zalando.nakadi.client.java.enumerator.BatchItemPublishingStatus; +import org.zalando.nakadi.client.java.enumerator.BatchItemStep; + +/** + * A status corresponding to one individual Event's publishing attempt. + * + */ +public class BatchItemResponse { + private final String eid; + private final BatchItemPublishingStatus publishingStatus; + private final BatchItemStep step; + private final String detail; + + /** + * A status corresponding to one individual Event's publishing attempt. + * + * @param eid + * Eid of the corresponding item. Will be absent if missing on + * the incoming Event. + * @param publishingStatus + * Indicator of the submission of the Event within a Batch. - + * SUBMITTED indicates successful submission, including commit on + * he underlying broker. - FAILED indicates the message + * submission was not possible and can be resubmitted if so + * desired. - ABORTED indicates that the submission of this item + * was not attempted any further due to a failure on another item + * in the batch. + * @param step + * Indicator of the step in the pulbishing process this Event + * reached. In Items that FAILED means the step of the failure. - + * NONE indicates that nothing was yet attempted for the + * publishing of this Event. Should be present only in the case + * of aborting the publishing during the validation of another + * (previous) Event. - VALIDATING, ENRICHING, PARTITIONING and + * PUBLISHING indicate all the corresponding steps of the + * publishing process. + * @param detail + * Human readable information about the failure on this item. + * Items that are not SUBMITTED should have a description. + * + */ + public BatchItemResponse(String eid, + BatchItemPublishingStatus publishingStatus, BatchItemStep step, + String detail) { + this.eid = eid; + this.publishingStatus = publishingStatus; + this.step = step; + this.detail = detail; + } + + public String getEid() { + return eid; + } + + public BatchItemPublishingStatus getPublishingStatus() { + return publishingStatus; + } + + public BatchItemStep getStep() { + return step; + } + + public String getDetail() { + return detail; + } + +} diff --git a/api/src/main/java/org/zalando/nakadi/client/java/model/EventType.java b/api/src/main/java/org/zalando/nakadi/client/java/model/EventType.java index a59600d..ecef610 100644 --- a/api/src/main/java/org/zalando/nakadi/client/java/model/EventType.java +++ b/api/src/main/java/org/zalando/nakadi/client/java/model/EventType.java @@ -7,6 +7,9 @@ import org.zalando.nakadi.client.java.enumerator.EventValidationStrategy; import org.zalando.nakadi.client.java.enumerator.PartitionStrategy; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + /** * An event type defines the schema and its runtime properties. * @@ -19,7 +22,7 @@ public class EventType { private final List validationStrategies; private final List enrichmentStrategies; private final PartitionStrategy partitionStrategy; - private final String schema; + private final EventTypeSchema schema; private final List dataKeyFields; private final List partitionKeyFields; private final EventTypeStatistics statistics; @@ -101,10 +104,29 @@ public class EventType { * measured statistics). * */ - public EventType(String name, String owningApplication, EventTypeCategory category, - List validationStrategies, List enrichmentStrategies, - PartitionStrategy partitionStrategy, String schema, List dataKeyFields, - List partitionKeyFields, EventTypeStatistics statistics) { + @JsonCreator + public EventType( + @JsonProperty("name") + String name, + @JsonProperty("owning_application") + String owningApplication, + @JsonProperty("category") + EventTypeCategory category, + + @JsonProperty("validation_strategies") + List validationStrategies, + @JsonProperty("enrichment_strategies") + List enrichmentStrategies, + @JsonProperty("partition_strategy") + PartitionStrategy partitionStrategy, + @JsonProperty("schema") + EventTypeSchema schema, + @JsonProperty("data_key_fields") + List dataKeyFields, + @JsonProperty("partition_key_fields") + List partitionKeyFields, + @JsonProperty("statistics") + EventTypeStatistics statistics) { this.name = name; this.owningApplication = owningApplication; this.category = category; @@ -141,7 +163,7 @@ public PartitionStrategy getPartitionStrategy() { return partitionStrategy; } - public String getSchema() { + public EventTypeSchema getSchema() { return schema; } @@ -157,4 +179,16 @@ public EventTypeStatistics getStatistics() { return statistics; } + @Override + public String toString() { + return "EventType [name=" + name + ", owningApplication=" + + owningApplication + ", category=" + category + + ", validationStrategies=" + validationStrategies + + ", enrichmentStrategies=" + enrichmentStrategies + + ", partitionStrategy=" + partitionStrategy + ", schema=" + + schema + ", dataKeyFields=" + dataKeyFields + + ", partitionKeyFields=" + partitionKeyFields + + ", statistics=" + statistics + "]"; + } + } diff --git a/api/src/main/java/org/zalando/nakadi/client/java/model/EventTypeSchema.java b/api/src/main/java/org/zalando/nakadi/client/java/model/EventTypeSchema.java index 15db5a2..3c486f8 100644 --- a/api/src/main/java/org/zalando/nakadi/client/java/model/EventTypeSchema.java +++ b/api/src/main/java/org/zalando/nakadi/client/java/model/EventTypeSchema.java @@ -2,6 +2,9 @@ import org.zalando.nakadi.client.java.enumerator.SchemaType; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + /** * The schema for an EventType, expected to be a json schema in yaml * format (other formats might be added in the future). @@ -13,8 +16,12 @@ public class EventTypeSchema { private final SchemaType type; private final String schema; - - public EventTypeSchema(SchemaType type, String schema) { + @JsonCreator + public EventTypeSchema( + @JsonProperty("type") + SchemaType type, + @JsonProperty("schema") + String schema) { this.type = type; this.schema = schema; } diff --git a/api/src/main/scala/org/zalando/nakadi/client/StreamCursor.scala b/api/src/main/scala/org/zalando/nakadi/client/StreamCursor.scala deleted file mode 100644 index 397417b..0000000 --- a/api/src/main/scala/org/zalando/nakadi/client/StreamCursor.scala +++ /dev/null @@ -1,9 +0,0 @@ -package org.zalando.nakadi.client - -/** - * @param partition Id of the partition pointed to by this cursor. - * @param offset Offset of the event being pointed to. - */ -case class Cursor( - partition: Integer, - offset: Integer) diff --git a/api/src/main/scala/org/zalando/nakadi/client/scala/Listener.scala b/api/src/main/scala/org/zalando/nakadi/client/scala/Listener.scala index e04a65a..33d99cd 100644 --- a/api/src/main/scala/org/zalando/nakadi/client/scala/Listener.scala +++ b/api/src/main/scala/org/zalando/nakadi/client/scala/Listener.scala @@ -1,8 +1,8 @@ package org.zalando.nakadi.client.scala -import org.zalando.nakadi.client.scala.model.Event import org.zalando.nakadi.client.ClientError -import org.zalando.nakadi.client.Cursor +import org.zalando.nakadi.client.scala.model.Cursor +import org.zalando.nakadi.client.scala.model.Event diff --git a/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java b/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java index bf55b7e..1201a16 100644 --- a/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java +++ b/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java @@ -4,145 +4,152 @@ import java.util.Optional; import java.util.concurrent.Future; -import scala.collection.JavaConverters; - import org.zalando.nakadi.client.Deserializer; -import org.zalando.nakadi.client.java.Listener; import org.zalando.nakadi.client.Serializer; -import org.zalando.nakadi.client.java.StreamParameters; -import org.zalando.nakadi.client.java.model.*; -import org.zalando.nakadi.client.java.enumerator.*; +import org.zalando.nakadi.client.java.enumerator.EventEnrichmentStrategy; +import org.zalando.nakadi.client.java.enumerator.EventValidationStrategy; +import org.zalando.nakadi.client.java.enumerator.PartitionStrategy; +import org.zalando.nakadi.client.java.model.Event; +import org.zalando.nakadi.client.java.model.EventType; +import org.zalando.nakadi.client.java.model.Metrics; +import org.zalando.nakadi.client.java.model.Partition; +import org.zalando.nakadi.client.scala.Connection; import org.zalando.nakadi.client.utils.FutureConversions; import org.zalando.nakadi.client.utils.Serialization; - -import scala.collection.Seq; - +import org.zalando.nakadi.client.utils.Uri; public class ClientImpl implements Client { - private final org.zalando.nakadi.client.scala.Client client; - -// //Deserializers -// private final Deserializer metricsDeserializer =Serialization.metricsDeserializer(); -// private final Deserializer partitionDeserializer = Serialization.partitionDeserializer(); -// //Seq Deserializers -// private final Deserializer> seqOfEventTypeDeserializer =Serialization.seqOfEventTypeDeserializer(); -// private final Deserializer> seqOfPartitionDeserializer =Serialization.seqOfPartitionDeserializer(); -// private final Deserializer> seqOfEventValidationStrategy =Serialization.seqOfEventValidationStrategy(); -// private final Deserializer> seqOfEventEnrichmentStrategy =Serialization.seqOfEventEnrichmentStrategy(); -// private final Deserializer> seqOfPartitionStrategy =Serialization.seqOfPartitionStrategy(); -// //Serializers -// private final Serializer eventTypeSerializer =Serialization.defaultSerializer(); -// private final Deserializer eventTypeDeserializer = Serialization.eventTypeDeserializer(); + private final Connection connection; + private final String charSet; + //Deserializers + private final Deserializer metricsDeserializer =Serialization.metricsDeserializer(); + private final Deserializer partitionDeserializer = Serialization.partitionDeserializer(); + //List Deserializers + private final Deserializer> seqOfEventTypeDeserializer =Serialization.seqOfEventTypeDeserializer(); + private final Deserializer> seqOfPartitionDeserializer =Serialization.seqOfPartitionDeserializer(); + private final Deserializer> seqOfEventValidationStrategy =Serialization.seqOfEventValidationStrategy(); + private final Deserializer> seqOfEventEnrichmentStrategy =Serialization.seqOfEventEnrichmentStrategy(); + private final Deserializer> seqOfPartitionStrategy =Serialization.seqOfPartitionStrategy(); + //Serializers + private final Serializer eventTypeSerializer =Serialization.defaultSerializer(); + private final Deserializer eventTypeDeserializer = Serialization.eventTypeDeserializer(); + + public ClientImpl(Connection connection, String charSet) { + this.connection = connection; + this.charSet = charSet; + } + + @Override + public Future> getMetrics() { + + return connection.get4Java(Uri.URI_METRICS(), metricsDeserializer); + } + + @Override + public Future>> getEventTypes() { + + return connection.get4Java(Uri.URI_EVENT_TYPES(), seqOfEventTypeDeserializer); + } + + @Override + public Future createEventType(EventType eventType) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Future> getEventType(String eventTypeName) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Future updateEventType(String eventTypeName, + EventType eventType) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Future deleteEventType(String eventTypeName) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Future publishEvent(String eventTypeName, + T event, Serializer serializer) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Future publishEvent(String eventTypeName, + T event) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Future publishEvents(String eventTypeName, + List events, Serializer serializer) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Future publishEvents(String eventTypeName, + List events) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Future>> getPartitions(String eventTypeName) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Future>> getValidationStrategies() { + // TODO Auto-generated method stub + return null; + } + + @Override + public Future>> getEnrichmentStrategies() { + // TODO Auto-generated method stub + return null; + } + + @Override + public Future>> getPartitioningStrategies() { + + return connection.get4Java(Uri.URI_PARTITIONING_STRATEGIES(), seqOfPartitionStrategy); + } + + @Override + public Future stop() { + // TODO Auto-generated method stub + return null; + } + + @Override + public Future subscribe(String eventTypeName, + org.zalando.nakadi.client.scala.StreamParameters parameters, + Listener listener, Deserializer deserializer) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Future unsubscribe(String eventTypeName, + Listener listener) { + // TODO Auto-generated method stub + return null; + } + - public ClientImpl(org.zalando.nakadi.client.scala.Client client) { - this.client = client; - } - -@Override -public Future> getMetrics() { - - return null; -} - -@Override -public Future>> getEventTypes() { - // TODO Auto-generated method stub - return null; -} - -@Override -public Future createEventType(EventType eventType) { - // TODO Auto-generated method stub - return null; -} - -@Override -public Future> getEventType(String eventTypeName) { - // TODO Auto-generated method stub - return null; -} - -@Override -public Future updateEventType(String eventTypeName, EventType eventType) { - // TODO Auto-generated method stub - return null; -} - -@Override -public Future deleteEventType(String eventTypeName) { - // TODO Auto-generated method stub - return null; -} - -@Override -public Future publishEvent(String eventTypeName, T event, Serializer serializer) { - // TODO Auto-generated method stub - return null; -} - -@Override -public Future publishEvent(String eventTypeName, T event) { - // TODO Auto-generated method stub - return null; -} - -@Override -public Future publishEvents(String eventTypeName, List events, Serializer serializer) { - // TODO Auto-generated method stub - return null; -} - -@Override -public Future publishEvents(String eventTypeName, List events) { - // TODO Auto-generated method stub - return null; -} - -@Override -public Future>> getPartitions(String eventTypeName) { - // TODO Auto-generated method stub - return null; -} - -@Override -public Future>> getValidationStrategies() { - // TODO Auto-generated method stub - return null; -} - -@Override -public Future>> getEnrichmentStrategies() { - // TODO Auto-generated method stub - return null; -} - -@Override -public Future>> getPartitioningStrategies() { - // TODO Auto-generated method stub - return null; -} - -@Override -public Future stop() { - // TODO Auto-generated method stub - return null; -} - -@Override -public Future subscribe(String eventTypeName, - org.zalando.nakadi.client.scala.StreamParameters parameters, Listener listener, Deserializer deserializer) { - // TODO Auto-generated method stub - return null; -} - -@Override -public Future unsubscribe(String eventTypeName, Listener listener) { - // TODO Auto-generated method stub - return null; -} - - - } \ No newline at end of file diff --git a/client/src/main/scala/org/zalando/nakadi/client/java/model/JavaJacksonJsonMarshaller.scala b/client/src/main/scala/org/zalando/nakadi/client/java/model/JavaJacksonJsonMarshaller.scala new file mode 100644 index 0000000..7c597dd --- /dev/null +++ b/client/src/main/scala/org/zalando/nakadi/client/java/model/JavaJacksonJsonMarshaller.scala @@ -0,0 +1,82 @@ +package org.zalando.nakadi.client.java.model + +import org.slf4j.LoggerFactory +import org.zalando.nakadi.client.Deserializer +import org.zalando.nakadi.client.Serializer +import org.zalando.nakadi.client.java.enumerator._ + +import com.fasterxml.jackson.annotation.JsonInclude +import com.fasterxml.jackson.core.JsonFactory +import com.fasterxml.jackson.core.JsonParser +import com.fasterxml.jackson.core.`type`.TypeReference +import com.fasterxml.jackson.databind.DeserializationContext +import com.fasterxml.jackson.databind.DeserializationFeature +import com.fasterxml.jackson.databind.JsonDeserializer +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.databind.PropertyNamingStrategy +import com.fasterxml.jackson.databind.SerializationFeature +import com.fasterxml.jackson.databind.deser.DeserializationProblemHandler +import com.fasterxml.jackson.module.scala.DefaultScalaModule +import com.typesafe.scalalogging.Logger +object JavaJacksonJsonMarshaller { + val logger = Logger(LoggerFactory.getLogger(this.getClass)) + // All TypeReferences + implicit def problemTR: TypeReference[Problem] = new TypeReference[Problem] {} + implicit def metricsTR: TypeReference[Metrics] = new TypeReference[Metrics] {} + implicit def partitionTR: TypeReference[Partition] = new TypeReference[Partition] {} + implicit def cursorTR: TypeReference[Cursor] = new TypeReference[Cursor] {} + implicit def eventTypeSchemaTR: TypeReference[EventTypeSchema] = new TypeReference[EventTypeSchema] {} + implicit def eventValidationStrategyTR: TypeReference[EventValidationStrategy] = new TypeReference[EventValidationStrategy] {} + implicit def partitionResolutionStrategyTR: TypeReference[PartitionStrategy] = new TypeReference[PartitionStrategy] {} + implicit def eventEnrichmentStrategyTR: TypeReference[EventEnrichmentStrategy] = new TypeReference[EventEnrichmentStrategy] {} + implicit def dataChangeEventQualifierTR: TypeReference[DataChangeEventQualifier] = new TypeReference[DataChangeEventQualifier] {} + implicit def eventTypeStatisticsTR: TypeReference[EventTypeStatistics] = new TypeReference[EventTypeStatistics] {} + implicit def eventTypeTR: TypeReference[EventType] = new TypeReference[EventType] {} + implicit def eventTR: TypeReference[Event] = new TypeReference[Event] {} + implicit def eventStreamBatchTR: TypeReference[EventStreamBatch[_]] = new TypeReference[EventStreamBatch[_]] {} + + implicit def eventMetadataTR: TypeReference[EventMetadata] = new TypeReference[EventMetadata] {} + implicit def businessEventTR: TypeReference[BusinessEvent] = new TypeReference[BusinessEvent] {} + implicit def batchItemResponseTR: TypeReference[BatchItemResponse] = new TypeReference[BatchItemResponse] {} + implicit def dataChangeEventTR: TypeReference[DataChangeEvent[Any]] = new TypeReference[DataChangeEvent[Any]] {} + + //Lists + implicit def listOfPartitionStrategyTR: TypeReference[java.util.List[PartitionStrategy]] = new TypeReference[java.util.List[PartitionStrategy]] {} + implicit def listOfEventValidationStrategyTR: TypeReference[java.util.List[EventValidationStrategy]] = new TypeReference[java.util.List[EventValidationStrategy]] {} + implicit def listOfEventEnrichmentStrategyTR: TypeReference[java.util.List[EventEnrichmentStrategy]] = new TypeReference[java.util.List[EventEnrichmentStrategy]] {} + implicit def listOfEventTypeTR: TypeReference[java.util.List[EventType]] = new TypeReference[java.util.List[EventType]] {} + implicit def listOfPartitionTR: TypeReference[java.util.List[Partition]] = new TypeReference[java.util.List[Partition]] {} + + implicit def optionalDeserializer[T](implicit expectedType: TypeReference[T]): Deserializer[Option[T]] = new Deserializer[Option[T]] { + def from(from: String): Option[T] = { + logger.debug("json: {}",from) + defaultObjectMapper.readValue[Option[T]](from, expectedType) + } + } + + implicit def serializer[T]: Serializer[T] = new Serializer[T] { + def to(from: T): String = defaultObjectMapper.writeValueAsString(from) + } + + implicit def deserializer[T](implicit expectedType: TypeReference[T]): Deserializer[T] = new Deserializer[T] { + def from(from: String): T = { + logger.debug("json: {}",from) + defaultObjectMapper.readValue[T](from, expectedType) + } + } + + lazy val defaultObjectMapper: ObjectMapper = new ObjectMapper() // + .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) + .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .setPropertyNamingStrategy(PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES) + .setSerializationInclusion(JsonInclude.Include.NON_NULL) + .addHandler(new DeserializationProblemHandler() { + override def handleUnknownProperty(ctxt: DeserializationContext, + jp: JsonParser, deserializer: JsonDeserializer[_], + beanOrClass: AnyRef, + propertyName: String): Boolean = { + logger.warn(s"unknown property occurred in JSON representation: [beanOrClass=$beanOrClass, property=$propertyName]") + true + } + }) +} \ No newline at end of file diff --git a/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala index f602c0e..7a01f39 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala @@ -19,11 +19,13 @@ import org.zalando.nakadi.client.Deserializer import org.zalando.nakadi.client.Serializer import org.zalando.nakadi.client.scala.model._ import org.zalando.nakadi.client.ClientError +import org.zalando.nakadi.client.utils.Uri private[client] class ClientImpl(connection: Connection, charSet: String = "UTF-8") extends Client with HttpFactory { implicit val materializer = connection.materializer + import Uri._ val logger = Logger(LoggerFactory.getLogger(this.getClass)) def getMetrics()(implicit des: Deserializer[Metrics]): Future[Either[ClientError, Option[Metrics]]] = { diff --git a/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala index c94b1fa..e936458 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala @@ -21,6 +21,13 @@ import org.zalando.nakadi.client.actor.EventConsumer.ShutdownMsg import org.zalando.nakadi.client.Deserializer import org.zalando.nakadi.client.Serializer import org.zalando.nakadi.client.scala.model.Event +import akka.http.scaladsl.unmarshalling.Unmarshal +import scala.util.Try +import scala.util.Failure +import scala.util.Success +import akka.http.scaladsl.model.{ HttpHeader, HttpMethod, HttpMethods, HttpResponse, MediaRange } +import java.util.Optional +import org.zalando.nakadi.client.utils.FutureConversions trait Connection extends HttpFactory { @@ -32,6 +39,8 @@ trait Connection extends HttpFactory { def get(endpoint: String): Future[HttpResponse] def get(endpoint: String, headers: Seq[HttpHeader]): Future[HttpResponse] + def get4Java[T](endpoint: String, des: Deserializer[T]): java.util.concurrent.Future[Optional[T]] + def get4Java[T](endpoint: String, headers: Seq[HttpHeader], des: Deserializer[T]): java.util.concurrent.Future[Optional[T]] def stream(endpoint: String, headers: Seq[HttpHeader]): Future[HttpResponse] def delete(endpoint: String): Future[HttpResponse] def post[T](endpoint: String, model: T)(implicit serializer: Serializer[T]): Future[HttpResponse] @@ -101,6 +110,26 @@ sealed class ConnectionImpl(val host: String, val port: Int, val tokenProvider: logger.info("Get - URL {} - Headers {}", endpoint, headers) executeCall(withHttpRequest(endpoint, HttpMethods.GET, headers, tokenProvider, None)) } + def get4Java[T](endpoint: String, des: Deserializer[T]): java.util.concurrent.Future[Optional[T]] = { + FutureConversions.fromFuture2Future(get(endpoint).flatMap(deserialize4Java(_, des))) + } + def get4Java[T](endpoint: String, headers: Seq[HttpHeader], des: Deserializer[T]): java.util.concurrent.Future[Optional[T]] = { + FutureConversions.fromFuture2Future(executeCall(withHttpRequest(endpoint, HttpMethods.GET, headers, tokenProvider, None)).flatMap(deserialize4Java(_, des))) + } + + private def deserialize4Java[T](response:HttpResponse, des: Deserializer[T]):Future[Optional[T]] = response match { + case HttpResponse(status, headers, entity, protocol) if (status.isSuccess()) => + + Try(Unmarshal(entity).to[String].map(body => des.from(body))) match { + case Success(result) => result.map(Optional.of(_)) + case Failure(error) => throw new RuntimeException(error.getMessage) + } + // val errorMsg = "Failed to Deserialize with error:" + error.getMessage + // listener.onError(url, null, ClientError("Failed to Deserialize with an error!", None)) + + case HttpResponse(status, headers, entity, protocol) if (status.isFailure()) => + throw new RuntimeException(status.reason()) + } def stream(endpoint: String, headers: Seq[HttpHeader]): Future[HttpResponse] = { logger.info("Streaming on Get: {}", endpoint) executeCall(withHttpRequest(endpoint, HttpMethods.GET, headers, tokenProvider, None)) //TODO: Change to stream single event diff --git a/client/src/main/scala/org/zalando/nakadi/client/scala/model/JacksonJsonMarshaller.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/model/ScalaJacksonJsonMarshaller.scala similarity index 96% rename from client/src/main/scala/org/zalando/nakadi/client/scala/model/JacksonJsonMarshaller.scala rename to client/src/main/scala/org/zalando/nakadi/client/scala/model/ScalaJacksonJsonMarshaller.scala index 0316baf..64d3616 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/scala/model/JacksonJsonMarshaller.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/scala/model/ScalaJacksonJsonMarshaller.scala @@ -1,10 +1,9 @@ package org.zalando.nakadi.client.scala.model -import scala.reflect.runtime.universe._ import org.slf4j.LoggerFactory import org.zalando.nakadi.client.Deserializer import org.zalando.nakadi.client.Serializer -import org.zalando.nakadi.client.scala._ + import com.fasterxml.jackson.annotation.JsonInclude import com.fasterxml.jackson.core.JsonFactory import com.fasterxml.jackson.core.JsonParser @@ -18,11 +17,9 @@ import com.fasterxml.jackson.databind.SerializationFeature import com.fasterxml.jackson.databind.deser.DeserializationProblemHandler import com.fasterxml.jackson.module.scala.DefaultScalaModule import com.typesafe.scalalogging.Logger -import org.zalando.nakadi.client.scala.model._ object JacksonJsonMarshaller { val logger = Logger(LoggerFactory.getLogger(this.getClass)) - val factory = new JsonFactory(); // All TypeReferences implicit def problemTR: TypeReference[Problem] = new TypeReference[Problem] {} diff --git a/client/src/main/scala/org/zalando/nakadi/client/scala/package.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/package.scala deleted file mode 100644 index cdaeecc..0000000 --- a/client/src/main/scala/org/zalando/nakadi/client/scala/package.scala +++ /dev/null @@ -1,19 +0,0 @@ -package org.zalando.nakadi.client - -package object scala { - - val URI_METRICS = "/metrics" - - /*Events*/ - val URI_EVENT_TYPES = "/event-types" - val URI_EVENT_TYPE_BY_NAME = "/event-types/%s" - val URI_EVENTS_OF_EVENT_TYPE = "/event-types/%s/events" - - /*Partitions*/ - val URI_PARTITIONS_BY_EVENT_TYPE = "/event-types/%s/partitions" - - /*Strategies*/ - val URI_VALIDATION_STRATEGIES = "/registry/validation-strategies" - val URI_ENRICHMENT_STRATEGIES = "/registry/enrichment-strategies" - val URI_PARTITIONING_STRATEGIES = "/registry/partition-strategies" -} \ No newline at end of file diff --git a/client/src/main/scala/org/zalando/nakadi/client/utils/ClientBuilder.scala b/client/src/main/scala/org/zalando/nakadi/client/utils/ClientBuilder.scala index 41ebe7c..3ec4767 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/utils/ClientBuilder.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/utils/ClientBuilder.scala @@ -63,7 +63,10 @@ class ClientBuilder private(host: String = "", // def build(): Client = new ClientImpl(Connection.newConnection(host, port, tokenProvider, securedConnection, verifySSlCertificate), "UTF-8") - def buildJavaClient(): org.zalando.nakadi.client.java.Client = new org.zalando.nakadi.client.java.ClientImpl(build) + def buildJavaClient():org.zalando.nakadi.client.java.Client = { + val connection =Connection.newConnection(host, port, tokenProvider, securedConnection, verifySSlCertificate) + new org.zalando.nakadi.client.java.ClientImpl(connection, "UTF-8") + } private def checkNotNull[T](subject: T): T = if (Option(subject).isEmpty) throw new NullPointerException else subject diff --git a/client/src/main/scala/org/zalando/nakadi/client/utils/FutureConversions.scala b/client/src/main/scala/org/zalando/nakadi/client/utils/FutureConversions.scala index b5724a1..f10fad0 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/utils/FutureConversions.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/utils/FutureConversions.scala @@ -8,63 +8,64 @@ import scala.concurrent.Await import scala.concurrent.duration.Duration object FutureConversions { -// -// private def extractEither[T](either: Either[String, T]): T = either match { -// case Left(error) => throw new RuntimeException(error) -// case Right(t) => t -// } -// private def extractOption[T >: Null](option: Option[ClientError]): T = option match { -// case Some(v) => throw new RuntimeException(v.msg) -// case None => null -// } -// -// /** -// * Transforms the value in the option wrapped in the either right inside a -// * future into a java Optional wrapped in a Java future. -// * If either left inside the future contains a client error, -// * then a RuntimeException is thrown with the error! -// */ -// def fromOptionOfEither2Optional[T](in: scala.concurrent.Future[Either[ClientError, Option[T]]]): java.util.concurrent.Future[Optional[T]] = { -// new MFuture[Either[ClientError, Option[T]], Optional[T]](in, a => fromRightOptionOfEither2Option(a)) -// } -// -// /** -// * Transforms the sequence in the option wrapped in the either right inside a -// * future into a java List wrapped in an Optional wrapped in a Java future. -// * If either left inside the future contains a client error, -// * then a RuntimeException is thrown with the error! -// */ -// def fromSeqOfOptionalEither2OptionalList[T](in: scala.concurrent.Future[Either[ClientError, Option[Seq[T]]]]): java.util.concurrent.Future[Optional[java.util.List[T]]] = { -// new MFuture[Either[ClientError, Option[Seq[T]]], Optional[java.util.List[T]]](in, a => fromSeqOfOptionalEither2OptionalList(a)) -// } -// -// /** -// * Transforms an optional into a Void if it is empty, else RuntimeException is thrown with the error! -// */ -// def fromOptional2Future(in: scala.concurrent.Future[Option[ClientError]]): java.util.concurrent.Future[Void] = { -// new MFuture[Option[ClientError], Void](in, a => extractOption(a)) -// } -// -// private def fromSequenceToList[T](in: Seq[T]): Optional[java.util.List[T]] = in match { -// case Nil => Optional.empty() -// case seq => Optional.of(new java.util.ArrayList[T](seq)) -// -// } -// -// private def fromRightOptionOfEither2Option[R](in: Either[ClientError, Option[R]]): Optional[R] = in match { -// case Left(e) => throw new RuntimeException(e.msg) -// case Right(Some(value)) => Optional.of(value) -// case Right(None) => Optional.empty() -// } -// private def fromSeqOfOptionalEither2OptionalList[R](in: Either[ClientError, Option[Seq[R]]]): Optional[java.util.List[R]] = in match { -// case Left(e) => throw new RuntimeException(e.msg) -// case Right(None) => Optional.empty() -// case Right(Some(Nil)) => Optional.of(new java.util.ArrayList[R]()) -// case Right(Some(seq)) => Optional.of(new java.util.ArrayList[R](seq)) -// } -// -// private def convert[T](x: scala.concurrent.Future[Either[String, T]]): java.util.concurrent.Future[T] = -// new MFuture[Either[String, T], T](x, a => extractEither(a)) + // + private def extractEither[T](either: Either[String, T]): T = either match { + case Left(error) => throw new RuntimeException(error) + case Right(t) => t + } + // private def extractOption[T >: Null](option: Option[ClientError]): T = option match { + // case Some(v) => throw new RuntimeException(v.msg) + // case None => null + // } + + def fromOption2Optional[T](in: scala.concurrent.Future[Option[T]]): java.util.concurrent.Future[Optional[T]] = { + new MFuture[Option[T], Optional[T]](in, a => fromOptional2Optional(a)) + } + def fromFuture2Future[T](in: scala.concurrent.Future[T]): java.util.concurrent.Future[T] = { + new MFuture[T, T](in, a => a) + } + + // /** + // * Transforms the sequence in the option wrapped in the either right inside a + // * future into a java List wrapped in an Optional wrapped in a Java future. + // * If either left inside the future contains a client error, + // * then a RuntimeException is thrown with the error! + // */ + // def fromSeqOfOptionalEither2OptionalList[T](in: scala.concurrent.Future[Either[ClientError, Option[Seq[T]]]]): java.util.concurrent.Future[Optional[java.util.List[T]]] = { + // new MFuture[Either[ClientError, Option[Seq[T]]], Optional[java.util.List[T]]](in, a => fromSeqOfOptionalEither2OptionalList(a)) + // } + // + // /** + // * Transforms an optional into a Void if it is empty, else RuntimeException is thrown with the error! + // */ + // def fromOptional2Future(in: scala.concurrent.Future[Option[ClientError]]): java.util.concurrent.Future[Void] = { + // new MFuture[Option[ClientError], Void](in, a => extractOption(a)) + // } + + private def fromSequenceToList[T](in: Seq[T]): Optional[java.util.List[T]] = in match { + case Nil => Optional.empty() + case seq => Optional.of(new java.util.ArrayList[T](seq)) + + } + + // private def fromRightOptionOfEither2Option[R](in: Either[ClientError, Option[R]]): Optional[R] = in match { + // case Left(e) => throw new RuntimeException(e.msg) + // case Right(Some(value)) => Optional.of(value) + // case Right(None) => Optional.empty() + // } + // private def fromSeqOfOptionalEither2OptionalList[R](in: Either[ClientError, Option[Seq[R]]]): Optional[java.util.List[R]] = in match { + // case Left(e) => throw new RuntimeException(e.msg) + // case Right(None) => Optional.empty() + // case Right(Some(Nil)) => Optional.of(new java.util.ArrayList[R]()) + // case Right(Some(seq)) => Optional.of(new java.util.ArrayList[R](seq)) + // } + private def fromOptional2Optional[R](in: Option[R]): Optional[R] = in match { + case Some(value) => Optional.of(value) + case None => Optional.empty() + } + + private def convert[T](x: scala.concurrent.Future[Either[String, T]]): java.util.concurrent.Future[T] = + new MFuture[Either[String, T], T](x, a => extractEither(a)) } diff --git a/client/src/main/scala/org/zalando/nakadi/client/utils/ImplicitHelper.scala b/client/src/main/scala/org/zalando/nakadi/client/utils/ImplicitHelper.scala index 96579e0..778e388 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/utils/ImplicitHelper.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/utils/ImplicitHelper.scala @@ -1,23 +1,30 @@ package org.zalando.nakadi.client.utils +import org.zalando.nakadi.client.java.model._ +import org.zalando.nakadi.client.Deserializer +import org.zalando.nakadi.client.Serializer +import com.fasterxml.jackson.core.`type`.TypeReference +import org.zalando.nakadi.client.java.enumerator._ +import org.zalando.nakadi.client.java._ +import org.zalando.nakadi.client.java.model.JavaJacksonJsonMarshaller + /** - * Meant for usage in the Java client side, because Java just cannot(meme:badPokerface) handle implicits. + * Meant for usage in the Java client side. */ object Serialization { - - -// def defaultSerializer[T]():Serializer[T] = JacksonJsonMarshaller.serializer[T] -// def metricsDeserializer():Deserializer[Metrics] = JacksonJsonMarshaller.deserializer(JacksonJsonMarshaller.metricsTR) -// def eventTypeDeserializer():Deserializer[EventType] = JacksonJsonMarshaller.deserializer(JacksonJsonMarshaller.eventTypeTR) -// def customDeserializer[T](tr:TypeReference[T]):Deserializer[T] = JacksonJsonMarshaller.deserializer(tr) -// def partitionDeserializer():Deserializer[Partition] = JacksonJsonMarshaller.deserializer(JacksonJsonMarshaller.partitionTR) -// -// //Sequence -// def seqOfEventTypeDeserializer(): Deserializer[Seq[EventType]] = JacksonJsonMarshaller.deserializer(JacksonJsonMarshaller.listOfEventTypeTR) -// def seqOfPartitionDeserializer():Deserializer[Seq[Partition]] = JacksonJsonMarshaller.deserializer(JacksonJsonMarshaller.listOfPartitionTR) -// def seqOfEventValidationStrategy():Deserializer[Seq[EventValidationStrategy]] = JacksonJsonMarshaller.deserializer(JacksonJsonMarshaller.listOfEventValidationStrategyTR) -// def seqOfEventEnrichmentStrategy():Deserializer[Seq[EventEnrichmentStrategy]] = JacksonJsonMarshaller.deserializer(JacksonJsonMarshaller.listOfEventEnrichmentStrategyTR) -// def seqOfPartitionStrategy():Deserializer[Seq[PartitionStrategy]] = JacksonJsonMarshaller.deserializer(JacksonJsonMarshaller.listOfPartitionStrategyTR) + + def defaultSerializer[T](): Serializer[T] = JavaJacksonJsonMarshaller.serializer[T] + def metricsDeserializer(): Deserializer[Metrics] = JavaJacksonJsonMarshaller.deserializer(JavaJacksonJsonMarshaller.metricsTR) + def eventTypeDeserializer(): Deserializer[EventType] = JavaJacksonJsonMarshaller.deserializer(JavaJacksonJsonMarshaller.eventTypeTR) + def customDeserializer[T](tr: TypeReference[T]): Deserializer[T] = JavaJacksonJsonMarshaller.deserializer(tr) + def partitionDeserializer(): Deserializer[Partition] = JavaJacksonJsonMarshaller.deserializer(JavaJacksonJsonMarshaller.partitionTR) + + //Sequence + def seqOfEventTypeDeserializer(): Deserializer[java.util.List[EventType]] = JavaJacksonJsonMarshaller.deserializer(JavaJacksonJsonMarshaller.listOfEventTypeTR) + def seqOfPartitionDeserializer(): Deserializer[java.util.List[Partition]] = JavaJacksonJsonMarshaller.deserializer(JavaJacksonJsonMarshaller.listOfPartitionTR) + def seqOfEventValidationStrategy(): Deserializer[java.util.List[EventValidationStrategy]] = JavaJacksonJsonMarshaller.deserializer(JavaJacksonJsonMarshaller.listOfEventValidationStrategyTR) + def seqOfEventEnrichmentStrategy(): Deserializer[java.util.List[EventEnrichmentStrategy]] = JavaJacksonJsonMarshaller.deserializer(JavaJacksonJsonMarshaller.listOfEventEnrichmentStrategyTR) + def seqOfPartitionStrategy(): Deserializer[java.util.List[PartitionStrategy]] = JavaJacksonJsonMarshaller.deserializer(JavaJacksonJsonMarshaller.listOfPartitionStrategyTR) } \ No newline at end of file diff --git a/client/src/main/scala/org/zalando/nakadi/client/utils/Uri.scala b/client/src/main/scala/org/zalando/nakadi/client/utils/Uri.scala new file mode 100644 index 0000000..15f284d --- /dev/null +++ b/client/src/main/scala/org/zalando/nakadi/client/utils/Uri.scala @@ -0,0 +1,19 @@ +package org.zalando.nakadi.client.utils + +object Uri { + + final val URI_METRICS = "/metrics" + + /*Events*/ + final val URI_EVENT_TYPES = "/event-types" + final val URI_EVENT_TYPE_BY_NAME = "/event-types/%s" + final val URI_EVENTS_OF_EVENT_TYPE = "/event-types/%s/events" + + /*Partitions*/ + final val URI_PARTITIONS_BY_EVENT_TYPE = "/event-types/%s/partitions" + + /*Strategies*/ + final val URI_VALIDATION_STRATEGIES = "/registry/validation-strategies" + final val URI_ENRICHMENT_STRATEGIES = "/registry/enrichment-strategies" + final val URI_PARTITIONING_STRATEGIES = "/registry/partition-strategies" +} \ No newline at end of file diff --git a/client/src/test/scala/org/zalando/nakadi/client/scala/ClientTest.scala b/client/src/test/scala/org/zalando/nakadi/client/scala/ClientTest.scala index 47a2f96..cfc14d5 100644 --- a/client/src/test/scala/org/zalando/nakadi/client/scala/ClientTest.scala +++ b/client/src/test/scala/org/zalando/nakadi/client/scala/ClientTest.scala @@ -23,9 +23,11 @@ import akka.http.scaladsl.model.HttpProtocols import akka.http.scaladsl.model.HttpEntity import akka.http.scaladsl.model.ContentTypes import org.zalando.nakadi.client.scala.model.JacksonJsonMarshaller +import org.zalando.nakadi.client.utils.Uri class ClientTest extends WordSpec with Matchers with MockitoSugar with BeforeAndAfter { import JacksonJsonMarshaller._ + import Uri._ private var connection: Connection = mock[Connection] private val client: Client = new ClientImpl(connection) before { diff --git a/it/src/test/java/org/zalando/nakadi/client/examples/java/ClientExample.java b/it/src/main/java/org/zalando/nakadi/client/examples/java/ClientExample.java similarity index 68% rename from it/src/test/java/org/zalando/nakadi/client/examples/java/ClientExample.java rename to it/src/main/java/org/zalando/nakadi/client/examples/java/ClientExample.java index aa5a70e..8b42322 100644 --- a/it/src/test/java/org/zalando/nakadi/client/examples/java/ClientExample.java +++ b/it/src/main/java/org/zalando/nakadi/client/examples/java/ClientExample.java @@ -7,15 +7,15 @@ import java.util.function.Consumer; import org.zalando.nakadi.client.java.Client; -import org.zalando.nakadi.client.java.model.PartitionStrategy; -import org.zalando.nakadi.client.utils.ClientBuilder; +import org.zalando.nakadi.client.java.model.EventType; import org.zalando.nakadi.client.scala.ClientFactory; +import org.zalando.nakadi.client.utils.ClientBuilder; public class ClientExample { private static final String token = ClientFactory.getToken(); - private static Optional> unwrap( - Future>> result) + private static Optional> unwrap( + Future>> result) throws InterruptedException, ExecutionException { return result.get(); } @@ -29,14 +29,14 @@ public static void main(String[] args) throws InterruptedException, .withTokenProvider4Java(() -> token)// .buildJavaClient(); - Future>> result = client - .getPartitioningStrategies(); + Future>> result = client + .getEventTypes(); - Optional> opt = ClientExample.unwrap(result); + Optional> opt = ClientExample.unwrap(result); - opt.ifPresent(new Consumer>() { + opt.ifPresent(new Consumer>() { @Override - public void accept(List t) { + public void accept(List t) { System.out.println(">>>>" + t); } }); diff --git a/it/src/test/java/org/zalando/nakadi/client/examples/java/EventCreationExample.java b/it/src/main/java/org/zalando/nakadi/client/examples/java/EventCreationExample.java similarity index 61% rename from it/src/test/java/org/zalando/nakadi/client/examples/java/EventCreationExample.java rename to it/src/main/java/org/zalando/nakadi/client/examples/java/EventCreationExample.java index e9abd12..3275280 100644 --- a/it/src/test/java/org/zalando/nakadi/client/examples/java/EventCreationExample.java +++ b/it/src/main/java/org/zalando/nakadi/client/examples/java/EventCreationExample.java @@ -1,20 +1,22 @@ package org.zalando.nakadi.client.examples.java; -import java.util.ArrayList; import java.util.List; import org.zalando.nakadi.client.java.Client; +import org.zalando.nakadi.client.java.enumerator.EventEnrichmentStrategy; +import org.zalando.nakadi.client.java.enumerator.EventTypeCategory; +import org.zalando.nakadi.client.java.enumerator.EventValidationStrategy; +import org.zalando.nakadi.client.java.enumerator.PartitionStrategy; +import org.zalando.nakadi.client.java.enumerator.SchemaType; import org.zalando.nakadi.client.java.model.Event; -import org.zalando.nakadi.client.java.model.EventEnrichmentStrategy; import org.zalando.nakadi.client.java.model.EventType; -import org.zalando.nakadi.client.java.model.EventTypeCategory; import org.zalando.nakadi.client.java.model.EventTypeSchema; -import org.zalando.nakadi.client.java.model.EventValidationStrategy; -import org.zalando.nakadi.client.java.model.PartitionStrategy; -import org.zalando.nakadi.client.java.model.SchemaType; +import org.zalando.nakadi.client.java.model.EventTypeStatistics; import org.zalando.nakadi.client.scala.ClientFactory; import org.zalando.nakadi.client.utils.ClientBuilder; +import com.google.common.collect.Lists; + public class EventCreationExample { public class MeetingsEvent implements Event { @@ -49,24 +51,29 @@ public EventTypeSchema createEventTypeSchema(String schema) { return new EventTypeSchema(SchemaType.JSON, schema); } - public EventType createEventType(String name,EventTypeSchema eventTypeSchema) { - String owner = "team-laas"; + public EventType createEventType(String name, + EventTypeSchema eventTypeSchema) { + String owningApplication = "team-laas"; EventTypeCategory category = EventTypeCategory.UNDEFINED; - EventValidationStrategy validationStrategies = EventValidationStrategy.NONE; - EventEnrichmentStrategy enrichmentStrategies = null; + List validationStrategies = Lists + .newArrayList(EventValidationStrategy.NONE); + List enrichmentStrategies = Lists + .newArrayList(); PartitionStrategy partitionStrategy = PartitionStrategy.RANDOM; - List paritionKeyFields = new ArrayList("date", "topic"); - - return new EventType(name,// eventTypeName - owner,// owner - category,// category - validationStrategies,// validationStrategies - enrichmentStrategies,// enrichmentStrategies - partitionStrategy,// partitionStrategy - eventTypeSchema,// eventTypeSchema - null,// dataKeyFields - null,// partitionKeyFields - null);// statistics + + List dataKeyFields = null; + List partitionKeyFields = Lists.newArrayList("date", "topic"); + EventTypeStatistics statistics = null; + return new EventType(name, // + owningApplication, // + category, // + validationStrategies, // + enrichmentStrategies, // + partitionStrategy, // + eventTypeSchema, // + dataKeyFields, // + partitionKeyFields, // + statistics); } diff --git a/it/src/test/scala/org/zalando/nakadi/client/examples/scala/ClientCreationExample.scala b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/ClientCreationExample.scala similarity index 100% rename from it/src/test/scala/org/zalando/nakadi/client/examples/scala/ClientCreationExample.scala rename to it/src/main/scala/org/zalando/nakadi/client/examples/scala/ClientCreationExample.scala diff --git a/it/src/test/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala similarity index 100% rename from it/src/test/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala rename to it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala diff --git a/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactoryy.scala b/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactoryy.scala new file mode 100644 index 0000000..3fb35f8 --- /dev/null +++ b/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactoryy.scala @@ -0,0 +1,11 @@ +package org.zalando.nakadi.client.scala + +object ClientFactory { + def host():String = "nakadi-sandbox.my-test.fernan.do" + def OAuth2Token(): () => String = () => "" + def getToken():String = OAuth2Token().apply() + def port():Integer = 443 + def connection():Connection = Connection.newConnection(host, port, OAuth2Token(), true, false) + def client():Client = new ClientImpl(connection, "UTF-8") + +} \ No newline at end of file diff --git a/it/src/test/scala/org/zalando/nakadi/client/subscription/LeClient.scala b/it/src/main/scala/org/zalando/nakadi/client/subscription/LeClient.scala similarity index 100% rename from it/src/test/scala/org/zalando/nakadi/client/subscription/LeClient.scala rename to it/src/main/scala/org/zalando/nakadi/client/subscription/LeClient.scala diff --git a/it/src/main/scala/org/zalando/nakadi/client/subscription/MyEventExample.scala b/it/src/main/scala/org/zalando/nakadi/client/subscription/MyEventExample.scala new file mode 100644 index 0000000..3f3428d --- /dev/null +++ b/it/src/main/scala/org/zalando/nakadi/client/subscription/MyEventExample.scala @@ -0,0 +1,5 @@ +package org.zalando.nakadi.client.subscription + +import org.zalando.nakadi.client.scala.model.Event + +case class MyEventExample(orderNumber: String) extends Event \ No newline at end of file diff --git a/it/src/main/scala/org/zalando/nakadi/client/subscription/MyListener.scala b/it/src/main/scala/org/zalando/nakadi/client/subscription/MyListener.scala new file mode 100644 index 0000000..64adfd9 --- /dev/null +++ b/it/src/main/scala/org/zalando/nakadi/client/subscription/MyListener.scala @@ -0,0 +1,16 @@ +package org.zalando.nakadi.client.subscription + +import org.zalando.nakadi.client.scala.model.Cursor +import org.zalando.nakadi.client.scala.Listener +import org.zalando.nakadi.client.ClientError + +class MyListener extends Listener[MyEventExample] { + def id: String = "test" + def onError(sourceUrl: String, cursor: Cursor, error: ClientError): Unit = { + println("YOOOOOOOOOOOOOO ") + } + def onSubscribed(): Unit = ??? + def onUnsubscribed(): Unit = ??? + def onReceive(sourceUrl: String, cursor: Cursor, event: Seq[MyEventExample]): Unit = ??? + +} \ No newline at end of file diff --git a/it/src/test/scala/org/zalando/nakadi/client/subscription/Subscriber.scala b/it/src/main/scala/org/zalando/nakadi/client/subscription/Subscriber.scala similarity index 74% rename from it/src/test/scala/org/zalando/nakadi/client/subscription/Subscriber.scala rename to it/src/main/scala/org/zalando/nakadi/client/subscription/Subscriber.scala index 95afd03..58eccde 100644 --- a/it/src/test/scala/org/zalando/nakadi/client/subscription/Subscriber.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/subscription/Subscriber.scala @@ -8,9 +8,8 @@ import org.zalando.nakadi.client.scala.model.{ Cursor, Event } import com.fasterxml.jackson.core.`type`.TypeReference import org.zalando.nakadi.client.scala.model.JacksonJsonMarshaller import org.zalando.nakadi.client.scala.ClientFactory -import org.zalando.nakadi.client.scala.Listener -case class MyEventExample(orderNumber: String) extends Event + object Subscriber extends App { val a = new A() a.startListening() @@ -26,20 +25,12 @@ class A { implicit def myEventExampleTR: TypeReference[MyEventExample] = new TypeReference[MyEventExample] {} def startListening() = { - val listener = new Listener[MyEventExample] { - def id: String = "test" - def onError(sourceUrl: String, cursor: Cursor, error: ClientError): Unit = { - println("YOOOOOOOOOOOOOO ") - } - def onSubscribed(): Unit = ??? - def onUnsubscribed(): Unit = ??? - def onReceive(sourceUrl: String, cursor: Cursor, events: Seq[MyEventExample]): Unit = ??? - } + val listener = new MyListener val url = "/event-types/test-client-integration-event-1936085527-148383828851369665/events" val cr = Cursor(0, 170000) val params = new StreamParameters( - cursor = Some(cr), batchLimit = Some(10) // ,streamLimit=Some(10) - // ,streamTimeout=Some(10) + cursor = Some(cr), batchLimit = Some(500) // ,streamLimit=Some(10) +// ,streamTimeout=Some(10) // ,streamKeepAliveLimit =Some(10) ) client.subscribe(eventType, params, listener) diff --git a/it/src/test/scala/org/zalando/nakadi/client/ClientSubscriptionTest.scala b/it/src/test/scala/org/zalando/nakadi/client/ClientSubscriptionTest.scala index fdd070c..09f6028 100644 --- a/it/src/test/scala/org/zalando/nakadi/client/ClientSubscriptionTest.scala +++ b/it/src/test/scala/org/zalando/nakadi/client/ClientSubscriptionTest.scala @@ -8,6 +8,7 @@ import org.zalando.nakadi.client.scala.EventTypesActions import org.zalando.nakadi.client.scala.ClientFactory import org.zalando.nakadi.client.scala.EventActions import org.zalando.nakadi.client.scala.StreamParameters +import org.zalando.nakadi.client.scala.Listener class ClientSubscriptionTest extends WordSpec with Matchers with ModelFactory { import ClientFactory._ From 07260008f8e00f59a12aeb48e22af55391a3f813 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Fri, 29 Apr 2016 01:43:35 +0200 Subject: [PATCH 029/183] techjira:LAAS-60 Java Client almost done!! --- .../main/java/org/zalando/nakadi/client/java/ClientImpl.java | 5 +---- .../org/zalando/nakadi/client/utils/ClientBuilder.scala | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java b/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java index 1201a16..4785889 100644 --- a/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java +++ b/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java @@ -14,13 +14,11 @@ import org.zalando.nakadi.client.java.model.Metrics; import org.zalando.nakadi.client.java.model.Partition; import org.zalando.nakadi.client.scala.Connection; -import org.zalando.nakadi.client.utils.FutureConversions; import org.zalando.nakadi.client.utils.Serialization; import org.zalando.nakadi.client.utils.Uri; public class ClientImpl implements Client { private final Connection connection; - private final String charSet; //Deserializers private final Deserializer metricsDeserializer =Serialization.metricsDeserializer(); @@ -35,9 +33,8 @@ public class ClientImpl implements Client { private final Serializer eventTypeSerializer =Serialization.defaultSerializer(); private final Deserializer eventTypeDeserializer = Serialization.eventTypeDeserializer(); - public ClientImpl(Connection connection, String charSet) { + public ClientImpl(Connection connection) { this.connection = connection; - this.charSet = charSet; } @Override diff --git a/client/src/main/scala/org/zalando/nakadi/client/utils/ClientBuilder.scala b/client/src/main/scala/org/zalando/nakadi/client/utils/ClientBuilder.scala index 3ec4767..d30ea6c 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/utils/ClientBuilder.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/utils/ClientBuilder.scala @@ -65,7 +65,7 @@ class ClientBuilder private(host: String = "", // def buildJavaClient():org.zalando.nakadi.client.java.Client = { val connection =Connection.newConnection(host, port, tokenProvider, securedConnection, verifySSlCertificate) - new org.zalando.nakadi.client.java.ClientImpl(connection, "UTF-8") + new org.zalando.nakadi.client.java.ClientImpl(connection) } private def checkNotNull[T](subject: T): T = From 0bf7064857191d5d46d27ec9b9777bb4d596b554 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Fri, 29 Apr 2016 17:18:48 +0200 Subject: [PATCH 030/183] techjira:LAAS-60 Adding Java Model! --- .../zalando/nakadi/client/java/Client.java | 4 +- .../nakadi/client/java/ClientError.java | 20 ++ .../zalando/nakadi/client/java/Listener.java | 1 - .../nakadi/client/java/model/EventType.java | 314 +++++++++--------- .../zalando/nakadi/client/ClientError.scala | 3 - .../zalando/nakadi/client/scala/Client.scala | 58 +++- .../nakadi/client/scala/Listener.scala | 1 - .../nakadi/client/java/ClientImpl.java | 68 ++-- .../client/actor/EventConsumingActor.scala | 3 +- .../model/JavaJacksonJsonMarshaller.scala | 54 +-- .../nakadi/client/scala/ClientImpl.scala | 60 ++-- .../nakadi/client/scala/Connection.scala | 68 ++-- .../model/ScalaJacksonJsonMarshaller.scala | 2 +- .../client/utils/FutureConversions.scala | 5 +- .../nakadi/client/utils/ImplicitHelper.scala | 1 + .../org/zalando/nakadi/client/utils/Uri.scala | 18 +- .../nakadi/client/scala/TestFactory.scala | 19 -- .../examples/java/EventCreationExample.java | 89 +++-- .../nakadi/client/java/ClientTest.java | 5 + .../examples/scala/EventCreationExample.scala | 32 +- .../examples/scala/EventListenerExample.scala | 68 ++++ ...ientFactoryy.scala => ClientFactory.scala} | 7 +- .../client/subscription/MyListener.scala | 2 +- .../client/subscription/Subscriber.scala | 13 +- .../client/ClientSubscriptionTest.scala | 1 + .../client/EventTypesIntegrationTest.scala | 2 +- .../nakadi/client/KlientIntegrationTest.scala | 2 + 27 files changed, 560 insertions(+), 360 deletions(-) create mode 100644 api/src/main/java/org/zalando/nakadi/client/java/ClientError.java delete mode 100644 api/src/main/scala/org/zalando/nakadi/client/ClientError.scala create mode 100644 it/src/main/java/org/zalando/nakadi/client/java/ClientTest.java create mode 100644 it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala rename it/src/main/scala/org/zalando/nakadi/client/scala/{ClientFactoryy.scala => ClientFactory.scala} (59%) diff --git a/api/src/main/java/org/zalando/nakadi/client/java/Client.java b/api/src/main/java/org/zalando/nakadi/client/java/Client.java index 2751295..6c1097a 100644 --- a/api/src/main/java/org/zalando/nakadi/client/java/Client.java +++ b/api/src/main/java/org/zalando/nakadi/client/java/Client.java @@ -71,7 +71,7 @@ public interface Client { * @param serializer The custom serializer to serialize the event. * @return Void in case of success */ - Future publishEvent(String eventTypeName, T event, Serializer serializer); + Future publishEvent(String eventTypeName, T event, Serializer> serializer); /** * Publishes a single event to the given eventType.
      @@ -93,7 +93,7 @@ public interface Client { * @param serializer The custom serializer to serialize the events. * @return Void in case of success */ - Future publishEvents(String eventTypeName, List events, Serializer serializer); + Future publishEvents(String eventTypeName, List events, Serializer> serializer); /** * Publishes a List of events to the given eventType.
      * Partition selection is done using the defined partition resolution,
      diff --git a/api/src/main/java/org/zalando/nakadi/client/java/ClientError.java b/api/src/main/java/org/zalando/nakadi/client/java/ClientError.java new file mode 100644 index 0000000..9444c93 --- /dev/null +++ b/api/src/main/java/org/zalando/nakadi/client/java/ClientError.java @@ -0,0 +1,20 @@ +package org.zalando.nakadi.client.java; + +public class ClientError { + private final String msg; + private final Integer status; + + public ClientError(String msg, Integer status) { + this.msg = msg; + this.status = status; + } + + public String getMsg() { + return msg; + } + + public Integer getStatus() { + return status; + } + +} \ No newline at end of file diff --git a/api/src/main/java/org/zalando/nakadi/client/java/Listener.java b/api/src/main/java/org/zalando/nakadi/client/java/Listener.java index bc06d9c..1dc68c7 100644 --- a/api/src/main/java/org/zalando/nakadi/client/java/Listener.java +++ b/api/src/main/java/org/zalando/nakadi/client/java/Listener.java @@ -2,7 +2,6 @@ import java.util.List; -import org.zalando.nakadi.client.ClientError; import org.zalando.nakadi.client.java.model.Cursor; import org.zalando.nakadi.client.java.model.Event; diff --git a/api/src/main/java/org/zalando/nakadi/client/java/model/EventType.java b/api/src/main/java/org/zalando/nakadi/client/java/model/EventType.java index ecef610..6a9ff93 100644 --- a/api/src/main/java/org/zalando/nakadi/client/java/model/EventType.java +++ b/api/src/main/java/org/zalando/nakadi/client/java/model/EventType.java @@ -16,168 +16,158 @@ * */ public class EventType { - private final String name; - private final String owningApplication; - private final EventTypeCategory category; - private final List validationStrategies; - private final List enrichmentStrategies; - private final PartitionStrategy partitionStrategy; - private final EventTypeSchema schema; - private final List dataKeyFields; - private final List partitionKeyFields; - private final EventTypeStatistics statistics; - - /** - * An event type defines the schema and its runtime properties. - * - * @param name - * Name of this EventType. Encodes the owner/responsible for this - * EventType. The name for the EventType SHOULD follow the - * pattern, but is not enforced - * 'stups_owning_application.event-type', for example - * 'gizig.price-change'. The components of the name are: * - * Organization: the organizational unit where the team is - * located; can be omitted. * Team name: name of the team - * responsible for owning application; can be omitted. * Owning - * application: SHOULD match the field owning_application; - * indicates * EventType name: name of this EventType; SHOULD end - * in ChangeEvent for DataChangeEvents; MUST be in the past tense - * for BusinessEvents. (TBD: how to deal with organizational - * changes? Should be reflected on the name of the EventType? - * Isn't it better to omit [organization:team] completely?) - * @param owningApplication - * Indicator of the Application owning this `EventType`. - * @param category - * Defines the category of this EventType. The value set will - * influence, if not set otherwise, the default set of - * validation-strategies, enrichment-strategies, and the - * effective_schema in the following way: - `undefined`: No - * predefined changes apply. `effective_schema` is exactly the - * same as the `EventTypeSchema`. Default validation_strategy for - * this `EventType` is `[{name: 'schema-validation'}]`. - `data`: - * Events of this category will be DataChangeEvents. - * `effective_schema` contains `metadata`, and adds fields - * `data_op` and `data_type`. The passed EventTypeSchema defines - * the schema of `data`. Default validation_strategy for this - * `EventType` is `[{name: 'datachange-schema-validation'}]`. - - * `business`: Events of this category will be BusinessEvents. - * `effective_schema` contains `metadata` and any additionally - * defined properties passed in the `EventTypeSchema` directly on - * top level of the Event. If name conflicts arise, creation of - * this EventType will be rejected. Default validation_strategy - * for this `EventType` is `[{name: 'schema-validation'}]`. - * @param validationStrategies - * Determines the validation that has to be executed upon - * reception of Events of this type. Events are rejected if any - * of the rules fail (see details of Problem response on the - * Event publishing methods). Rule evaluation order is the same - * as in this array. If not explicitly set, default value will - * respect the definition of the `EventType.category`. - * @param enrichmentStrategies - * Determines the enrichment to be performed on an Event upon - * reception. Enrichment is performed once upon reception (and - * after validation) of an Event and is only possible on fields - * that are not defined on the incoming Event. See documentation - * for the write operation for details on behaviour in case of - * unsuccessful enrichment. - * @param partitionStrategy - * Determines how the assignment of the event to a Partition - * should be handled. - * @param schema - * The schema for this EventType. This is expected to be a json - * schema in yaml format (other formats might be added in the - * future). - * @param dataKeyFields - * Indicators of the path of the properties that constitute the - * primary key (identifier) of the data within this Event. If set - * MUST be a valid required field as defined in the schema. (TBD - * should be required? Is applicable only to both Business and - * DataChange Events?) - * @param partitioningKeyFields - * Indicator of the field used for guaranteeing the ordering of - * Events of this type (used by the PartitionResolutionStrategy). - * If set MUST be a valid required field as defined in the - * schema. - * @param statistics - * Statistics of this EventType used for optimization purposes. - * Internal use of these values might change over time. (TBD: - * measured statistics). - * - */ - @JsonCreator - public EventType( - @JsonProperty("name") - String name, - @JsonProperty("owning_application") - String owningApplication, - @JsonProperty("category") - EventTypeCategory category, - - @JsonProperty("validation_strategies") - List validationStrategies, - @JsonProperty("enrichment_strategies") - List enrichmentStrategies, - @JsonProperty("partition_strategy") - PartitionStrategy partitionStrategy, - @JsonProperty("schema") - EventTypeSchema schema, - @JsonProperty("data_key_fields") - List dataKeyFields, - @JsonProperty("partition_key_fields") - List partitionKeyFields, - @JsonProperty("statistics") - EventTypeStatistics statistics) { - this.name = name; - this.owningApplication = owningApplication; - this.category = category; - this.validationStrategies = validationStrategies; - this.enrichmentStrategies = enrichmentStrategies; - this.partitionStrategy = partitionStrategy; - this.schema = schema; - this.dataKeyFields = dataKeyFields; - this.partitionKeyFields = partitionKeyFields; - this.statistics = statistics; - } - - public String getName() { - return name; - } - - public String getOwningApplication() { - return owningApplication; - } - - public EventTypeCategory getCategory() { - return category; - } - - public List getValidationStrategies() { - return validationStrategies; - } - - public List getEnrichmentStrategies() { - return enrichmentStrategies; - } - - public PartitionStrategy getPartitionStrategy() { - return partitionStrategy; - } - - public EventTypeSchema getSchema() { - return schema; - } - - public List getDataKeyFields() { - return dataKeyFields; - } - - public List getPartitionKeyFields() { - return partitionKeyFields; - } - - public EventTypeStatistics getStatistics() { - return statistics; - } + private final String name; + private final String owningApplication; + private final EventTypeCategory category; + private final List validationStrategies; + private final List enrichmentStrategies; + private final PartitionStrategy partitionStrategy; + private final EventTypeSchema schema; + private final List dataKeyFields; + private final List partitionKeyFields; + private final EventTypeStatistics statistics; + + /** + * An event type defines the schema and its runtime properties. + * + * @param name + * Name of this EventType. Encodes the owner/responsible for this + * EventType. The name for the EventType SHOULD follow the + * pattern, but is not enforced + * 'stups_owning_application.event-type', for example + * 'gizig.price-change'. The components of the name are: * + * Organization: the organizational unit where the team is + * located; can be omitted. * Team name: name of the team + * responsible for owning application; can be omitted. * Owning + * application: SHOULD match the field owning_application; + * indicates * EventType name: name of this EventType; SHOULD end + * in ChangeEvent for DataChangeEvents; MUST be in the past tense + * for BusinessEvents. (TBD: how to deal with organizational + * changes? Should be reflected on the name of the EventType? + * Isn't it better to omit [organization:team] completely?) + * @param owningApplication + * Indicator of the Application owning this `EventType`. + * @param category + * Defines the category of this EventType. The value set will + * influence, if not set otherwise, the default set of + * validation-strategies, enrichment-strategies, and the + * effective_schema in the following way: - `undefined`: No + * predefined changes apply. `effective_schema` is exactly the + * same as the `EventTypeSchema`. Default validation_strategy for + * this `EventType` is `[{name: 'schema-validation'}]`. - `data`: + * Events of this category will be DataChangeEvents. + * `effective_schema` contains `metadata`, and adds fields + * `data_op` and `data_type`. The passed EventTypeSchema defines + * the schema of `data`. Default validation_strategy for this + * `EventType` is `[{name: 'datachange-schema-validation'}]`. - + * `business`: Events of this category will be BusinessEvents. + * `effective_schema` contains `metadata` and any additionally + * defined properties passed in the `EventTypeSchema` directly on + * top level of the Event. If name conflicts arise, creation of + * this EventType will be rejected. Default validation_strategy + * for this `EventType` is `[{name: 'schema-validation'}]`. + * @param validationStrategies + * Determines the validation that has to be executed upon + * reception of Events of this type. Events are rejected if any + * of the rules fail (see details of Problem response on the + * Event publishing methods). Rule evaluation order is the same + * as in this array. If not explicitly set, default value will + * respect the definition of the `EventType.category`. + * @param enrichmentStrategies + * Determines the enrichment to be performed on an Event upon + * reception. Enrichment is performed once upon reception (and + * after validation) of an Event and is only possible on fields + * that are not defined on the incoming Event. See documentation + * for the write operation for details on behaviour in case of + * unsuccessful enrichment. + * @param partitionStrategy + * Determines how the assignment of the event to a Partition + * should be handled. + * @param schema + * The schema for this EventType. This is expected to be a json + * schema in yaml format (other formats might be added in the + * future). + * @param dataKeyFields + * Indicators of the path of the properties that constitute the + * primary key (identifier) of the data within this Event. If set + * MUST be a valid required field as defined in the schema. (TBD + * should be required? Is applicable only to both Business and + * DataChange Events?) + * @param partitioningKeyFields + * Indicator of the field used for guaranteeing the ordering of + * Events of this type (used by the PartitionResolutionStrategy). + * If set MUST be a valid required field as defined in the + * schema. + * @param statistics + * Statistics of this EventType used for optimization purposes. + * Internal use of these values might change over time. (TBD: + * measured statistics). + * + */ + @JsonCreator + public EventType( + @JsonProperty("name") String name, + @JsonProperty("owning_application") String owningApplication, + @JsonProperty("category") EventTypeCategory category, + + @JsonProperty("validation_strategies") List validationStrategies, + @JsonProperty("enrichment_strategies") List enrichmentStrategies, + @JsonProperty("partition_strategy") PartitionStrategy partitionStrategy, + @JsonProperty("schema") EventTypeSchema schema, + @JsonProperty("data_key_fields") List dataKeyFields, + @JsonProperty("partition_key_fields") List partitionKeyFields, + @JsonProperty("statistics") EventTypeStatistics statistics) { + this.name = name; + this.owningApplication = owningApplication; + this.category = category; + this.validationStrategies = validationStrategies; + this.enrichmentStrategies = enrichmentStrategies; + this.partitionStrategy = partitionStrategy; + this.schema = schema; + this.dataKeyFields = dataKeyFields; + this.partitionKeyFields = partitionKeyFields; + this.statistics = statistics; + } + + public String getName() { + return name; + } + + public String getOwningApplication() { + return owningApplication; + } + + public EventTypeCategory getCategory() { + return category; + } + + public List getValidationStrategies() { + return validationStrategies; + } + + public List getEnrichmentStrategies() { + return enrichmentStrategies; + } + + public PartitionStrategy getPartitionStrategy() { + return partitionStrategy; + } + + public EventTypeSchema getSchema() { + return schema; + } + + public List getDataKeyFields() { + return dataKeyFields; + } + + public List getPartitionKeyFields() { + return partitionKeyFields; + } + + public EventTypeStatistics getStatistics() { + return statistics; + } @Override public String toString() { diff --git a/api/src/main/scala/org/zalando/nakadi/client/ClientError.scala b/api/src/main/scala/org/zalando/nakadi/client/ClientError.scala deleted file mode 100644 index 903fb83..0000000 --- a/api/src/main/scala/org/zalando/nakadi/client/ClientError.scala +++ /dev/null @@ -1,3 +0,0 @@ -package org.zalando.nakadi.client - -case class ClientError(msg: String, status: Option[Int]) \ No newline at end of file diff --git a/api/src/main/scala/org/zalando/nakadi/client/scala/Client.scala b/api/src/main/scala/org/zalando/nakadi/client/scala/Client.scala index 18bf5fa..adf95f1 100644 --- a/api/src/main/scala/org/zalando/nakadi/client/scala/Client.scala +++ b/api/src/main/scala/org/zalando/nakadi/client/scala/Client.scala @@ -5,9 +5,9 @@ import org.zalando.nakadi.client.Deserializer import org.zalando.nakadi.client.Serializer import org.zalando.nakadi.client.scala.model._ import org.zalando.nakadi.client._ +import com.fasterxml.jackson.core.`type`.TypeReference - - +case class ClientError(msg: String, status: Option[Int]) trait Client { @@ -18,7 +18,7 @@ trait Client { * curl --request GET /metrics * }}} */ - def getMetrics()(implicit des: Deserializer[Metrics]): Future[Either[ClientError, Option[Metrics]]] + def getMetrics(): Future[Either[ClientError, Option[Metrics]]] /** * Returns a list of all registered EventTypes. @@ -28,7 +28,7 @@ trait Client { * }}} * */ - def getEventTypes()(implicit des: Deserializer[Seq[EventType]]): Future[Either[ClientError, Option[Seq[EventType]]]] + def getEventTypes(): Future[Either[ClientError, Option[Seq[EventType]]]] /** * Creates a new EventType. @@ -40,7 +40,7 @@ trait Client { * @param event - The EventType to create. * */ - def createEventType(eventType: EventType)(implicit ser: Serializer[EventType]): Future[Option[ClientError]] + def createEventType(eventType: EventType): Future[Option[ClientError]] /** * Returns the EventType identified by its name. @@ -49,7 +49,7 @@ trait Client { * }}} * @param eventTypeName - Name of the EventType */ - def getEventType(eventTypeName: String)(implicit des: Deserializer[EventType]): Future[Either[ClientError, Option[EventType]]] + def getEventType(eventTypeName: String): Future[Either[ClientError, Option[EventType]]] /** * Updates the EventType identified by its name. * {{{ @@ -58,7 +58,7 @@ trait Client { * @param eventTypeName - Name of the EventType * @param event - Event to update */ - def updateEventType(eventTypeName: String, eventType: EventType)(implicit ser: Serializer[EventType]): Future[Option[ClientError]] + def updateEventType(eventTypeName: String, eventType: EventType): Future[Option[ClientError]] /** * Deletes an EventType identified by its name. * @@ -78,7 +78,16 @@ trait Client { * @param eventTypeName - Name of the EventType * @param event - Event to publish */ - def publishEvents[T <: Event](eventTypeName: String, events: Seq[T])(implicit ser: Serializer[Seq[T]]): Future[Option[ClientError]] + def publishEvents[T <: Event](eventTypeName: String, events: Seq[T], ser: Serializer[Seq[T]]): Future[Option[ClientError]] + /** + * Publishes multiple Events for the given EventType. + * {{{ + * curl --request POST -d @fileWithEvent /event-types/{name}/events + * }}} + * @param eventTypeName - Name of the EventType + * @param event - Event to publish + */ + def publishEvents[T <: Event](eventTypeName: String, events: Seq[T]): Future[Option[ClientError]] /** * Publishes a single Events for the given EventType. @@ -89,7 +98,18 @@ trait Client { * @param event - Event to publish * */ - def publishEvent[T <: Event](eventTypeName: String, event: T)(implicit ser: Serializer[T]): Future[Option[ClientError]] + def publishEvent[T <: Event](eventTypeName: String, event: T, ser: Serializer[T]): Future[Option[ClientError]] + + /** + * Publishes a single Events for the given EventType. + * {{{ + * curl --request POST -d @fileWithEvent /event-types/{name}/events + * }}} + * @param eventTypeName - Name of the EventType + * @param event - Event to publish + * + */ + def publishEvent[T <: Event](eventTypeName: String, event: T): Future[Option[ClientError]] /** * List the partitions for the given EventType. @@ -98,7 +118,7 @@ trait Client { * }}} * @param eventTypeName - Name of the EventType */ - def getPartitions(eventTypeName: String)(implicit des: Deserializer[Seq[Partition]]): Future[Either[ClientError, Option[Seq[Partition]]]] + def getPartitions(eventTypeName: String): Future[Either[ClientError, Option[Seq[Partition]]]] /** * Returns all of the validation strategies supported by this installation of Nakadi. @@ -107,7 +127,7 @@ trait Client { * curl --request GET /registry/validation-strategies * }}} */ - def getValidationStrategies()(implicit des: Deserializer[Seq[EventValidationStrategy.Value]]): Future[Either[ClientError, Option[Seq[EventValidationStrategy.Value]]]] + def getValidationStrategies(): Future[Either[ClientError, Option[Seq[EventValidationStrategy.Value]]]] /** * Returns all of the enrichment strategies supported by this installation of Nakadi. @@ -116,7 +136,7 @@ trait Client { * }}} */ - def getEnrichmentStrategies()(implicit des: Deserializer[Seq[EventEnrichmentStrategy.Value]]): Future[Either[ClientError, Option[Seq[EventEnrichmentStrategy.Value]]]] + def getEnrichmentStrategies(): Future[Either[ClientError, Option[Seq[EventEnrichmentStrategy.Value]]]] /** * Returns all of the partitioning strategies supported by this installation of Nakadi. @@ -124,13 +144,13 @@ trait Client { * curl --request GET /registry/partitioning-strategies * }}} */ - def getPartitioningStrategies()(implicit des: Deserializer[Seq[PartitionStrategy.Value]]): Future[Either[ClientError, Option[Seq[PartitionStrategy.Value]]]] + def getPartitioningStrategies(): Future[Either[ClientError, Option[Seq[PartitionStrategy.Value]]]] /** * Shuts down the communication system of the client */ - def stop(): Future[Option[ClientError]] + def stop(): Option[ClientError] /** * Registers the subscription of a listener to start streaming events from a partition in non-blocking fashion. @@ -138,8 +158,18 @@ trait Client { * @eventType - Name of the EventType to listen for. * @parameters - Parameters for the streaming of events. * @listener - Listener to pass the event to when it is received. + * @des - Json Marshaller(implicit) to deserialize the event to Json. */ def subscribe[T <: Event](eventTypeName: String, parameters: StreamParameters, listener: Listener[T])(implicit des: Deserializer[T]): Future[Option[ClientError]] + /** + * Registers the subscription of a listener to start streaming events from a partition in non-blocking fashion. + * + * @eventType - Name of the EventType to listen for. + * @parameters - Parameters for the streaming of events. + * @listener - Listener to pass the event to when it is received. + * @typeRef - TypeReference/Helper for using with the Jackson-Objectmapper to deserializing the event to json. + */ + def subscribe[T <: Event](eventTypeName: String, parameters: StreamParameters, listener: Listener[T], typeRef: TypeReference[T]): Future[Option[ClientError]] /** * Removes the subscription of a listener, to stop streaming events from a partition. * diff --git a/api/src/main/scala/org/zalando/nakadi/client/scala/Listener.scala b/api/src/main/scala/org/zalando/nakadi/client/scala/Listener.scala index 33d99cd..c9b4d7c 100644 --- a/api/src/main/scala/org/zalando/nakadi/client/scala/Listener.scala +++ b/api/src/main/scala/org/zalando/nakadi/client/scala/Listener.scala @@ -1,6 +1,5 @@ package org.zalando.nakadi.client.scala -import org.zalando.nakadi.client.ClientError import org.zalando.nakadi.client.scala.model.Cursor import org.zalando.nakadi.client.scala.model.Event diff --git a/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java b/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java index 4785889..21c3286 100644 --- a/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java +++ b/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java @@ -1,5 +1,6 @@ package org.zalando.nakadi.client.java; +import java.util.Arrays; import java.util.List; import java.util.Optional; import java.util.concurrent.Future; @@ -19,19 +20,28 @@ public class ClientImpl implements Client { private final Connection connection; - - //Deserializers - private final Deserializer metricsDeserializer =Serialization.metricsDeserializer(); - private final Deserializer partitionDeserializer = Serialization.partitionDeserializer(); - //List Deserializers - private final Deserializer> seqOfEventTypeDeserializer =Serialization.seqOfEventTypeDeserializer(); - private final Deserializer> seqOfPartitionDeserializer =Serialization.seqOfPartitionDeserializer(); - private final Deserializer> seqOfEventValidationStrategy =Serialization.seqOfEventValidationStrategy(); - private final Deserializer> seqOfEventEnrichmentStrategy =Serialization.seqOfEventEnrichmentStrategy(); - private final Deserializer> seqOfPartitionStrategy =Serialization.seqOfPartitionStrategy(); - //Serializers - private final Serializer eventTypeSerializer =Serialization.defaultSerializer(); - private final Deserializer eventTypeDeserializer = Serialization.eventTypeDeserializer(); + + // Deserializers + private final Deserializer metricsDeserializer = Serialization + .metricsDeserializer(); + private final Deserializer partitionDeserializer = Serialization + .partitionDeserializer(); + // List Deserializers + private final Deserializer> seqOfEventTypeDeserializer = Serialization + .seqOfEventTypeDeserializer(); + private final Deserializer> seqOfPartitionDeserializer = Serialization + .seqOfPartitionDeserializer(); + private final Deserializer> seqOfEventValidationStrategy = Serialization + .seqOfEventValidationStrategy(); + private final Deserializer> seqOfEventEnrichmentStrategy = Serialization + .seqOfEventEnrichmentStrategy(); + private final Deserializer> seqOfPartitionStrategy = Serialization + .seqOfPartitionStrategy(); + // Serializers + private final Serializer eventTypeSerializer = Serialization + .defaultSerializer(); + private final Deserializer eventTypeDeserializer = Serialization + .eventTypeDeserializer(); public ClientImpl(Connection connection) { this.connection = connection; @@ -40,19 +50,20 @@ public ClientImpl(Connection connection) { @Override public Future> getMetrics() { - return connection.get4Java(Uri.URI_METRICS(), metricsDeserializer); + return connection.get4Java(Uri.URI_METRICS(), metricsDeserializer); } @Override public Future>> getEventTypes() { - return connection.get4Java(Uri.URI_EVENT_TYPES(), seqOfEventTypeDeserializer); + return connection.get4Java(Uri.URI_EVENT_TYPES(), + seqOfEventTypeDeserializer); } @Override public Future createEventType(EventType eventType) { - // TODO Auto-generated method stub - return null; + return connection.post4Java(Uri.URI_EVENT_TYPES(), eventType, + eventTypeSerializer); } @Override @@ -76,30 +87,28 @@ public Future deleteEventType(String eventTypeName) { @Override public Future publishEvent(String eventTypeName, - T event, Serializer serializer) { - // TODO Auto-generated method stub - return null; + T event, Serializer> serializer) { + return publishEvents(eventTypeName, Arrays.asList(event), serializer); } @Override public Future publishEvent(String eventTypeName, T event) { - // TODO Auto-generated method stub - return null; + return publishEvents(eventTypeName, Arrays.asList(event)); } @Override public Future publishEvents(String eventTypeName, - List events, Serializer serializer) { - // TODO Auto-generated method stub - return null; + List events, Serializer> serializer) { + return connection.post4Java(Uri.getEventStreamingUri(eventTypeName), + events, serializer); } @Override public Future publishEvents(String eventTypeName, List events) { - // TODO Auto-generated method stub - return null; + return publishEvents(eventTypeName, events, + Serialization.defaultSerializer()); } @Override @@ -123,7 +132,8 @@ public Future>> getEnrichmentStrategies() @Override public Future>> getPartitioningStrategies() { - return connection.get4Java(Uri.URI_PARTITIONING_STRATEGIES(), seqOfPartitionStrategy); + return connection.get4Java(Uri.URI_PARTITIONING_STRATEGIES(), + seqOfPartitionStrategy); } @Override @@ -147,6 +157,4 @@ public Future unsubscribe(String eventTypeName, return null; } - - } \ No newline at end of file diff --git a/client/src/main/scala/org/zalando/nakadi/client/actor/EventConsumingActor.scala b/client/src/main/scala/org/zalando/nakadi/client/actor/EventConsumingActor.scala index ba996c8..ec3a9cc 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/actor/EventConsumingActor.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/actor/EventConsumingActor.scala @@ -7,7 +7,6 @@ import akka.stream.actor.ActorSubscriberMessage.OnNext import akka.stream.actor.RequestStrategy import akka.util.ByteString import org.zalando.nakadi.client.Deserializer -import org.zalando.nakadi.client.ClientError import org.zalando.nakadi.client.scala.model.JacksonJsonMarshaller import com.fasterxml.jackson.core.`type`.TypeReference import org.zalando.nakadi.client.scala.model._ @@ -35,7 +34,7 @@ class EventConsumer[T <: Event](url: String, listener: Listener[T], des: Deseria override def receive: Receive = { case OnNext(msg: ByteString) => val message = msg.utf8String - + log.info("##############"+message) if (message.contains("events")) { count += 1 log.info("[Got event nr {} for {} and with msg {} ] ", count, url, message) diff --git a/client/src/main/scala/org/zalando/nakadi/client/java/model/JavaJacksonJsonMarshaller.scala b/client/src/main/scala/org/zalando/nakadi/client/java/model/JavaJacksonJsonMarshaller.scala index 7c597dd..d7f55ed 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/java/model/JavaJacksonJsonMarshaller.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/java/model/JavaJacksonJsonMarshaller.scala @@ -18,47 +18,49 @@ import com.fasterxml.jackson.databind.SerializationFeature import com.fasterxml.jackson.databind.deser.DeserializationProblemHandler import com.fasterxml.jackson.module.scala.DefaultScalaModule import com.typesafe.scalalogging.Logger +import com.fasterxml.jackson.databind.annotation.JsonSerialize.Inclusion + object JavaJacksonJsonMarshaller { val logger = Logger(LoggerFactory.getLogger(this.getClass)) // All TypeReferences - implicit def problemTR: TypeReference[Problem] = new TypeReference[Problem] {} - implicit def metricsTR: TypeReference[Metrics] = new TypeReference[Metrics] {} - implicit def partitionTR: TypeReference[Partition] = new TypeReference[Partition] {} - implicit def cursorTR: TypeReference[Cursor] = new TypeReference[Cursor] {} - implicit def eventTypeSchemaTR: TypeReference[EventTypeSchema] = new TypeReference[EventTypeSchema] {} - implicit def eventValidationStrategyTR: TypeReference[EventValidationStrategy] = new TypeReference[EventValidationStrategy] {} - implicit def partitionResolutionStrategyTR: TypeReference[PartitionStrategy] = new TypeReference[PartitionStrategy] {} - implicit def eventEnrichmentStrategyTR: TypeReference[EventEnrichmentStrategy] = new TypeReference[EventEnrichmentStrategy] {} - implicit def dataChangeEventQualifierTR: TypeReference[DataChangeEventQualifier] = new TypeReference[DataChangeEventQualifier] {} - implicit def eventTypeStatisticsTR: TypeReference[EventTypeStatistics] = new TypeReference[EventTypeStatistics] {} - implicit def eventTypeTR: TypeReference[EventType] = new TypeReference[EventType] {} - implicit def eventTR: TypeReference[Event] = new TypeReference[Event] {} - implicit def eventStreamBatchTR: TypeReference[EventStreamBatch[_]] = new TypeReference[EventStreamBatch[_]] {} + def problemTR: TypeReference[Problem] = new TypeReference[Problem] {} + def metricsTR: TypeReference[Metrics] = new TypeReference[Metrics] {} + def partitionTR: TypeReference[Partition] = new TypeReference[Partition] {} + def cursorTR: TypeReference[Cursor] = new TypeReference[Cursor] {} + def eventTypeSchemaTR: TypeReference[EventTypeSchema] = new TypeReference[EventTypeSchema] {} + def eventValidationStrategyTR: TypeReference[EventValidationStrategy] = new TypeReference[EventValidationStrategy] {} + def partitionResolutionStrategyTR: TypeReference[PartitionStrategy] = new TypeReference[PartitionStrategy] {} + def eventEnrichmentStrategyTR: TypeReference[EventEnrichmentStrategy] = new TypeReference[EventEnrichmentStrategy] {} + def dataChangeEventQualifierTR: TypeReference[DataChangeEventQualifier] = new TypeReference[DataChangeEventQualifier] {} + def eventTypeStatisticsTR: TypeReference[EventTypeStatistics] = new TypeReference[EventTypeStatistics] {} + def eventTypeTR: TypeReference[EventType] = new TypeReference[EventType] {} + def eventTR: TypeReference[Event] = new TypeReference[Event] {} + def eventStreamBatchTR: TypeReference[EventStreamBatch[_]] = new TypeReference[EventStreamBatch[_]] {} - implicit def eventMetadataTR: TypeReference[EventMetadata] = new TypeReference[EventMetadata] {} - implicit def businessEventTR: TypeReference[BusinessEvent] = new TypeReference[BusinessEvent] {} - implicit def batchItemResponseTR: TypeReference[BatchItemResponse] = new TypeReference[BatchItemResponse] {} - implicit def dataChangeEventTR: TypeReference[DataChangeEvent[Any]] = new TypeReference[DataChangeEvent[Any]] {} + def eventMetadataTR: TypeReference[EventMetadata] = new TypeReference[EventMetadata] {} + def businessEventTR: TypeReference[BusinessEvent] = new TypeReference[BusinessEvent] {} + def batchItemResponseTR: TypeReference[BatchItemResponse] = new TypeReference[BatchItemResponse] {} + def dataChangeEventTR: TypeReference[DataChangeEvent[Any]] = new TypeReference[DataChangeEvent[Any]] {} //Lists - implicit def listOfPartitionStrategyTR: TypeReference[java.util.List[PartitionStrategy]] = new TypeReference[java.util.List[PartitionStrategy]] {} - implicit def listOfEventValidationStrategyTR: TypeReference[java.util.List[EventValidationStrategy]] = new TypeReference[java.util.List[EventValidationStrategy]] {} - implicit def listOfEventEnrichmentStrategyTR: TypeReference[java.util.List[EventEnrichmentStrategy]] = new TypeReference[java.util.List[EventEnrichmentStrategy]] {} - implicit def listOfEventTypeTR: TypeReference[java.util.List[EventType]] = new TypeReference[java.util.List[EventType]] {} - implicit def listOfPartitionTR: TypeReference[java.util.List[Partition]] = new TypeReference[java.util.List[Partition]] {} + def listOfPartitionStrategyTR: TypeReference[java.util.List[PartitionStrategy]] = new TypeReference[java.util.List[PartitionStrategy]] {} + def listOfEventValidationStrategyTR: TypeReference[java.util.List[EventValidationStrategy]] = new TypeReference[java.util.List[EventValidationStrategy]] {} + def listOfEventEnrichmentStrategyTR: TypeReference[java.util.List[EventEnrichmentStrategy]] = new TypeReference[java.util.List[EventEnrichmentStrategy]] {} + def listOfEventTypeTR: TypeReference[java.util.List[EventType]] = new TypeReference[java.util.List[EventType]] {} + def listOfPartitionTR: TypeReference[java.util.List[Partition]] = new TypeReference[java.util.List[Partition]] {} - implicit def optionalDeserializer[T](implicit expectedType: TypeReference[T]): Deserializer[Option[T]] = new Deserializer[Option[T]] { + def optionalDeserializer[T]( expectedType: TypeReference[T]): Deserializer[Option[T]] = new Deserializer[Option[T]] { def from(from: String): Option[T] = { logger.debug("json: {}",from) defaultObjectMapper.readValue[Option[T]](from, expectedType) } } - implicit def serializer[T]: Serializer[T] = new Serializer[T] { + def serializer[T]: Serializer[T] = new Serializer[T] { def to(from: T): String = defaultObjectMapper.writeValueAsString(from) } - implicit def deserializer[T](implicit expectedType: TypeReference[T]): Deserializer[T] = new Deserializer[T] { + def deserializer[T]( expectedType: TypeReference[T]): Deserializer[T] = new Deserializer[T] { def from(from: String): T = { logger.debug("json: {}",from) defaultObjectMapper.readValue[T](from, expectedType) @@ -69,7 +71,7 @@ object JavaJacksonJsonMarshaller { .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) .setPropertyNamingStrategy(PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES) - .setSerializationInclusion(JsonInclude.Include.NON_NULL) + .setSerializationInclusion(JsonInclude.Include.NON_EMPTY) .addHandler(new DeserializationProblemHandler() { override def handleUnknownProperty(ctxt: DeserializationContext, jp: JsonParser, deserializer: JsonDeserializer[_], diff --git a/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala index 7a01f39..4e8ddf3 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala @@ -18,33 +18,34 @@ import org.zalando.nakadi.client.scala.model.JacksonJsonMarshaller import org.zalando.nakadi.client.Deserializer import org.zalando.nakadi.client.Serializer import org.zalando.nakadi.client.scala.model._ -import org.zalando.nakadi.client.ClientError import org.zalando.nakadi.client.utils.Uri +import com.fasterxml.jackson.core.`type`.TypeReference private[client] class ClientImpl(connection: Connection, charSet: String = "UTF-8") extends Client with HttpFactory { implicit val materializer = connection.materializer import Uri._ - + import JacksonJsonMarshaller._ +// implicit val val logger = Logger(LoggerFactory.getLogger(this.getClass)) - def getMetrics()(implicit des: Deserializer[Metrics]): Future[Either[ClientError, Option[Metrics]]] = { - logFutureEither(connection.get(URI_METRICS).flatMap(mapToEither(_))) + def getMetrics(): Future[Either[ClientError, Option[Metrics]]] = { + logFutureEither(connection.get(URI_METRICS).flatMap(mapToEither(_)(deserializer(metricsTR)))) } - def getEventTypes()(implicit des: Deserializer[Seq[EventType]]): Future[Either[ClientError, Option[Seq[EventType]]]] = { - logFutureEither(connection.get(URI_EVENT_TYPES).flatMap(mapToEither(_))) + def getEventTypes(): Future[Either[ClientError, Option[Seq[EventType]]]] = { + logFutureEither(connection.get(URI_EVENT_TYPES).flatMap(mapToEither(_)(deserializer(listOfEventTypeTR)))) } - def createEventType(eventType: EventType)(implicit ser: Serializer[EventType]): Future[Option[ClientError]] = { + def createEventType(eventType: EventType): Future[Option[ClientError]] = { logFutureOption(connection.post(URI_EVENT_TYPES, eventType).flatMap(mapToOption(_))) } - def getEventType(name: String)(implicit des: Deserializer[EventType]): Future[Either[ClientError, Option[EventType]]] = { - logFutureEither(connection.get(URI_EVENT_TYPE_BY_NAME.format(name)).flatMap(in => mapToEither(in))) + def getEventType(name: String): Future[Either[ClientError, Option[EventType]]] = { + logFutureEither(connection.get(URI_EVENT_TYPE_BY_NAME.format(name)).flatMap(in => mapToEither(in)(deserializer(eventTypeTR)))) } - def updateEventType(name: String, eventType: EventType)(implicit ser: Serializer[EventType]): Future[Option[ClientError]] = { + def updateEventType(name: String, eventType: EventType): Future[Option[ClientError]] = { val result = connection.put(URI_EVENT_TYPE_BY_NAME.format(name), eventType) logFutureOption(result.flatMap(in => mapToOption(in))) } @@ -53,35 +54,46 @@ private[client] class ClientImpl(connection: Connection, charSet: String = "UTF- logFutureOption(connection.delete(URI_EVENT_TYPE_BY_NAME.format(name)).flatMap(in => mapToOption(in))) } - def publishEvents[T <: Event](eventTypeName: String, events: Seq[T])(implicit ser: Serializer[Seq[T]]): Future[Option[ClientError]] = { + def publishEvents[T <: Event](eventTypeName: String, events: Seq[T], ser: Serializer[Seq[T]]): Future[Option[ClientError]] = { logFutureOption(connection.post(URI_EVENTS_OF_EVENT_TYPE.format(eventTypeName), events).flatMap(in => mapToOption(in))) } + def publishEvents[T <: Event](eventTypeName: String, events: Seq[T]): Future[Option[ClientError]] = { + logFutureOption(connection.post(URI_EVENTS_OF_EVENT_TYPE.format(eventTypeName), events).flatMap(in => mapToOption(in))) + } - def publishEvent[T <: Event](name: String, event: T)(implicit ser: Serializer[T]): Future[Option[ClientError]] = { + def publishEvent[T <: Event](name: String, event: T, ser: Serializer[T]): Future[Option[ClientError]] = { logFutureOption(connection.post(URI_EVENTS_OF_EVENT_TYPE.format(name), event).flatMap(in => mapToOption(in))) } + + def publishEvent[T <: Event](name: String, event: T): Future[Option[ClientError]] = { + logFutureOption(connection.post(URI_EVENTS_OF_EVENT_TYPE.format(name), event).flatMap(in => mapToOption(in))) + } - def getPartitions(name: String)(implicit des: Deserializer[Seq[Partition]]): Future[Either[ClientError, Option[Seq[Partition]]]] = { - logFutureEither(connection.get(URI_PARTITIONS_BY_EVENT_TYPE.format(name)).flatMap(in => mapToEither(in))) + def getPartitions(name: String): Future[Either[ClientError, Option[Seq[Partition]]]] = { + logFutureEither(connection.get(URI_PARTITIONS_BY_EVENT_TYPE.format(name)).flatMap(in => mapToEither(in)(deserializer(listOfPartitionTR)))) } - def getValidationStrategies()(implicit des: Deserializer[Seq[EventValidationStrategy.Value]]): Future[Either[ClientError, Option[Seq[EventValidationStrategy.Value]]]] = { - logFutureEither(connection.get(URI_VALIDATION_STRATEGIES).flatMap(mapToEither(_))) + def getValidationStrategies(): Future[Either[ClientError, Option[Seq[EventValidationStrategy.Value]]]] = { + logFutureEither(connection.get(URI_VALIDATION_STRATEGIES).flatMap(mapToEither(_)(deserializer(listOfEventValidationStrategyTR)))) } - def getEnrichmentStrategies()(implicit des: Deserializer[Seq[EventEnrichmentStrategy.Value]]): Future[Either[ClientError, Option[Seq[EventEnrichmentStrategy.Value]]]] = { - logFutureEither(connection.get(URI_ENRICHMENT_STRATEGIES).flatMap(mapToEither(_))) + def getEnrichmentStrategies(): Future[Either[ClientError, Option[Seq[EventEnrichmentStrategy.Value]]]] = { + logFutureEither(connection.get(URI_ENRICHMENT_STRATEGIES).flatMap(mapToEither(_)(deserializer(listOfEventEnrichmentStrategyTR)))) } - def getPartitioningStrategies()(implicit des: Deserializer[Seq[PartitionStrategy.Value]]): Future[Either[ClientError, Option[Seq[PartitionStrategy.Value]]]] = - logFutureEither(connection.get(URI_PARTITIONING_STRATEGIES).flatMap(mapToEither(_))) + def getPartitioningStrategies(): Future[Either[ClientError, Option[Seq[PartitionStrategy.Value]]]] = + logFutureEither(connection.get(URI_PARTITIONING_STRATEGIES).flatMap(mapToEither(_)(deserializer(listOfPartitionStrategyTR)))) - def stop(): Future[Option[ClientError]] = { + def stop(): Option[ClientError] = { val result = Await.ready(connection.stop(), Duration.Inf) - Future.successful(None) + None } - def subscribe[T <: Event](eventType: String, params: StreamParameters, listener: Listener[T])(implicit des: Deserializer[T]): Future[Option[ClientError]] = { + def subscribe[T <: Event](eventTypeName: String, parameters: StreamParameters, listener: Listener[T], typeRef: TypeReference[T]): Future[Option[ClientError]] = { + subscribe(eventTypeName, parameters, listener)(deserializer(typeRef)) + + } + def subscribe[T <: Event](eventType: String, params: StreamParameters, listener: Listener[T])(implicit des: Deserializer[T]): Future[Option[ClientError]] = { (eventType, params, listener) match { case (_, _, listener) if listener == null => @@ -114,7 +126,7 @@ private[client] class ClientImpl(connection: Connection, charSet: String = "UTF- private[client] def logFutureEither[A, T](future: Future[Either[ClientError, T]]): Future[Either[ClientError, T]] = { future recover { case e: Throwable => - logger.error("A unexpected error occured", e) + logger.error("A unexpected error occured:", e.getMessage) Left(ClientError("Error: " + e.getMessage, None)) } } diff --git a/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala index e936458..05d96de 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala @@ -39,11 +39,12 @@ trait Connection extends HttpFactory { def get(endpoint: String): Future[HttpResponse] def get(endpoint: String, headers: Seq[HttpHeader]): Future[HttpResponse] - def get4Java[T](endpoint: String, des: Deserializer[T]): java.util.concurrent.Future[Optional[T]] - def get4Java[T](endpoint: String, headers: Seq[HttpHeader], des: Deserializer[T]): java.util.concurrent.Future[Optional[T]] def stream(endpoint: String, headers: Seq[HttpHeader]): Future[HttpResponse] def delete(endpoint: String): Future[HttpResponse] def post[T](endpoint: String, model: T)(implicit serializer: Serializer[T]): Future[HttpResponse] + def get4Java[T](endpoint: String, des: Deserializer[T]): java.util.concurrent.Future[Optional[T]] + def get4Java[T](endpoint: String, headers: Seq[HttpHeader], des: Deserializer[T]): java.util.concurrent.Future[Optional[T]] + def post4Java[T](endpoint: String, model: T)(implicit serializer: Serializer[T]): java.util.concurrent.Future[Void] def put[T](endpoint: String, model: T)(implicit serializer: Serializer[T]): Future[HttpResponse] def subscribe[T <: Event](url: String, request: HttpRequest, listener: Listener[T])(implicit des: Deserializer[T]) def subscribeJava[T <: org.zalando.nakadi.client.java.model.Event](url: String, request: HttpRequest, listener: org.zalando.nakadi.client.java.Listener[T])(implicit des: Deserializer[T]) @@ -110,26 +111,18 @@ sealed class ConnectionImpl(val host: String, val port: Int, val tokenProvider: logger.info("Get - URL {} - Headers {}", endpoint, headers) executeCall(withHttpRequest(endpoint, HttpMethods.GET, headers, tokenProvider, None)) } - def get4Java[T](endpoint: String, des: Deserializer[T]): java.util.concurrent.Future[Optional[T]] = { - FutureConversions.fromFuture2Future(get(endpoint).flatMap(deserialize4Java(_, des))) - } - def get4Java[T](endpoint: String, headers: Seq[HttpHeader], des: Deserializer[T]): java.util.concurrent.Future[Optional[T]] = { - FutureConversions.fromFuture2Future(executeCall(withHttpRequest(endpoint, HttpMethods.GET, headers, tokenProvider, None)).flatMap(deserialize4Java(_, des))) - } - - private def deserialize4Java[T](response:HttpResponse, des: Deserializer[T]):Future[Optional[T]] = response match { - case HttpResponse(status, headers, entity, protocol) if (status.isSuccess()) => - Try(Unmarshal(entity).to[String].map(body => des.from(body))) match { - case Success(result) => result.map(Optional.of(_)) - case Failure(error) => throw new RuntimeException(error.getMessage) - } - // val errorMsg = "Failed to Deserialize with error:" + error.getMessage - // listener.onError(url, null, ClientError("Failed to Deserialize with an error!", None)) + private def deserialize4Java[T](response: HttpResponse, des: Deserializer[T]): Future[Optional[T]] = response match { + case HttpResponse(status, headers, entity, protocol) if (status.isSuccess()) => - case HttpResponse(status, headers, entity, protocol) if (status.isFailure()) => - throw new RuntimeException(status.reason()) - } + Try(Unmarshal(entity).to[String].map(body => des.from(body))) match { + case Success(result) => result.map(Optional.of(_)) + case Failure(error) => throw new RuntimeException(error.getMessage) + } + + case HttpResponse(status, headers, entity, protocol) if (status.isFailure()) => + throw new RuntimeException(status.reason()) + } def stream(endpoint: String, headers: Seq[HttpHeader]): Future[HttpResponse] = { logger.info("Streaming on Get: {}", endpoint) executeCall(withHttpRequest(endpoint, HttpMethods.GET, headers, tokenProvider, None)) //TODO: Change to stream single event @@ -146,7 +139,42 @@ sealed class ConnectionImpl(val host: String, val port: Int, val tokenProvider: logger.debug("Data to post {}", entity) executeCall(withHttpRequestAndPayload(endpoint, entity, HttpMethods.POST, tokenProvider)) } + def get4Java[T](endpoint: String, des: Deserializer[T]): java.util.concurrent.Future[Optional[T]] = { + FutureConversions.fromFuture2Future(get(endpoint).flatMap(deserialize4Java(_, des))) + } + def get4Java[T](endpoint: String, headers: Seq[HttpHeader], des: Deserializer[T]): java.util.concurrent.Future[Optional[T]] = { + FutureConversions.fromFuture2Future(executeCall(withHttpRequest(endpoint, HttpMethods.GET, headers, tokenProvider, None)).flatMap(deserialize4Java(_, des))) + } + def post4Java[T](endpoint: String, model: T)(implicit serializer: Serializer[T]): java.util.concurrent.Future[Void] = { + val entity = serializer.to(model) + logger.info("Posting to endpoint {}", endpoint) + logger.debug("Data to post {}", entity) + val result = executeCall( + withHttpRequestAndPayload(endpoint, serialize4Java(model), HttpMethods.POST, tokenProvider)) + .flatMap(response4Java(_)) + FutureConversions.fromOption2Void(result) + } + + private def serialize4Java[T](model: T)(implicit serializer: Serializer[T]): String = + Try(serializer.to(model)) match { + case Success(result) => result + case Failure(error) => throw new RuntimeException("Failed to serialize: "+error.getMessage) + } + private def response4Java[T](response: HttpResponse): Future[Option[String]] = response match { + case HttpResponse(status, headers, entity, protocol) if (status.isSuccess()) => + Try(Unmarshal(entity).to[String]) match { + case Success(result) => result.map(Option(_)) + case Failure(error) => throw new RuntimeException(error.getMessage) + } + + case HttpResponse(status, headers, entity, protocol) if (status.isFailure()) => + Unmarshal(entity).to[String].map { x => + val msg = "http-stats(%s) - %s - problem: %s ".format(status.intValue(),x,status.defaultMessage()) + logger.warn(msg) + throw new RuntimeException(msg) + } + } def delete(endpoint: String): Future[HttpResponse] = { logger.info("Delete: {}", endpoint) executeCall(withHttpRequest(endpoint, HttpMethods.DELETE, Nil, tokenProvider, None)) diff --git a/client/src/main/scala/org/zalando/nakadi/client/scala/model/ScalaJacksonJsonMarshaller.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/model/ScalaJacksonJsonMarshaller.scala index 64d3616..4940227 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/scala/model/ScalaJacksonJsonMarshaller.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/scala/model/ScalaJacksonJsonMarshaller.scala @@ -23,7 +23,7 @@ object JacksonJsonMarshaller { // All TypeReferences implicit def problemTR: TypeReference[Problem] = new TypeReference[Problem] {} - implicit def metricsTR: TypeReference[Metrics] = new TypeReference[Metrics] {} + implicit val metricsTR: TypeReference[Metrics] = new TypeReference[Metrics] {} implicit def partitionTR: TypeReference[Partition] = new TypeReference[Partition] {} implicit def cursorTR: TypeReference[Cursor] = new TypeReference[Cursor] {} implicit def eventTypeSchemaTR: TypeReference[EventTypeSchema] = new TypeReference[EventTypeSchema] {} diff --git a/client/src/main/scala/org/zalando/nakadi/client/utils/FutureConversions.scala b/client/src/main/scala/org/zalando/nakadi/client/utils/FutureConversions.scala index f10fad0..e4afefb 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/utils/FutureConversions.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/utils/FutureConversions.scala @@ -21,8 +21,11 @@ object FutureConversions { def fromOption2Optional[T](in: scala.concurrent.Future[Option[T]]): java.util.concurrent.Future[Optional[T]] = { new MFuture[Option[T], Optional[T]](in, a => fromOptional2Optional(a)) } + def fromOption2Void[T](in: scala.concurrent.Future[Option[T]]): java.util.concurrent.Future[Void] = { + new MFuture[Option[T], Void](in, a => null) + } def fromFuture2Future[T](in: scala.concurrent.Future[T]): java.util.concurrent.Future[T] = { - new MFuture[T, T](in, a => a) + new MFuture[T, T](in, a => a) } // /** diff --git a/client/src/main/scala/org/zalando/nakadi/client/utils/ImplicitHelper.scala b/client/src/main/scala/org/zalando/nakadi/client/utils/ImplicitHelper.scala index 778e388..31269ff 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/utils/ImplicitHelper.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/utils/ImplicitHelper.scala @@ -27,4 +27,5 @@ object Serialization { def seqOfEventValidationStrategy(): Deserializer[java.util.List[EventValidationStrategy]] = JavaJacksonJsonMarshaller.deserializer(JavaJacksonJsonMarshaller.listOfEventValidationStrategyTR) def seqOfEventEnrichmentStrategy(): Deserializer[java.util.List[EventEnrichmentStrategy]] = JavaJacksonJsonMarshaller.deserializer(JavaJacksonJsonMarshaller.listOfEventEnrichmentStrategyTR) def seqOfPartitionStrategy(): Deserializer[java.util.List[PartitionStrategy]] = JavaJacksonJsonMarshaller.deserializer(JavaJacksonJsonMarshaller.listOfPartitionStrategyTR) + } \ No newline at end of file diff --git a/client/src/main/scala/org/zalando/nakadi/client/utils/Uri.scala b/client/src/main/scala/org/zalando/nakadi/client/utils/Uri.scala index 15f284d..20e47b1 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/utils/Uri.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/utils/Uri.scala @@ -2,18 +2,20 @@ package org.zalando.nakadi.client.utils object Uri { - final val URI_METRICS = "/metrics" + def URI_METRICS = "/metrics" /*Events*/ - final val URI_EVENT_TYPES = "/event-types" - final val URI_EVENT_TYPE_BY_NAME = "/event-types/%s" - final val URI_EVENTS_OF_EVENT_TYPE = "/event-types/%s/events" + def URI_EVENT_TYPES = "/event-types" + def URI_EVENT_TYPE_BY_NAME = "/event-types/%s" + def URI_EVENTS_OF_EVENT_TYPE() = "/event-types/%s/events" + def getEventStreamingUri(in:String) = s"/event-types/$in/events" + /*Partitions*/ - final val URI_PARTITIONS_BY_EVENT_TYPE = "/event-types/%s/partitions" + final def URI_PARTITIONS_BY_EVENT_TYPE = "/event-types/%s/partitions" /*Strategies*/ - final val URI_VALIDATION_STRATEGIES = "/registry/validation-strategies" - final val URI_ENRICHMENT_STRATEGIES = "/registry/enrichment-strategies" - final val URI_PARTITIONING_STRATEGIES = "/registry/partition-strategies" + final def URI_VALIDATION_STRATEGIES = "/registry/validation-strategies" + final def URI_ENRICHMENT_STRATEGIES = "/registry/enrichment-strategies" + final def URI_PARTITIONING_STRATEGIES = "/registry/partition-strategies" } \ No newline at end of file diff --git a/client/src/test/scala/org/zalando/nakadi/client/scala/TestFactory.scala b/client/src/test/scala/org/zalando/nakadi/client/scala/TestFactory.scala index 073cbea..aabaaf6 100644 --- a/client/src/test/scala/org/zalando/nakadi/client/scala/TestFactory.scala +++ b/client/src/test/scala/org/zalando/nakadi/client/scala/TestFactory.scala @@ -13,27 +13,8 @@ import org.zalando.nakadi.client.Deserializer import org.zalando.nakadi.client.Deserializer import org.zalando.nakadi.client.Serializer -object ClientFactory { - def host():String = "nakadi-sandbox.my-test.fernan.do" - def OAuth2Token(): () => String = () => "" - def getToken():String = OAuth2Token().apply() - def port():Integer = 443 - def connection():Connection = Connection.newConnection(host, port, OAuth2Token(), true, false) - def client():Client = new ClientImpl(connection, "UTF-8") - -} -//trait ClientFactory { -// def host():String -// def OAuth2Token : () => String -// def getToken:String -// def port(): Int -// def connection(): Connection -// def client(): Client -// -//} case class EventActions(client: Client) { - import JacksonJsonMarshaller._ def create[T <: Event](name: String, event: Seq[T])(implicit ser: Serializer[Seq[T]]) = { client.publishEvents[T](name, event) } diff --git a/it/src/main/java/org/zalando/nakadi/client/examples/java/EventCreationExample.java b/it/src/main/java/org/zalando/nakadi/client/examples/java/EventCreationExample.java index 3275280..a5fe100 100644 --- a/it/src/main/java/org/zalando/nakadi/client/examples/java/EventCreationExample.java +++ b/it/src/main/java/org/zalando/nakadi/client/examples/java/EventCreationExample.java @@ -1,6 +1,9 @@ package org.zalando.nakadi.client.examples.java; +import java.util.Arrays; import java.util.List; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; import org.zalando.nakadi.client.java.Client; import org.zalando.nakadi.client.java.enumerator.EventEnrichmentStrategy; @@ -14,11 +17,14 @@ import org.zalando.nakadi.client.java.model.EventTypeStatistics; import org.zalando.nakadi.client.scala.ClientFactory; import org.zalando.nakadi.client.utils.ClientBuilder; +import org.zalando.nakadi.client.utils.Serialization; import com.google.common.collect.Lists; public class EventCreationExample { - + /** + * Define how your event should look like + */ public class MeetingsEvent implements Event { private final String date; private final String topic; @@ -38,15 +44,6 @@ public String getTopic() { } - public Client createClient() { - return new ClientBuilder()// - .withHost("nakadi-sandbox.aruha-test.zalan.do")// - .withSecuredConnection(true) // s - .withVerifiedSslCertificate(false) // s - .withTokenProvider4Java(() -> ClientFactory.getToken())// - .buildJavaClient(); - } - public EventTypeSchema createEventTypeSchema(String schema) { return new EventTypeSchema(SchemaType.JSON, schema); } @@ -56,7 +53,7 @@ public EventType createEventType(String name, String owningApplication = "team-laas"; EventTypeCategory category = EventTypeCategory.UNDEFINED; List validationStrategies = Lists - .newArrayList(EventValidationStrategy.NONE); + .newArrayList(); List enrichmentStrategies = Lists .newArrayList(); PartitionStrategy partitionStrategy = PartitionStrategy.RANDOM; @@ -77,19 +74,67 @@ public EventType createEventType(String name, } - public static void main(String[] args) { - EventCreationExample example = new EventCreationExample(); - // 1. Create client - final Client client = example.createClient(); - - // 2. Create a simple Meeting instance - EventCreationExample.MeetingsEvent meeting = example.new MeetingsEvent( - "2016-04-28T13:28:15+00:00", "Hackthon"); + public static void main(String[] args) throws InterruptedException, + ExecutionException { + + /** + * An Event-type is ment for describing: 1. The Type of Events you want + * to create (i.e.: BusinessEvent) . 2. Schema validations to be + * enforced(or not)by Nakadi 3. How events should be distributed between + * their own partitions 4. A unique identifier for Subscribing + * + */ + String eventTypeName = "MeetingsEvent-example-E"; + + /** + * Create client + */ + final Client client = new ClientBuilder()// + .withHost(ClientFactory.host())// + .withSecuredConnection(true) // s + .withVerifiedSslCertificate(false) // s + .withTokenProvider4Java(() -> ClientFactory.getToken())// + .buildJavaClient(); - // Create the EventType - String schema = " { \"properties\":\" { \"date\": { \"type\": \"string\" }, \"topic\": { \"type\": \"string\"} } }"; + /** + * nakadi needs to know what kind of Json-schema you are going to send to + * the Event. We need to define a schema that matches the Event that we + * want to send along(MeetingsEvent). + */ + String schema = " { 'properties': { 'date': { 'type': 'string' }, 'topic': { 'type': 'string'} } }" + .replaceAll("'", "\""); - EventTypeSchema eventSchame = example.createEventTypeSchema(schema); + EventCreationExample example = new EventCreationExample(); + EventTypeSchema eventTypeSchema = example.createEventTypeSchema(schema); + EventType eventType = example.createEventType(eventTypeName, + eventTypeSchema); + Future result = null; + result = client.createEventType(eventType); + result.get(); + + // Create the event + MeetingsEvent event = example.new MeetingsEvent( + "2016-04-28T13:28:15+00:00", "Hackaton"); + MeetingsEvent event1 = example.new MeetingsEvent( + "2016-04-28T13:28:15+00:00", "Hackaton1"); + MeetingsEvent event2 = example.new MeetingsEvent( + "2016-04-28T13:28:15+00:00", "Hackaton2"); + MeetingsEvent event3 = example.new MeetingsEvent( + "2016-04-28T13:28:15+00:00", "Hackaton3"); + // Single Event + result = client.publishEvent(eventTypeName, event); + result.get(); + // Single Event with Serializer, + result = client.publishEvent(eventTypeName, event1, + Serialization.defaultSerializer()); + result.get(); + // Multi Event + result = client.publishEvents(eventTypeName, Arrays.asList(event2)); + result.get(); + // Multi Event with Serializer + result = client.publishEvents(eventTypeName, Arrays.asList(event3), + Serialization.defaultSerializer()); + result.get(); } } diff --git a/it/src/main/java/org/zalando/nakadi/client/java/ClientTest.java b/it/src/main/java/org/zalando/nakadi/client/java/ClientTest.java new file mode 100644 index 0000000..8aa7603 --- /dev/null +++ b/it/src/main/java/org/zalando/nakadi/client/java/ClientTest.java @@ -0,0 +1,5 @@ +package org.zalando.nakadi.client.java; + +public class ClientTest { + +} diff --git a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala index 5d5f304..67712ac 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala @@ -1,10 +1,15 @@ package org.zalando.nakadi.client.examples.scala -import org.joda.time.DateTime -import org.joda.time.format.DateTimeFormatter -import org.zalando.nakadi.client.scala.model._ +import org.zalando.nakadi.client.scala.Client import org.zalando.nakadi.client.scala.ClientFactory -import com.fasterxml.jackson.databind.util.ISO8601DateFormat +import org.zalando.nakadi.client.scala.model.Event +import org.zalando.nakadi.client.scala.model.EventType +import org.zalando.nakadi.client.scala.model.EventTypeCategory +import org.zalando.nakadi.client.scala.model.EventTypeSchema +import org.zalando.nakadi.client.scala.model.JacksonJsonMarshaller +import org.zalando.nakadi.client.scala.model.PartitionStrategy +import org.zalando.nakadi.client.scala.model.SchemaType +import org.zalando.nakadi.client.utils.ClientBuilder object EventCreationExample extends App { @@ -15,7 +20,7 @@ object EventCreationExample extends App { import org.zalando.nakadi.client.utils.ClientBuilder import org.zalando.nakadi.client.scala.Client val client: Client = ClientBuilder() - .withHost("nakadi-sandbox.aruha-test.zalan.do") + .withHost(ClientFactory.host()) .withSecuredConnection(true) //s .withVerifiedSslCertificate(false) //s .withTokenProvider(ClientFactory.OAuth2Token()) // @@ -28,12 +33,12 @@ object EventCreationExample extends App { //We define the Json representation of our Meeting Event. val schema: String = """ { - "properties": + 'properties': { - "date": { "type": "string" }, - "topic": { "type": "string"} + 'date': { 'type': 'string' }, + 'topic': { 'type': 'string'} } - }""" + }""".replaceAll("'", "\"") // 3. Create the EventType, // We need to create an eventType(a topic), where listeners @@ -43,7 +48,7 @@ object EventCreationExample extends App { //First the eventType name, wich will be part of the URL: https://nakadi.test.io/event-types/{eventTypeName} //See the API for more information on the EventType model //https://github.com/zalando/nakadi/blob/nakadi-jvm/api/nakadi-event-bus-api.yaml#L1240 - val eventTypeName = "org.zalando.laas.meetings-1" + val eventTypeName = "org.zalando.laas.meetings-2" val owner = "team-laas" val category = EventTypeCategory.UNDEFINED // We want just to pass data without through Nakadi, simple schema-validation is enough! @@ -67,10 +72,11 @@ object EventCreationExample extends App { import JacksonJsonMarshaller._ client.createEventType(eventType) - + Thread.sleep(10000) // 4. Publish the EventType - + val event = new MeetingsEvent("2016-04-28T13:28:15+00:00", "Hackaton") - client.publishEvent(eventTypeName, event) + client.publishEvents(eventTypeName, List(event)) + client.stop() } \ No newline at end of file diff --git a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala new file mode 100644 index 0000000..be10ab3 --- /dev/null +++ b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala @@ -0,0 +1,68 @@ +package org.zalando.nakadi.client.examples.scala + +import org.zalando.nakadi.client.scala.ClientFactory +import org.zalando.nakadi.client.utils.ClientBuilder +import org.zalando.nakadi.client.scala.Client +import org.zalando.nakadi.client.scala._ +import org.zalando.nakadi.client.scala.model._ +import EventCreationExample._ +import com.fasterxml.jackson.core.`type`.TypeReference + +/** + * Your listener will have to implement the necessary + */ +class MeetingsListener() extends Listener[MeetingsEvent] { + + def id: String = "test" + def onError(sourceUrl: String, cursor: Cursor, error: ClientError): Unit = { + println("YOOOOOOOOOOOOOO ") + } + def onSubscribed(): Unit = ??? + def onUnsubscribed(): Unit = ??? + def onReceive(sourceUrl: String, cursor: Cursor, event: Seq[MeetingsEvent]): Unit = ??? +} + +object EventListenerExample extends App { + + /** + * Create our client + */ + val client: Client = ClientBuilder() + .withHost(ClientFactory.host()) + .withSecuredConnection(true) //s + .withVerifiedSslCertificate(false) //s + .withTokenProvider(ClientFactory.OAuth2Token()) // + // .build(); + .build() + + + /** + * Initialize our Listener + */ + val listener = new MeetingsListener() + + /** + * Create the Parameters with the cursor. + */ + + val cursor = Cursor(0, 0) + + val parameters = new StreamParameters( + cursor = Some(cursor) + ,batchLimit = Some(30) +// ,batchFlushTimeout = Some(1) +// ,streamKeepAliveLimit=Some(4) +// ,streamTimeout=Some(1) + ) + + /** + * Create TypeReference for the JacksonObjectMapper + */ + + implicit def typeRef: TypeReference[MeetingsEvent] = new TypeReference[MeetingsEvent] {} + import org.zalando.nakadi.client.scala.model.JacksonJsonMarshaller._ + + val eventTypeName = "MeetingsEvent-example-E" + val result = client.subscribe(eventTypeName, parameters, listener) + +} \ No newline at end of file diff --git a/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactoryy.scala b/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala similarity index 59% rename from it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactoryy.scala rename to it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala index 3fb35f8..1d6c8a0 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactoryy.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala @@ -1,8 +1,9 @@ package org.zalando.nakadi.client.scala - object ClientFactory { - def host():String = "nakadi-sandbox.my-test.fernan.do" - def OAuth2Token(): () => String = () => "" + import sys.process._ + import scala.language.postfixOps + def host():String = "nakadi-sandbox.aruha-test.zalan.do" + def OAuth2Token(): () => String = () => "11d0bf06-1ef1-4cc0-9d74-7787a6ab240a" def getToken():String = OAuth2Token().apply() def port():Integer = 443 def connection():Connection = Connection.newConnection(host, port, OAuth2Token(), true, false) diff --git a/it/src/main/scala/org/zalando/nakadi/client/subscription/MyListener.scala b/it/src/main/scala/org/zalando/nakadi/client/subscription/MyListener.scala index 64adfd9..3838900 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/subscription/MyListener.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/subscription/MyListener.scala @@ -2,7 +2,7 @@ package org.zalando.nakadi.client.subscription import org.zalando.nakadi.client.scala.model.Cursor import org.zalando.nakadi.client.scala.Listener -import org.zalando.nakadi.client.ClientError +import org.zalando.nakadi.client.scala.ClientError class MyListener extends Listener[MyEventExample] { def id: String = "test" diff --git a/it/src/main/scala/org/zalando/nakadi/client/subscription/Subscriber.scala b/it/src/main/scala/org/zalando/nakadi/client/subscription/Subscriber.scala index 58eccde..70891c5 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/subscription/Subscriber.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/subscription/Subscriber.scala @@ -2,23 +2,24 @@ package org.zalando.nakadi.client.subscription import scala.concurrent.Await import scala.concurrent.duration.DurationInt -import org.zalando.nakadi.client.ClientError + +import org.zalando.nakadi.client.scala.ClientFactory import org.zalando.nakadi.client.scala.StreamParameters -import org.zalando.nakadi.client.scala.model.{ Cursor, Event } -import com.fasterxml.jackson.core.`type`.TypeReference +import org.zalando.nakadi.client.scala.model.Cursor import org.zalando.nakadi.client.scala.model.JacksonJsonMarshaller -import org.zalando.nakadi.client.scala.ClientFactory + +import com.fasterxml.jackson.core.`type`.TypeReference object Subscriber extends App { - val a = new A() + val a = new Subscriber() a.startListening() // a.printPartitions() // a.printEventTypes() // a.sendEvents(30) } -class A { +class Subscriber { import ClientFactory._ import JacksonJsonMarshaller._ val eventType = "test-client-integration-event-1936085527-148383828851369665" diff --git a/it/src/test/scala/org/zalando/nakadi/client/ClientSubscriptionTest.scala b/it/src/test/scala/org/zalando/nakadi/client/ClientSubscriptionTest.scala index 09f6028..1ce6b58 100644 --- a/it/src/test/scala/org/zalando/nakadi/client/ClientSubscriptionTest.scala +++ b/it/src/test/scala/org/zalando/nakadi/client/ClientSubscriptionTest.scala @@ -9,6 +9,7 @@ import org.zalando.nakadi.client.scala.ClientFactory import org.zalando.nakadi.client.scala.EventActions import org.zalando.nakadi.client.scala.StreamParameters import org.zalando.nakadi.client.scala.Listener +import org.zalando.nakadi.client.scala.ClientError class ClientSubscriptionTest extends WordSpec with Matchers with ModelFactory { import ClientFactory._ diff --git a/it/src/test/scala/org/zalando/nakadi/client/EventTypesIntegrationTest.scala b/it/src/test/scala/org/zalando/nakadi/client/EventTypesIntegrationTest.scala index 605f38b..59e72cf 100644 --- a/it/src/test/scala/org/zalando/nakadi/client/EventTypesIntegrationTest.scala +++ b/it/src/test/scala/org/zalando/nakadi/client/EventTypesIntegrationTest.scala @@ -26,7 +26,7 @@ import JacksonJsonMarshaller._ case class MyEventExample(orderNumber: String)extends Event implicit def problemTR: TypeReference[MyEventExample] = new TypeReference[MyEventExample] {} val events = for { - a <- 0 to 4005 + a <- 0 to 12 } yield MyEventExample("order-"+a) // eventAction.create("test-client-integration-event-1936085527-148383828851369665", List(MyEventExample("test-1"))) eventAction.create("test-client-integration-event-1936085527-148383828851369665", events) diff --git a/it/src/test/scala/org/zalando/nakadi/client/KlientIntegrationTest.scala b/it/src/test/scala/org/zalando/nakadi/client/KlientIntegrationTest.scala index afaeae6..05d37ba 100644 --- a/it/src/test/scala/org/zalando/nakadi/client/KlientIntegrationTest.scala +++ b/it/src/test/scala/org/zalando/nakadi/client/KlientIntegrationTest.scala @@ -12,6 +12,8 @@ import org.zalando.nakadi.client.scala.ClientFactory import org.zalando.nakadi.client.scala.ModelFactory import com.fasterxml.jackson.core.`type`.TypeReference +import org.zalando.nakadi.client.scala.ClientError + class KlientIntegrationTest extends WordSpec with Matchers with ModelFactory { import ClientFactory._ import JacksonJsonMarshaller._ From 88d97860a41f413de9d4fe62d8982338661b2c0e Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Fri, 29 Apr 2016 17:19:07 +0200 Subject: [PATCH 031/183] techjira:LAAS-60 Adding Java Model! --- .../scala/org/zalando/nakadi/client/scala/ClientFactory.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala b/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala index 1d6c8a0..883c271 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala @@ -2,8 +2,8 @@ package org.zalando.nakadi.client.scala object ClientFactory { import sys.process._ import scala.language.postfixOps - def host():String = "nakadi-sandbox.aruha-test.zalan.do" - def OAuth2Token(): () => String = () => "11d0bf06-1ef1-4cc0-9d74-7787a6ab240a" + def host():String = "nakadi-sandbox.my-test.fernando.do" + def OAuth2Token(): () => String = () => "" def getToken():String = OAuth2Token().apply() def port():Integer = 443 def connection():Connection = Connection.newConnection(host, port, OAuth2Token(), true, false) From 6a1fdd3b3d1e69f5e4d717b2840223ccbfbefb89 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Sun, 1 May 2016 23:52:53 +0200 Subject: [PATCH 032/183] techjira:LAAS-60 WIP!! --- .../client/actor/EventReceivingActor.scala | 26 ++++++++----------- .../nakadi/client/scala/ClientImpl.scala | 2 +- .../nakadi/client/scala/Connection.scala | 5 ++-- 3 files changed, 15 insertions(+), 18 deletions(-) diff --git a/client/src/main/scala/org/zalando/nakadi/client/actor/EventReceivingActor.scala b/client/src/main/scala/org/zalando/nakadi/client/actor/EventReceivingActor.scala index d1730c6..e8faa77 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/actor/EventReceivingActor.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/actor/EventReceivingActor.scala @@ -16,18 +16,14 @@ object EventReceivingActor { -//class EventReceivingActor extends Actor with ActorLogging with ActorPublisher[NextEvent] { -// val queue: Queue[NextEvent] = Queue() -// val receivedOffset: Set[Integer] = Set() -// -// override def receive: Receive = { -// case msg: NextEvent => -// if (!visited(msg.lastReceivedCursor.offset.get)) { -// visited += url.url -// queue.enqueue(url) -// if (isActive && totalDemand > 0) { -// onNext(queue.dequeue()) -// } -// } -// } -//} \ No newline at end of file +class EventReceivingActor (url: String, cursor:Option[Cursor]) extends Actor with ActorLogging with ActorPublisher[Cursor] { + + override def preStart() { + self ! cursor + } + + override def receive: Receive = { + case cursor: Cursor => onNext(cursor) + + } +} \ No newline at end of file diff --git a/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala index 4e8ddf3..f702530 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala @@ -112,7 +112,7 @@ private[client] class ClientImpl(connection: Connection, charSet: String = "UTF- connection.tokenProvider(), Option(params)) logger.debug("Subscribing listener {} - cursor {} - parameters {} - eventType {} ", listener.id, cursor, params, eventType) - connection.subscribe(url, request, listener) + connection.subscribe(url, cursor, request, listener) Future.successful(None) } } diff --git a/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala index 05d96de..c5a9735 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala @@ -28,6 +28,7 @@ import scala.util.Success import akka.http.scaladsl.model.{ HttpHeader, HttpMethod, HttpMethods, HttpResponse, MediaRange } import java.util.Optional import org.zalando.nakadi.client.utils.FutureConversions +import org.zalando.nakadi.client.scala.model.Cursor trait Connection extends HttpFactory { @@ -46,7 +47,7 @@ trait Connection extends HttpFactory { def get4Java[T](endpoint: String, headers: Seq[HttpHeader], des: Deserializer[T]): java.util.concurrent.Future[Optional[T]] def post4Java[T](endpoint: String, model: T)(implicit serializer: Serializer[T]): java.util.concurrent.Future[Void] def put[T](endpoint: String, model: T)(implicit serializer: Serializer[T]): Future[HttpResponse] - def subscribe[T <: Event](url: String, request: HttpRequest, listener: Listener[T])(implicit des: Deserializer[T]) + def subscribe[T <: Event](url: String, cursor:Option[Cursor], request: HttpRequest, listener: Listener[T])(implicit des: Deserializer[T]) def subscribeJava[T <: org.zalando.nakadi.client.java.model.Event](url: String, request: HttpRequest, listener: org.zalando.nakadi.client.java.Listener[T])(implicit des: Deserializer[T]) def stop(): Future[Terminated] @@ -197,7 +198,7 @@ sealed class ConnectionImpl(val host: String, val port: Int, val tokenProvider: def stop(): Future[Terminated] = actorSystem.terminate() - def subscribe[T <: Event](url: String, request: HttpRequest, listener: Listener[T])(implicit des: Deserializer[T]) = { + def subscribe[T <: Event](url: String, cursor:Option[Cursor], request: HttpRequest, listener: Listener[T])(implicit des: Deserializer[T]) = { logger.info("Subscribing listener {} with request {}", listener.id, request.uri) import EventConsumer._ case class MyEventExample(orderNumber: String) From a48f7173f4e24aa80a9fd739f1c10f22aea68cc7 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Mon, 2 May 2016 23:06:58 +0200 Subject: [PATCH 033/183] techjira:LAAS-60 Adding Java Model! --- .../zalando/nakadi/client/scala/Client.scala | 4 +- .../client/actor/EventConsumingActor.scala | 75 ++++++++++------ .../client/actor/EventReceivingActor.scala | 52 +++++++++--- .../nakadi/client/scala/ClientImpl.scala | 33 +++---- .../nakadi/client/scala/Connection.scala | 85 ++++++++++++------- .../nakadi/client/scala/HttpFactory.scala | 51 ++++++++--- .../examples/scala/EventCreationExample.scala | 16 +++- .../examples/scala/EventListenerExample.scala | 50 ++++++----- .../nakadi/client/scala/ClientFactory.scala | 4 +- .../client/subscription/Subscriber.scala | 2 +- .../client/ClientSubscriptionTest.scala | 2 +- .../nakadi/client/example2/Ecample3.scala | 2 +- .../client/example2/HttpClientExample.scala | 4 +- project/Dependencies.scala | 2 +- 14 files changed, 248 insertions(+), 134 deletions(-) diff --git a/api/src/main/scala/org/zalando/nakadi/client/scala/Client.scala b/api/src/main/scala/org/zalando/nakadi/client/scala/Client.scala index adf95f1..48ea341 100644 --- a/api/src/main/scala/org/zalando/nakadi/client/scala/Client.scala +++ b/api/src/main/scala/org/zalando/nakadi/client/scala/Client.scala @@ -160,7 +160,7 @@ trait Client { * @listener - Listener to pass the event to when it is received. * @des - Json Marshaller(implicit) to deserialize the event to Json. */ - def subscribe[T <: Event](eventTypeName: String, parameters: StreamParameters, listener: Listener[T])(implicit des: Deserializer[T]): Future[Option[ClientError]] + def subscribe[T <: Event](eventTypeName: String, parameters: StreamParameters, listener: Listener[T])(implicit des: Deserializer[EventStreamBatch[T]]): Future[Option[ClientError]] /** * Registers the subscription of a listener to start streaming events from a partition in non-blocking fashion. * @@ -169,7 +169,7 @@ trait Client { * @listener - Listener to pass the event to when it is received. * @typeRef - TypeReference/Helper for using with the Jackson-Objectmapper to deserializing the event to json. */ - def subscribe[T <: Event](eventTypeName: String, parameters: StreamParameters, listener: Listener[T], typeRef: TypeReference[T]): Future[Option[ClientError]] + def subscribe[T <: Event](eventTypeName: String, parameters: StreamParameters, listener: Listener[T], typeRef: TypeReference[EventStreamBatch[T]]): Future[Option[ClientError]] /** * Removes the subscription of a listener, to stop streaming events from a partition. * diff --git a/client/src/main/scala/org/zalando/nakadi/client/actor/EventConsumingActor.scala b/client/src/main/scala/org/zalando/nakadi/client/actor/EventConsumingActor.scala index ec3a9cc..0db9112 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/actor/EventConsumingActor.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/actor/EventConsumingActor.scala @@ -1,32 +1,48 @@ package org.zalando.nakadi.client.actor +import scala.util.Failure +import scala.util.Success +import scala.util.Try + +import org.zalando.nakadi.client.Deserializer +import org.zalando.nakadi.client.scala.ClientError +import org.zalando.nakadi.client.scala.Listener +import org.zalando.nakadi.client.scala.model.Cursor +import org.zalando.nakadi.client.scala.model.Event +import org.zalando.nakadi.client.scala.model.EventStreamBatch + +import EventReceivingActor.NextEvent import akka.actor.Actor import akka.actor.ActorLogging +import akka.actor.ActorRef +import akka.actor.actorRef2Scala import akka.stream.actor.ActorSubscriber +import akka.stream.actor.ActorSubscriberMessage.OnComplete +import akka.stream.actor.ActorSubscriberMessage.OnError import akka.stream.actor.ActorSubscriberMessage.OnNext -import akka.stream.actor.RequestStrategy +import akka.stream.actor._ import akka.util.ByteString -import org.zalando.nakadi.client.Deserializer -import org.zalando.nakadi.client.scala.model.JacksonJsonMarshaller -import com.fasterxml.jackson.core.`type`.TypeReference -import org.zalando.nakadi.client.scala.model._ -import org.zalando.nakadi.client.scala.Listener object EventConsumer { - case class Msg(msg: ByteString) - case class ShutdownMsg() } case class MyEventExample(orderNumber: String) extends Event -class EventConsumer[T <: Event](url: String, listener: Listener[T], des: Deserializer[T]) extends Actor with ActorLogging with ActorSubscriber { +class EventConsumer[T <: Event](url: String, // + listener: Listener[T], // + receivingActor: ActorRef, // + des: Deserializer[EventStreamBatch[T]]) + extends Actor with ActorLogging with ActorSubscriber with MessageSplitter { + import EventConsumer._ - var count = 0 + import EventReceivingActor._ +// val requestStrategy = WatermarkRequestStrategy(50) override protected def requestStrategy: RequestStrategy = new RequestStrategy { override def requestDemand(remainingRequested: Int): Int = { + log.info("########## Received: {}", remainingRequested) Math.max(remainingRequested, 10) } } @@ -34,26 +50,37 @@ class EventConsumer[T <: Event](url: String, listener: Listener[T], des: Deseria override def receive: Receive = { case OnNext(msg: ByteString) => val message = msg.utf8String - log.info("##############"+message) - if (message.contains("events")) { - count += 1 - log.info("[Got event nr {} for {} and with msg {} ] ", count, url, message) - // Try(ser.fromJson(msg.utf8String)) match { - // case Success(event) => - // listener.onReceive(eventType, cursor, event) - // case Failure(error) => - // val errorMsg = "Failed to Deserialize with error:" + error.getMessage - // listener.onError(url, null, ClientError("Failed to Deserialize with an error!", None)) + log.info("[EventConsumer] Event for lstnr {} and url {} with msg {}", listener.id, url, message) + Try(deserializeMsg(message, des)) match { + case Success(EventStreamBatch(cursor, Some(events))) => + listener.onReceive(url, cursor, events) + request4NextEvent(cursor) + case Success(EventStreamBatch(cursor, None)) => + log.info("[EventConsumer] Keep alive msg at {}", cursor) + request4NextEvent(cursor) + case Failure(error) => + val errorMsg = "[EventConsumer] Failed to Deserialize with error:" + error.getMessage + log.error(error, message) + listener.onError(url, null, ClientError("Failed to Deserialize with an error: "+errorMsg, None)) + //TODO: Restart itself } - // } - case OnNext(_) => - println("Got something") + case OnError(err: Throwable) => + log.error(err, "EventConsumer] Received Exception!") + context.stop(self) + case OnComplete => // 5 + log.info("[EventConsumer] Stream Completed!") +// context.stop(self) + } + + def request4NextEvent(cursor: Cursor) = { + + receivingActor ! NextEvent(Option(cursor)) } } trait MessageSplitter { - def deserializeMsg[T <: Event](msg: String)(implicit des: Deserializer[EventStreamBatch[T]]): EventStreamBatch[T] = des.from(msg) + def deserializeMsg[T <: Event](msg: String, des: Deserializer[EventStreamBatch[T]]): EventStreamBatch[T] = des.from(msg) } diff --git a/client/src/main/scala/org/zalando/nakadi/client/actor/EventReceivingActor.scala b/client/src/main/scala/org/zalando/nakadi/client/actor/EventReceivingActor.scala index e8faa77..c9f924d 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/actor/EventReceivingActor.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/actor/EventReceivingActor.scala @@ -1,29 +1,53 @@ package org.zalando.nakadi.client.actor -import akka.actor.ActorLogging -import akka.actor.Actor -import akka.stream.actor.ActorPublisher -import EventReceivingActor._ import scala.collection.mutable.Queue -import org.zalando.nakadi.client.scala.model.Cursor - +import org.zalando.nakadi.client.scala.model.Cursor +import akka.actor.Actor +import akka.actor.ActorLogging +import akka.stream.actor.ActorPublisher +import akka.stream.actor.ActorPublisherMessage.Cancel +import akka.stream.actor.ActorPublisherMessage.Request object EventReceivingActor { - case class NextEvent(lastReceivedCursor:Cursor) + case class NextEvent(lastReceivedCursor: Option[Cursor]) + case class HttpError(status:Int, msg:Option[String]) } +class EventReceivingActor(url: String) extends Actor with ActorLogging with ActorPublisher[Option[Cursor]] { + + import EventReceivingActor._ + var prev: Option[Cursor] = None + var curr: Option[Cursor] = None -class EventReceivingActor (url: String, cursor:Option[Cursor]) extends Actor with ActorLogging with ActorPublisher[Cursor] { - - override def preStart() { - self ! cursor - } + val queue: Queue[Option[Cursor]] = Queue() override def receive: Receive = { - case cursor: Cursor => onNext(cursor) + case NextEvent(cursor) => + log.info("[EventReceivingActor] Request next event chunk {} ", cursor) + queue.enqueue(cursor) + sendEvents() + case Request(cnt) => + log.info("[EventReceivingActor] Requested {} event chunks", cnt) + sendEvents() + case Cancel => + log.info("[EventReceivingActor] Stopping receiving events!") + context.stop(self) + case HttpError(status,_) => + log.error("[EventReceivingActor] {}",status) + case e => + log.error(e.toString()) + } + def sendEvents() { + while (isActive && totalDemand > 0 && !queue.isEmpty) { +// prev = curr + val cursor = queue.dequeue() + log.info("Requesting events 4 cursor {} at url {} ", cursor, url) + onNext(cursor) + } } -} \ No newline at end of file +} + diff --git a/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala index f702530..7055c44 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala @@ -21,13 +21,11 @@ import org.zalando.nakadi.client.scala.model._ import org.zalando.nakadi.client.utils.Uri import com.fasterxml.jackson.core.`type`.TypeReference - - private[client] class ClientImpl(connection: Connection, charSet: String = "UTF-8") extends Client with HttpFactory { implicit val materializer = connection.materializer import Uri._ import JacksonJsonMarshaller._ -// implicit val + // implicit val val logger = Logger(LoggerFactory.getLogger(this.getClass)) def getMetrics(): Future[Either[ClientError, Option[Metrics]]] = { logFutureEither(connection.get(URI_METRICS).flatMap(mapToEither(_)(deserializer(metricsTR)))) @@ -58,15 +56,15 @@ private[client] class ClientImpl(connection: Connection, charSet: String = "UTF- logFutureOption(connection.post(URI_EVENTS_OF_EVENT_TYPE.format(eventTypeName), events).flatMap(in => mapToOption(in))) } def publishEvents[T <: Event](eventTypeName: String, events: Seq[T]): Future[Option[ClientError]] = { - logFutureOption(connection.post(URI_EVENTS_OF_EVENT_TYPE.format(eventTypeName), events).flatMap(in => mapToOption(in))) + logFutureOption(connection.post(URI_EVENTS_OF_EVENT_TYPE.format(eventTypeName), events).flatMap(in => mapToOption(in))) } def publishEvent[T <: Event](name: String, event: T, ser: Serializer[T]): Future[Option[ClientError]] = { logFutureOption(connection.post(URI_EVENTS_OF_EVENT_TYPE.format(name), event).flatMap(in => mapToOption(in))) } - + def publishEvent[T <: Event](name: String, event: T): Future[Option[ClientError]] = { - logFutureOption(connection.post(URI_EVENTS_OF_EVENT_TYPE.format(name), event).flatMap(in => mapToOption(in))) + logFutureOption(connection.post(URI_EVENTS_OF_EVENT_TYPE.format(name), event).flatMap(in => mapToOption(in))) } def getPartitions(name: String): Future[Either[ClientError, Option[Seq[Partition]]]] = { @@ -89,11 +87,11 @@ private[client] class ClientImpl(connection: Connection, charSet: String = "UTF- None } - def subscribe[T <: Event](eventTypeName: String, parameters: StreamParameters, listener: Listener[T], typeRef: TypeReference[T]): Future[Option[ClientError]] = { - subscribe(eventTypeName, parameters, listener)(deserializer(typeRef)) - - } - def subscribe[T <: Event](eventType: String, params: StreamParameters, listener: Listener[T])(implicit des: Deserializer[T]): Future[Option[ClientError]] = { + def subscribe[T <: Event](eventTypeName: String, parameters: StreamParameters, listener: Listener[T], typeRef: TypeReference[EventStreamBatch[T]]): Future[Option[ClientError]] = { + subscribe(eventTypeName, parameters, listener)(deserializer(typeRef)) + + } + def subscribe[T <: Event](eventType: String, params: StreamParameters, listener: Listener[T])(implicit des: Deserializer[EventStreamBatch[T]]): Future[Option[ClientError]] = (eventType, params, listener) match { case (_, _, listener) if listener == null => @@ -106,16 +104,11 @@ private[client] class ClientImpl(connection: Connection, charSet: String = "UTF- case (eventType, StreamParameters(cursor, _, _, _, _, _, _), listener) if Option(eventType).isDefined => val url = URI_EVENTS_OF_EVENT_TYPE.format(eventType) - - val request = withHttpRequest(url, HttpMethods.GET, - RawHeader("Accept", "application/x-json-stream") :: withHeaders(Option(params)), //Headers - connection.tokenProvider(), Option(params)) - - logger.debug("Subscribing listener {} - cursor {} - parameters {} - eventType {} ", listener.id, cursor, params, eventType) - connection.subscribe(url, cursor, request, listener) + logger.debug("Subscribing listener {} - cursor {} - parameters {} - eventType {} - url {}", listener.id, cursor, params, eventType,url) + val finalUrl =withUrl(url, Some(params)) + connection.subscribe(finalUrl, cursor, listener)(des) Future.successful(None) } - } def unsubscribe[T <: Event](eventType: String, listener: Listener[T]): Future[Option[ClientError]] = ??? @@ -168,7 +161,7 @@ private[client] class ClientImpl(connection: Connection, charSet: String = "UTF- private[client] def mapToOption[T](response: HttpResponse): Future[Option[ClientError]] = { response.status match { case status if (status.isSuccess()) => - logger.debug("Success. http-status: %s", status.intValue().toString()) + logger.debug("Success. http-status: %s".format(status.intValue())) Future.successful(None) case status if (status.isRedirection()) => val msg = "Redirection - http-status: %s, reason[%s]".format(status.intValue().toString(), status.reason()) diff --git a/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala index c5a9735..6183c33 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala @@ -29,6 +29,14 @@ import akka.http.scaladsl.model.{ HttpHeader, HttpMethod, HttpMethods, HttpRespo import java.util.Optional import org.zalando.nakadi.client.utils.FutureConversions import org.zalando.nakadi.client.scala.model.Cursor +import org.zalando.nakadi.client.actor.EventReceivingActor +import akka.stream.actor.ActorPublisher +import org.zalando.nakadi.client.scala.model.EventStreamBatch +import akka.stream.Outlet +import akka.stream.ActorMaterializerSettings +import akka.stream.Attributes +import akka.NotUsed +import akka.http.scaladsl.settings.ConnectionPoolSettings trait Connection extends HttpFactory { @@ -47,7 +55,7 @@ trait Connection extends HttpFactory { def get4Java[T](endpoint: String, headers: Seq[HttpHeader], des: Deserializer[T]): java.util.concurrent.Future[Optional[T]] def post4Java[T](endpoint: String, model: T)(implicit serializer: Serializer[T]): java.util.concurrent.Future[Void] def put[T](endpoint: String, model: T)(implicit serializer: Serializer[T]): Future[HttpResponse] - def subscribe[T <: Event](url: String, cursor:Option[Cursor], request: HttpRequest, listener: Listener[T])(implicit des: Deserializer[T]) + def subscribe[T <: Event](url: String, cursor: Option[Cursor], listener: Listener[T])(implicit des: Deserializer[EventStreamBatch[T]]) def subscribeJava[T <: org.zalando.nakadi.client.java.model.Event](url: String, request: HttpRequest, listener: org.zalando.nakadi.client.java.Listener[T])(implicit des: Deserializer[T]) def stop(): Future[Terminated] @@ -92,13 +100,22 @@ sealed class ConnectionImpl(val host: String, val port: Int, val tokenProvider: private implicit val actorSystem = ActorSystem("Nakadi-Client-Connections") private implicit val http = Http(actorSystem) - implicit val materializer = ActorMaterializer() + implicit val materializer = ActorMaterializer( + ActorMaterializerSettings(actorSystem) + .withInputBuffer( + initialSize = 8, + maxSize = 16)) private val actors: Map[String, Actor] = Map() val logger = Logger(LoggerFactory.getLogger(this.getClass)) - + val cps = ConnectionPoolSettings(actorSystem).withMaxConnections(64).withPipeliningLimit(1).withMaxOpenRequests(64) val connection: Flow[HttpRequest, HttpResponse, Future[Http.OutgoingConnection]] = newSslContext(securedConnection, verifySSlCertificate) match { - case Some(result) => http.outgoingConnectionHttps(host, port, result) +// case Some(result) => +// val re=http.cachedHostConnectionPoolHttps(host, // +// port = port, // +// connectionContext = result, // +// settings = cps) +// case Some(result) => http.outgoingConnectionHttps(host, port, result) case None => logger.warn("Disabled HTTPS, switching to HTTP only!") http.outgoingConnection(host, port) @@ -118,7 +135,7 @@ sealed class ConnectionImpl(val host: String, val port: Int, val tokenProvider: Try(Unmarshal(entity).to[String].map(body => des.from(body))) match { case Success(result) => result.map(Optional.of(_)) - case Failure(error) => throw new RuntimeException(error.getMessage) + case Failure(error) => throw new RuntimeException(error.getMessage) } case HttpResponse(status, headers, entity, protocol) if (status.isFailure()) => @@ -159,7 +176,7 @@ sealed class ConnectionImpl(val host: String, val port: Int, val tokenProvider: private def serialize4Java[T](model: T)(implicit serializer: Serializer[T]): String = Try(serializer.to(model)) match { case Success(result) => result - case Failure(error) => throw new RuntimeException("Failed to serialize: "+error.getMessage) + case Failure(error) => throw new RuntimeException("Failed to serialize: " + error.getMessage) } private def response4Java[T](response: HttpResponse): Future[Option[String]] = response match { @@ -170,10 +187,10 @@ sealed class ConnectionImpl(val host: String, val port: Int, val tokenProvider: } case HttpResponse(status, headers, entity, protocol) if (status.isFailure()) => - Unmarshal(entity).to[String].map { x => - val msg = "http-stats(%s) - %s - problem: %s ".format(status.intValue(),x,status.defaultMessage()) - logger.warn(msg) - throw new RuntimeException(msg) + Unmarshal(entity).to[String].map { x => + val msg = "http-stats(%s) - %s - problem: %s ".format(status.intValue(), x, status.defaultMessage()) + logger.warn(msg) + throw new RuntimeException(msg) } } def delete(endpoint: String): Future[HttpResponse] = { @@ -198,27 +215,37 @@ sealed class ConnectionImpl(val host: String, val port: Int, val tokenProvider: def stop(): Future[Terminated] = actorSystem.terminate() - def subscribe[T <: Event](url: String, cursor:Option[Cursor], request: HttpRequest, listener: Listener[T])(implicit des: Deserializer[T]) = { - logger.info("Subscribing listener {} with request {}", listener.id, request.uri) + def subscribe[T <: Event](url: String, cursor: Option[Cursor], listener: Listener[T])(implicit des: Deserializer[EventStreamBatch[T]]) = { + logger.info("Subscribing listener {} to uri {}", listener.id, url) import EventConsumer._ - case class MyEventExample(orderNumber: String) - val subscriberRef = actorSystem.actorOf(Props(classOf[EventConsumer[T]], url, listener, des)) - val subscriber = ActorSubscriber[ByteString](subscriberRef) - val sink2 = Sink.fromSubscriber(subscriber) - val flow: Flow[HttpRequest, HttpResponse, Future[Http.OutgoingConnection]] = connection - - val req = Source.single(request) // - .via(flow) // - .buffer(200, OverflowStrategy.backpressure) // - .map(x => x.entity.dataBytes) - .runForeach(_.runWith(sink2)) - .onComplete { _ => - subscriberRef ! ShutdownMsg - logger.info("Shutting down") - } - } + import EventReceivingActor._ + + //Create the Actors + val eventReceiver = actorSystem.actorOf(Props(classOf[EventReceivingActor], url)) + val eventConsumer = actorSystem.actorOf(Props(classOf[EventConsumer[T]], url, listener, eventReceiver, des)) + val receiver = ActorPublisher[Option[Cursor]](eventReceiver) + val consumer = ActorSubscriber[ByteString](eventConsumer) + + // val A:Outlet[Option[]]= Source.fromPublisher(receiver) + val flow = connection // + // .async + // .withAttributes(Attributes.inputBuffer(initial = 8, max = 16)) + + Source.fromPublisher(receiver) + // Source.single(cursor) + // .map(withHttpRequest(url, _, None, tokenProvider)) + .via(toRequest(url: String)) + .via(flow) + .buffer(1024, OverflowStrategy.backpressure) + .filter { x => x.status.isSuccess() } + .map { x => x.entity.dataBytes } + .runForeach(_.runWith(Sink.fromSubscriber(consumer))) + eventReceiver ! NextEvent(cursor) + } + + def toRequest(url: String)(implicit m: ActorMaterializer): Flow[Option[Cursor], HttpRequest, NotUsed] = + Flow[Option[Cursor]].map(withHttpRequest(url, _, None, tokenProvider)) def subscribeJava[T <: org.zalando.nakadi.client.java.model.Event](url: String, request: HttpRequest, listener: org.zalando.nakadi.client.java.Listener[T])(implicit des: Deserializer[T]) = ??? - } diff --git a/client/src/main/scala/org/zalando/nakadi/client/scala/HttpFactory.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/HttpFactory.scala index 92c2010..13148fc 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/scala/HttpFactory.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/scala/HttpFactory.scala @@ -1,31 +1,30 @@ package org.zalando.nakadi.client.scala import scala.collection.immutable.Seq - import akka.http.scaladsl.model.{ ContentType, HttpHeader, HttpMethod, HttpRequest, MediaRange } import akka.http.scaladsl.model.MediaTypes.`application/json` import akka.http.scaladsl.model.Uri.apply import akka.http.scaladsl.model.headers import akka.http.scaladsl.model.headers.{ OAuth2BearerToken, RawHeader } +import akka.http.scaladsl.model.HttpMethods +import akka.http.scaladsl.model.headers.{ Accept, RawHeader } +import akka.http.scaladsl.model.MediaTypes.`application/json` +import org.zalando.nakadi.client.scala.model.Cursor +import org.slf4j.LoggerFactory +import com.typesafe.scalalogging.Logger + // trait HttpFactory { type TokenProvider = () => String - def withHeaders(params: Option[StreamParameters]): List[HttpHeader] = { + def withHeaders(params: Option[StreamParameters]): Seq[HttpHeader] = { params match { case Some(StreamParameters(cursor, _, _, _, _, _, flowId)) => - val nakadiCursor = cursor match { - case Some(value) if Option(value.partition).isDefined && Option(value.offset).isDefined => - ("X-Nakadi-Cursors", Some("[{\"partition\":\"" + value.partition + "\", \"offset\": \"" + value.offset + "\"}]")) - case None => - ("X-Nakadi-Cursors", None) - } - val parameters = List(nakadiCursor, ("X-Flow-Id", flowId)) - for { (key, optional) <- parameters; value <- optional } yield RawHeader(key, value) + withDefaultHeaders(cursor, flowId) case None => Nil } } - def withQueryParams(params: Option[StreamParameters]): List[String] = { + def withQueryParams(params: Option[StreamParameters]): Seq[String] = { params match { case Some(StreamParameters(_, batchLimit, streamLimit, batchFlushTimeout, streamTimeout, streamKeepAliveLimit, _)) => val parameters = List(("batch_limit", batchLimit), ("stream_limit", streamLimit), // @@ -35,13 +34,18 @@ trait HttpFactory { case None => Nil } } + + def withUrl(url:String, params: Option[StreamParameters])={ + val paramsList = withQueryParams(params) + val urlParams = if (!paramsList.isEmpty) paramsList.mkString("?", "&", "") else "" + url + urlParams + } def withHttpRequest(url: String, httpMethod: HttpMethod, additionalHeaders: Seq[HttpHeader], tokenProvider: TokenProvider, params: Option[StreamParameters]): HttpRequest = { val allHeaders: Seq[HttpHeader] = additionalHeaders :+ headers.Authorization(OAuth2BearerToken(tokenProvider())) val paramsList = withQueryParams(params) - val urlParams = if (!paramsList.isEmpty) paramsList.mkString("?", "&", "") else "" - val finalUrl = url + urlParams - HttpRequest(uri = finalUrl, method = httpMethod).withHeaders(allHeaders) + + HttpRequest(uri = url, method = httpMethod).withHeaders(allHeaders) } def withHttpRequestAndPayload(url: String, entity: String, httpMethod: HttpMethod, tokenProvider: TokenProvider): HttpRequest = { @@ -51,5 +55,24 @@ trait HttpFactory { .withEntity(ContentType(`application/json`), entity) } + def withDefaultHeaders(cursor: Option[Cursor], flowId: Option[String]): Seq[HttpHeader] = { + val nakadiCursor = cursor match { + case Some(value) if Option(value.partition).isDefined && Option(value.offset).isDefined => + ("X-Nakadi-Cursors", Some("[{\"partition\":\"" + value.partition + "\", \"offset\": \"" + value.offset + "\"}]")) + case None => + ("X-Nakadi-Cursors", None) + } + val parameters = List(nakadiCursor, ("X-Flow-Id", flowId)) + for { (key, optional) <- parameters; value <- optional } yield RawHeader(key, value) + } + + def withHttpRequest(url: String, cursor: Option[Cursor], flowId: Option[String], tokenProvider: () => String): HttpRequest = { + println("############### URL %s cursor %s".format(url,cursor)) + val customHeaders = withDefaultHeaders(cursor, flowId) :+ RawHeader("Accept", "application/x-json-stream") + val tokenHeader = headers.Authorization(OAuth2BearerToken(tokenProvider())) + val allHeaders: Seq[HttpHeader] = customHeaders :+ tokenHeader + HttpRequest(uri = url, method = HttpMethods.GET).withHeaders(allHeaders) + } + } // diff --git a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala index 67712ac..937b4fd 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala @@ -10,6 +10,8 @@ import org.zalando.nakadi.client.scala.model.JacksonJsonMarshaller import org.zalando.nakadi.client.scala.model.PartitionStrategy import org.zalando.nakadi.client.scala.model.SchemaType import org.zalando.nakadi.client.utils.ClientBuilder +import scala.concurrent.Await +import scala.concurrent.duration.DurationInt object EventCreationExample extends App { @@ -48,7 +50,7 @@ object EventCreationExample extends App { //First the eventType name, wich will be part of the URL: https://nakadi.test.io/event-types/{eventTypeName} //See the API for more information on the EventType model //https://github.com/zalando/nakadi/blob/nakadi-jvm/api/nakadi-event-bus-api.yaml#L1240 - val eventTypeName = "org.zalando.laas.meetings-2" + val eventTypeName = "MeetingsEvent-example-E" val owner = "team-laas" val category = EventTypeCategory.UNDEFINED // We want just to pass data without through Nakadi, simple schema-validation is enough! @@ -71,12 +73,18 @@ object EventCreationExample extends App { //You need to import the default Serializer if you don't sepecify your own! import JacksonJsonMarshaller._ - client.createEventType(eventType) - Thread.sleep(10000) +// client.createEventType(eventType) +// Thread.sleep(10000) // 4. Publish the EventType val event = new MeetingsEvent("2016-04-28T13:28:15+00:00", "Hackaton") - client.publishEvents(eventTypeName, List(event)) + val events = for { + a <- 1 to 25 + } yield MeetingsEvent("2016-04-28T13:28:15+00:00", "Hackaton"+a) + +// Thread.sleep(1000) + + Await.result(client.publishEvents(eventTypeName, events),10.seconds) client.stop() } \ No newline at end of file diff --git a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala index be10ab3..8073603 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala @@ -7,19 +7,30 @@ import org.zalando.nakadi.client.scala._ import org.zalando.nakadi.client.scala.model._ import EventCreationExample._ import com.fasterxml.jackson.core.`type`.TypeReference +import java.util.concurrent.atomic.AtomicLong /** * Your listener will have to implement the necessary */ -class MeetingsListener() extends Listener[MeetingsEvent] { +class MeetingsListener(val id: String) extends Listener[MeetingsEvent] { + private var eventCount: AtomicLong = new AtomicLong(0); - def id: String = "test" def onError(sourceUrl: String, cursor: Cursor, error: ClientError): Unit = { - println("YOOOOOOOOOOOOOO ") + println("An error occurred") } + def onSubscribed(): Unit = ??? + def onUnsubscribed(): Unit = ??? - def onReceive(sourceUrl: String, cursor: Cursor, event: Seq[MeetingsEvent]): Unit = ??? + + def onReceive(sourceUrl: String, cursor: Cursor, events: Seq[MeetingsEvent]): Unit = { + eventCount.addAndGet(events.size.toLong) + println("#####################################") + println(s"Received " + events.size.toLong) + println(s"Has a total of $eventCount") + println("#####################################") + + } } object EventListenerExample extends App { @@ -34,34 +45,35 @@ object EventListenerExample extends App { .withTokenProvider(ClientFactory.OAuth2Token()) // // .build(); .build() - - + /** * Initialize our Listener */ - val listener = new MeetingsListener() + val listener = new MeetingsListener("Tesst") /** * Create the Parameters with the cursor. */ - - val cursor = Cursor(0, 0) - - val parameters = new StreamParameters( - cursor = Some(cursor) - ,batchLimit = Some(30) -// ,batchFlushTimeout = Some(1) -// ,streamKeepAliveLimit=Some(4) -// ,streamTimeout=Some(1) - ) + + val cursor = Cursor(0, 26180) + + val parameters = new StreamParameters( + cursor = Some(cursor) // + , batchLimit = Some(2) // Maximum number of `Event`s in each chunk (and therefore per partition) of the stream. + , streamLimit = Some(2) // Maximum number of `Event`s to stream (over all partitions being streamed in this + //connection). + , batchFlushTimeout = Some(15) // Maximum time in seconds to wait for the flushing of each chunk (per partition). + // ,streamKeepAliveLimit=Some(4) + , streamTimeout = Some(40) + ) /** * Create TypeReference for the JacksonObjectMapper */ - implicit def typeRef: TypeReference[MeetingsEvent] = new TypeReference[MeetingsEvent] {} + implicit def typeRef: TypeReference[EventStreamBatch[MeetingsEvent]] = new TypeReference[EventStreamBatch[MeetingsEvent]] {} import org.zalando.nakadi.client.scala.model.JacksonJsonMarshaller._ - + val eventTypeName = "MeetingsEvent-example-E" val result = client.subscribe(eventTypeName, parameters, listener) diff --git a/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala b/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala index 883c271..081668c 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala @@ -2,8 +2,8 @@ package org.zalando.nakadi.client.scala object ClientFactory { import sys.process._ import scala.language.postfixOps - def host():String = "nakadi-sandbox.my-test.fernando.do" - def OAuth2Token(): () => String = () => "" + def host():String = "nakadi-sandbox.aruha-test.zalan.do" + def OAuth2Token(): () => String = () => "9b77d609-e613-4218-ad9c-fba4d2f0017d" def getToken():String = OAuth2Token().apply() def port():Integer = 443 def connection():Connection = Connection.newConnection(host, port, OAuth2Token(), true, false) diff --git a/it/src/main/scala/org/zalando/nakadi/client/subscription/Subscriber.scala b/it/src/main/scala/org/zalando/nakadi/client/subscription/Subscriber.scala index 70891c5..1d35390 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/subscription/Subscriber.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/subscription/Subscriber.scala @@ -34,7 +34,7 @@ class Subscriber { // ,streamTimeout=Some(10) // ,streamKeepAliveLimit =Some(10) ) - client.subscribe(eventType, params, listener) +// client.subscribe(eventType, params, listener) } def printPartitions() = { diff --git a/it/src/test/scala/org/zalando/nakadi/client/ClientSubscriptionTest.scala b/it/src/test/scala/org/zalando/nakadi/client/ClientSubscriptionTest.scala index 1ce6b58..c1f2c31 100644 --- a/it/src/test/scala/org/zalando/nakadi/client/ClientSubscriptionTest.scala +++ b/it/src/test/scala/org/zalando/nakadi/client/ClientSubscriptionTest.scala @@ -15,7 +15,7 @@ class ClientSubscriptionTest extends WordSpec with Matchers with ModelFactory { import ClientFactory._ import JacksonJsonMarshaller._ case class MyEventExample(orderNumber: String) extends Event - implicit def myEventExampleTR: TypeReference[MyEventExample] = new TypeReference[MyEventExample] {} + implicit def myEventExampleTR: TypeReference[EventStreamBatch[MyEventExample]] = new TypeReference[EventStreamBatch[MyEventExample]] {} val eventAction = new EventActions(client) val eventTypeAction = new EventTypesActions(client) "POST/PUT/GET/DELETE single EventType " in { diff --git a/it/src/test/scala/org/zalando/nakadi/client/example2/Ecample3.scala b/it/src/test/scala/org/zalando/nakadi/client/example2/Ecample3.scala index 7eadf9b..323a63c 100644 --- a/it/src/test/scala/org/zalando/nakadi/client/example2/Ecample3.scala +++ b/it/src/test/scala/org/zalando/nakadi/client/example2/Ecample3.scala @@ -54,7 +54,7 @@ object Test extends App with HttpFactory { // Request Parameters val eventName = "/event-types/test-client-integration-event-1936085527-148383828851369665/events" val params = Some(StreamParameters()) - val headers = RawHeader("Accept", "application/x-json-stream") :: withHeaders(params) + val headers = withHeaders(params) :+ RawHeader("Accept", "application/x-json-stream") val request = withHttpRequest(eventName, HttpMethods.GET, headers, OAuth2Token(),None) //Model diff --git a/it/src/test/scala/org/zalando/nakadi/client/example2/HttpClientExample.scala b/it/src/test/scala/org/zalando/nakadi/client/example2/HttpClientExample.scala index 1cc644d..c031ad4 100644 --- a/it/src/test/scala/org/zalando/nakadi/client/example2/HttpClientExample.scala +++ b/it/src/test/scala/org/zalando/nakadi/client/example2/HttpClientExample.scala @@ -32,7 +32,7 @@ object HttpClient extends App with HttpFactory { val eventName = "/event-types/test-client-integration-event-1936085527-148383828851369665/events" val cursor = Cursor(0,30115) val params = Some(StreamParameters()) - val headers = RawHeader("Accept", "application/x-json-stream") :: withHeaders(params) + val headers = withHeaders(params) :+ RawHeader("Accept", "application/x-json-stream") val request = withHttpRequest(eventName, HttpMethods.GET, headers, OAuth2Token(),None) case class MyEventExample(orderNumber: String) implicit val myEvent = new TypeReference[MyEventExample] {} @@ -40,7 +40,7 @@ object HttpClient extends App with HttpFactory { receiver.listen() } -class ReceiverGraph[T](eventName: String, connection: Connection, request: HttpRequest, headers: List[HttpHeader]) // +class ReceiverGraph[T](eventName: String, connection: Connection, request: HttpRequest, headers: Seq[HttpHeader]) // (implicit system: ActorSystem, m: ActorMaterializer, des: Deserializer[T]) { import GraphDSL.Implicits._ diff --git a/project/Dependencies.scala b/project/Dependencies.scala index b2954ef..4c380d3 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -2,7 +2,7 @@ import sbt._ object Dependencies { - val akkaVersion = "2.4.2" + val akkaVersion = "2.4.4" val apiDeps = { Seq( From d2beda5ee4ad0bc39343c1af452b0e5615ca80c9 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Mon, 2 May 2016 23:07:36 +0200 Subject: [PATCH 034/183] techjira:LAAS-60 Adding Java Model! --- .../scala/org/zalando/nakadi/client/scala/ClientFactory.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala b/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala index 081668c..d40896c 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala @@ -2,8 +2,8 @@ package org.zalando.nakadi.client.scala object ClientFactory { import sys.process._ import scala.language.postfixOps - def host():String = "nakadi-sandbox.aruha-test.zalan.do" - def OAuth2Token(): () => String = () => "9b77d609-e613-4218-ad9c-fba4d2f0017d" + def host():String = "nakadi-sandbox.a-test.a.do" + def OAuth2Token(): () => String = () => " " def getToken():String = OAuth2Token().apply() def port():Integer = 443 def connection():Connection = Connection.newConnection(host, port, OAuth2Token(), true, false) From a7a66688510a67f38ab2d90f28b11234ff2f5082 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Tue, 3 May 2016 00:53:46 +0200 Subject: [PATCH 035/183] techjira:LAAS-60 YAAY! RECEIVING ALL EVENTS NOW! --- .../client/actor/EventConsumingActor.scala | 1 - .../nakadi/client/scala/Connection.scala | 35 +++++++++++-------- .../examples/scala/EventListenerExample.scala | 4 +-- .../nakadi/client/scala/ClientFactory.scala | 4 +-- 4 files changed, 25 insertions(+), 19 deletions(-) diff --git a/client/src/main/scala/org/zalando/nakadi/client/actor/EventConsumingActor.scala b/client/src/main/scala/org/zalando/nakadi/client/actor/EventConsumingActor.scala index 0db9112..9d1724a 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/actor/EventConsumingActor.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/actor/EventConsumingActor.scala @@ -42,7 +42,6 @@ class EventConsumer[T <: Event](url: String, // // val requestStrategy = WatermarkRequestStrategy(50) override protected def requestStrategy: RequestStrategy = new RequestStrategy { override def requestDemand(remainingRequested: Int): Int = { - log.info("########## Received: {}", remainingRequested) Math.max(remainingRequested, 10) } } diff --git a/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala index 6183c33..ee8d5bd 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala @@ -37,6 +37,8 @@ import akka.stream.ActorMaterializerSettings import akka.stream.Attributes import akka.NotUsed import akka.http.scaladsl.settings.ConnectionPoolSettings +import akka.http.scaladsl.Http.HostConnectionPool +import akka.stream.scaladsl.Framing trait Connection extends HttpFactory { @@ -98,6 +100,9 @@ object Connection { sealed class ConnectionImpl(val host: String, val port: Int, val tokenProvider: () => String, securedConnection: Boolean, verifySSlCertificate: Boolean) extends Connection { import Connection._ + type HttpFlow[T] = Flow[(HttpRequest, T), (Try[HttpResponse], T), HostConnectionPool] + type StepResult[T] = (T, Option[String]) + private implicit val actorSystem = ActorSystem("Nakadi-Client-Connections") private implicit val http = Http(actorSystem) implicit val materializer = ActorMaterializer( @@ -110,12 +115,7 @@ sealed class ConnectionImpl(val host: String, val port: Int, val tokenProvider: val logger = Logger(LoggerFactory.getLogger(this.getClass)) val cps = ConnectionPoolSettings(actorSystem).withMaxConnections(64).withPipeliningLimit(1).withMaxOpenRequests(64) val connection: Flow[HttpRequest, HttpResponse, Future[Http.OutgoingConnection]] = newSslContext(securedConnection, verifySSlCertificate) match { -// case Some(result) => -// val re=http.cachedHostConnectionPoolHttps(host, // -// port = port, // -// connectionContext = result, // -// settings = cps) -// case Some(result) => http.outgoingConnectionHttps(host, port, result) + case Some(result) => http.outgoingConnectionHttps(host, port, result) case None => logger.warn("Disabled HTTPS, switching to HTTP only!") http.outgoingConnection(host, port) @@ -228,21 +228,28 @@ sealed class ConnectionImpl(val host: String, val port: Int, val tokenProvider: // val A:Outlet[Option[]]= Source.fromPublisher(receiver) val flow = connection // - // .async - // .withAttributes(Attributes.inputBuffer(initial = 8, max = 16)) + + val echo = Flow[ByteString] + .via(Framing.delimiter(ByteString("\n"), maximumFrameLength = 256, allowTruncation = true)) + .map(_.utf8String) Source.fromPublisher(receiver) - // Source.single(cursor) - // .map(withHttpRequest(url, _, None, tokenProvider)) - .via(toRequest(url: String)) + .via(requesCreator(url)) .via(flow) .buffer(1024, OverflowStrategy.backpressure) - .filter { x => x.status.isSuccess() } - .map { x => x.entity.dataBytes } + .via(requestRenderer) .runForeach(_.runWith(Sink.fromSubscriber(consumer))) eventReceiver ! NextEvent(cursor) - } + } + + def requesCreator(url:String):Flow[Option[Cursor],HttpRequest,NotUsed] = Flow[Option[Cursor]].map(withHttpRequest(url, _, None, tokenProvider)) + + def requestRenderer={//: Flow[HttpResponse,ByteString,NotUsed] = { + Flow[HttpResponse].filter(x => x.status.isSuccess()) + .map { x => x.entity.dataBytes.via(Framing.delimiter(ByteString("\n"), maximumFrameLength = 256, allowTruncation = true)) } + + } def toRequest(url: String)(implicit m: ActorMaterializer): Flow[Option[Cursor], HttpRequest, NotUsed] = Flow[Option[Cursor]].map(withHttpRequest(url, _, None, tokenProvider)) diff --git a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala index 8073603..af5bc56 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala @@ -55,12 +55,12 @@ object EventListenerExample extends App { * Create the Parameters with the cursor. */ - val cursor = Cursor(0, 26180) + val cursor = Cursor(0, 4) val parameters = new StreamParameters( cursor = Some(cursor) // , batchLimit = Some(2) // Maximum number of `Event`s in each chunk (and therefore per partition) of the stream. - , streamLimit = Some(2) // Maximum number of `Event`s to stream (over all partitions being streamed in this +// , streamLimit = Some(2) // Maximum number of `Event`s to stream (over all partitions being streamed in this //connection). , batchFlushTimeout = Some(15) // Maximum time in seconds to wait for the flushing of each chunk (per partition). // ,streamKeepAliveLimit=Some(4) diff --git a/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala b/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala index d40896c..d9abdf8 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala @@ -2,8 +2,8 @@ package org.zalando.nakadi.client.scala object ClientFactory { import sys.process._ import scala.language.postfixOps - def host():String = "nakadi-sandbox.a-test.a.do" - def OAuth2Token(): () => String = () => " " + def host():String = "nakadi-sandbox.aruha-test.zalan.do" + def OAuth2Token(): () => String = () => "d0a7d93b-0904-41d9-ad6c-081b2bef5c48" def getToken():String = OAuth2Token().apply() def port():Integer = 443 def connection():Connection = Connection.newConnection(host, port, OAuth2Token(), true, false) From 6a5d191d586e58cf12662ee788a14da077d1c408 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Tue, 3 May 2016 10:21:38 +0200 Subject: [PATCH 036/183] techjira:LAAS-60 TESTED AND working as expected! Up tot the Java part! --- .../nakadi/client/scala/Connection.scala | 15 +++++------ .../examples/scala/EventCreationExample.scala | 26 +++++++++---------- .../examples/scala/EventListenerExample.scala | 8 +++--- .../nakadi/client/scala/ClientFactory.scala | 2 +- 4 files changed, 23 insertions(+), 28 deletions(-) diff --git a/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala index ee8d5bd..b27f61e 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala @@ -68,9 +68,9 @@ trait Connection extends HttpFactory { * Companion object with factory methods. */ object Connection { - + val RECEIVE_BUFFER_SIZE = 20480 /** - * + * Creates a new SSL context for usage with connections based on the HTTPS protocol. */ def newSslContext(secured: Boolean, verified: Boolean): Option[HttpsConnectionContext] = (secured, verified) match { case (true, true) => Some(new HttpsConnectionContext(SSLContext.getDefault)) @@ -87,7 +87,7 @@ object Connection { } /** - * Creates a new + * Creates a new Connection */ def newConnection(host: String, port: Int, tokenProvider: () => String, securedConnection: Boolean, verifySSlCertificate: Boolean): Connection = new ConnectionImpl(host, port, tokenProvider, securedConnection, verifySSlCertificate) @@ -113,7 +113,6 @@ sealed class ConnectionImpl(val host: String, val port: Int, val tokenProvider: private val actors: Map[String, Actor] = Map() val logger = Logger(LoggerFactory.getLogger(this.getClass)) - val cps = ConnectionPoolSettings(actorSystem).withMaxConnections(64).withPipeliningLimit(1).withMaxOpenRequests(64) val connection: Flow[HttpRequest, HttpResponse, Future[Http.OutgoingConnection]] = newSslContext(securedConnection, verifySSlCertificate) match { case Some(result) => http.outgoingConnectionHttps(host, port, result) case None => @@ -226,8 +225,6 @@ sealed class ConnectionImpl(val host: String, val port: Int, val tokenProvider: val receiver = ActorPublisher[Option[Cursor]](eventReceiver) val consumer = ActorSubscriber[ByteString](eventConsumer) - // val A:Outlet[Option[]]= Source.fromPublisher(receiver) - val flow = connection // val echo = Flow[ByteString] .via(Framing.delimiter(ByteString("\n"), maximumFrameLength = 256, allowTruncation = true)) @@ -235,8 +232,8 @@ sealed class ConnectionImpl(val host: String, val port: Int, val tokenProvider: Source.fromPublisher(receiver) .via(requesCreator(url)) - .via(flow) - .buffer(1024, OverflowStrategy.backpressure) + .via(connection) + .buffer(RECEIVE_BUFFER_SIZE, OverflowStrategy.backpressure) .via(requestRenderer) .runForeach(_.runWith(Sink.fromSubscriber(consumer))) eventReceiver ! NextEvent(cursor) @@ -247,7 +244,7 @@ sealed class ConnectionImpl(val host: String, val port: Int, val tokenProvider: def requestRenderer={//: Flow[HttpResponse,ByteString,NotUsed] = { Flow[HttpResponse].filter(x => x.status.isSuccess()) - .map { x => x.entity.dataBytes.via(Framing.delimiter(ByteString("\n"), maximumFrameLength = 256, allowTruncation = true)) } + .map { x => x.entity.dataBytes.via(Framing.delimiter(ByteString("\n"), maximumFrameLength = RECEIVE_BUFFER_SIZE, allowTruncation = true)) } } def toRequest(url: String)(implicit m: ActorMaterializer): Flow[Option[Cursor], HttpRequest, NotUsed] = diff --git a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala index 937b4fd..2a3295d 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala @@ -15,8 +15,6 @@ import scala.concurrent.duration.DurationInt object EventCreationExample extends App { - - // 1. Create the client import org.zalando.nakadi.client.utils.ClientBuilder @@ -50,7 +48,7 @@ object EventCreationExample extends App { //First the eventType name, wich will be part of the URL: https://nakadi.test.io/event-types/{eventTypeName} //See the API for more information on the EventType model //https://github.com/zalando/nakadi/blob/nakadi-jvm/api/nakadi-event-bus-api.yaml#L1240 - val eventTypeName = "MeetingsEvent-example-E" + val eventTypeName = "MeetingsEvent-example-E" val owner = "team-laas" val category = EventTypeCategory.UNDEFINED // We want just to pass data without through Nakadi, simple schema-validation is enough! @@ -73,18 +71,18 @@ object EventCreationExample extends App { //You need to import the default Serializer if you don't sepecify your own! import JacksonJsonMarshaller._ -// client.createEventType(eventType) -// Thread.sleep(10000) + // client.createEventType(eventType) + // Thread.sleep(10000) // 4. Publish the EventType - - val event = new MeetingsEvent("2016-04-28T13:28:15+00:00", "Hackaton") - val events = for { - a <- 1 to 25 - } yield MeetingsEvent("2016-04-28T13:28:15+00:00", "Hackaton"+a) - -// Thread.sleep(1000) - - Await.result(client.publishEvents(eventTypeName, events),10.seconds) + + // val event = new MeetingsEvent("2016-04-28T13:28:15+00:00", "Hackaton") + var events = for { + a <- 1 to 49976 + } yield MeetingsEvent("2016-04-28T13:28:15+00:00", "Hackaton" + a) + + // Thread.sleep(1000) + + Await.result(client.publishEvents(eventTypeName, events), 120.seconds) client.stop() } \ No newline at end of file diff --git a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala index af5bc56..2778f06 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala @@ -27,7 +27,7 @@ class MeetingsListener(val id: String) extends Listener[MeetingsEvent] { eventCount.addAndGet(events.size.toLong) println("#####################################") println(s"Received " + events.size.toLong) - println(s"Has a total of $eventCount") + println(s"Has a total of $eventCount events") println("#####################################") } @@ -59,12 +59,12 @@ object EventListenerExample extends App { val parameters = new StreamParameters( cursor = Some(cursor) // - , batchLimit = Some(2) // Maximum number of `Event`s in each chunk (and therefore per partition) of the stream. + , batchLimit = Some(1) // Maximum number of `Event`s in each chunk (and therefore per partition) of the stream. // , streamLimit = Some(2) // Maximum number of `Event`s to stream (over all partitions being streamed in this //connection). - , batchFlushTimeout = Some(15) // Maximum time in seconds to wait for the flushing of each chunk (per partition). + , batchFlushTimeout = Some(10) // Maximum time in seconds to wait for the flushing of each chunk (per partition). // ,streamKeepAliveLimit=Some(4) - , streamTimeout = Some(40) + , streamTimeout = Some(50) ) /** diff --git a/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala b/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala index d9abdf8..9283598 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala @@ -3,7 +3,7 @@ object ClientFactory { import sys.process._ import scala.language.postfixOps def host():String = "nakadi-sandbox.aruha-test.zalan.do" - def OAuth2Token(): () => String = () => "d0a7d93b-0904-41d9-ad6c-081b2bef5c48" + def OAuth2Token(): () => String = () => "bc79ea48-520b-42ee-b3b7-a7b6ed27ce9f " def getToken():String = OAuth2Token().apply() def port():Integer = 443 def connection():Connection = Connection.newConnection(host, port, OAuth2Token(), true, false) From 17ab06497a6f8e9e87b642760a2f216a246567fc Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Tue, 3 May 2016 10:58:43 +0200 Subject: [PATCH 037/183] techjira:LAAS-60 TESTED AND working as expected! Up tot the Java part! --- .../nakadi/client/scala/Connection.scala | 27 ++++++++++--------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala index b27f61e..cd3c6cc 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala @@ -68,7 +68,8 @@ trait Connection extends HttpFactory { * Companion object with factory methods. */ object Connection { - val RECEIVE_BUFFER_SIZE = 20480 + val RECEIVE_BUFFER_SIZE = 40960 + val EVENT_DELIMITER = "\n" /** * Creates a new SSL context for usage with connections based on the HTTPS protocol. */ @@ -225,11 +226,6 @@ sealed class ConnectionImpl(val host: String, val port: Int, val tokenProvider: val receiver = ActorPublisher[Option[Cursor]](eventReceiver) val consumer = ActorSubscriber[ByteString](eventConsumer) - - val echo = Flow[ByteString] - .via(Framing.delimiter(ByteString("\n"), maximumFrameLength = 256, allowTruncation = true)) - .map(_.utf8String) - Source.fromPublisher(receiver) .via(requesCreator(url)) .via(connection) @@ -238,15 +234,20 @@ sealed class ConnectionImpl(val host: String, val port: Int, val tokenProvider: .runForeach(_.runWith(Sink.fromSubscriber(consumer))) eventReceiver ! NextEvent(cursor) - } - - def requesCreator(url:String):Flow[Option[Cursor],HttpRequest,NotUsed] = Flow[Option[Cursor]].map(withHttpRequest(url, _, None, tokenProvider)) - - def requestRenderer={//: Flow[HttpResponse,ByteString,NotUsed] = { + } + + def requesCreator(url: String): Flow[Option[Cursor], HttpRequest, NotUsed] = Flow[Option[Cursor]].map(withHttpRequest(url, _, None, tokenProvider)) + + def requestRenderer = { //: Flow[HttpResponse,ByteString,NotUsed] = { Flow[HttpResponse].filter(x => x.status.isSuccess()) - .map { x => x.entity.dataBytes.via(Framing.delimiter(ByteString("\n"), maximumFrameLength = RECEIVE_BUFFER_SIZE, allowTruncation = true)) } - + .map { + x => x.entity.dataBytes.via(delimiterFlow) + } } + + def delimiterFlow = Flow[ByteString] + .via(Framing.delimiter(ByteString(EVENT_DELIMITER), maximumFrameLength = RECEIVE_BUFFER_SIZE, allowTruncation = true)) + def toRequest(url: String)(implicit m: ActorMaterializer): Flow[Option[Cursor], HttpRequest, NotUsed] = Flow[Option[Cursor]].map(withHttpRequest(url, _, None, tokenProvider)) From 8fc985b7c9ded298f71c0f0674ca35614e5ff04c Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Tue, 3 May 2016 10:59:08 +0200 Subject: [PATCH 038/183] techjira:LAAS-60 TESTED AND working as expected! Up tot the Java part! --- .../nakadi/client/examples/scala/EventCreationExample.scala | 2 +- .../nakadi/client/examples/scala/EventListenerExample.scala | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala index 2a3295d..05432d3 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala @@ -77,7 +77,7 @@ object EventCreationExample extends App { // val event = new MeetingsEvent("2016-04-28T13:28:15+00:00", "Hackaton") var events = for { - a <- 1 to 49976 + a <- 1 to 445 } yield MeetingsEvent("2016-04-28T13:28:15+00:00", "Hackaton" + a) // Thread.sleep(1000) diff --git a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala index 2778f06..4c098a9 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala @@ -59,12 +59,12 @@ object EventListenerExample extends App { val parameters = new StreamParameters( cursor = Some(cursor) // - , batchLimit = Some(1) // Maximum number of `Event`s in each chunk (and therefore per partition) of the stream. + , batchLimit = Some(100) // Maximum number of `Event`s in each chunk (and therefore per partition) of the stream. // , streamLimit = Some(2) // Maximum number of `Event`s to stream (over all partitions being streamed in this //connection). - , batchFlushTimeout = Some(10) // Maximum time in seconds to wait for the flushing of each chunk (per partition). + , batchFlushTimeout = Some(15) // Maximum time in seconds to wait for the flushing of each chunk (per partition). // ,streamKeepAliveLimit=Some(4) - , streamTimeout = Some(50) + , streamTimeout = Some(30) ) /** From 08c951f6d00188b6e64654a60ffd43ff4f32846e Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Tue, 3 May 2016 12:26:04 +0200 Subject: [PATCH 039/183] techjira:LAAS-60 TESTED AND working as expected! Up tot the Java part! --- .../client/actor/EventConsumingActor.scala | 67 ++++++++++--------- .../client/actor/EventReceivingActor.scala | 37 +++++----- .../nakadi/client/scala/Connection.scala | 26 +++---- .../examples/scala/EventListenerExample.scala | 2 +- .../nakadi/client/scala/ClientFactory.scala | 4 +- 5 files changed, 69 insertions(+), 67 deletions(-) diff --git a/client/src/main/scala/org/zalando/nakadi/client/actor/EventConsumingActor.scala b/client/src/main/scala/org/zalando/nakadi/client/actor/EventConsumingActor.scala index 9d1724a..afc49cc 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/actor/EventConsumingActor.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/actor/EventConsumingActor.scala @@ -23,23 +23,24 @@ import akka.stream.actor.ActorSubscriberMessage.OnNext import akka.stream.actor._ import akka.util.ByteString -object EventConsumer { - case class Msg(msg: ByteString) - case class ShutdownMsg() -} - -case class MyEventExample(orderNumber: String) extends Event +/** + * This actor serves as Sink for the pipeline.
      + * 1. It receives the message and the cursor from the payload. + * 2. It tries to deserialize the message to EventStreamBatch, containing a cursor and a sequence of Events. + * 3. Passes the deserialized sequence of events to the listener. + * 4. Sends the received cursor from the Publisher, to be passed to the pipeline. + * + */ class EventConsumer[T <: Event](url: String, // listener: Listener[T], // receivingActor: ActorRef, // des: Deserializer[EventStreamBatch[T]]) - extends Actor with ActorLogging with ActorSubscriber with MessageSplitter { + extends Actor with ActorLogging with ActorSubscriber { - import EventConsumer._ - import EventReceivingActor._ + var currCursor: Cursor = null + var prevCursor: Cursor = null -// val requestStrategy = WatermarkRequestStrategy(50) override protected def requestStrategy: RequestStrategy = new RequestStrategy { override def requestDemand(remainingRequested: Int): Int = { Math.max(remainingRequested, 10) @@ -49,37 +50,37 @@ class EventConsumer[T <: Event](url: String, // override def receive: Receive = { case OnNext(msg: ByteString) => val message = msg.utf8String - log.info("[EventConsumer] Event for lstnr {} and url {} with msg {}", listener.id, url, message) - Try(deserializeMsg(message, des)) match { - case Success(EventStreamBatch(cursor, Some(events))) => - listener.onReceive(url, cursor, events) - request4NextEvent(cursor) - case Success(EventStreamBatch(cursor, None)) => - log.info("[EventConsumer] Keep alive msg at {}", cursor) - request4NextEvent(cursor) - case Failure(error) => - val errorMsg = "[EventConsumer] Failed to Deserialize with error:" + error.getMessage - log.error(error, message) - listener.onError(url, null, ClientError("Failed to Deserialize with an error: "+errorMsg, None)) - //TODO: Restart itself - } + log.debug("[EventConsumer] Event for lstnr {} and url {} with msg {}", listener.id, url, message) + handleMsg(message) case OnError(err: Throwable) => - log.error(err, "EventConsumer] Received Exception!") + log.error(err, "[EventConsumer] onError [preCursor {} currCursor {} listner {} url {}]", prevCursor, currCursor, listener.id, url) context.stop(self) - case OnComplete => // 5 - log.info("[EventConsumer] Stream Completed!") -// context.stop(self) + case OnComplete => + log.info("[EventConsumer] onComplete [preCursor {} currCursor {} listner {} url {}]", prevCursor, currCursor, listener.id, url) } def request4NextEvent(cursor: Cursor) = { - + prevCursor = currCursor + currCursor = cursor + log.debug("[EventConsumer] NextEvent [preCursor {} currCursor {} listner {} url {}]", prevCursor, currCursor, listener.id, url) receivingActor ! NextEvent(Option(cursor)) } -} -trait MessageSplitter { - - def deserializeMsg[T <: Event](msg: String, des: Deserializer[EventStreamBatch[T]]): EventStreamBatch[T] = des.from(msg) + def handleMsg(message: String) = { + Try(deserializeMsg(message, des)) match { + case Success(EventStreamBatch(cursor, Some(events))) => + listener.onReceive(url, cursor, events) + request4NextEvent(cursor) + case Success(EventStreamBatch(cursor, None)) => + log.debug("[EventConsumer] Keep alive msg at {}", cursor) + request4NextEvent(cursor) + case Failure(error) => + val errorMsg = "[EventConsumer] DeserializationError [preCursor %s currCursor %s listner %s url %s error %s]".format(prevCursor, currCursor, listener.id, url, error.getMessage) + log.error(error, message) + listener.onError(url, null, ClientError("Failed to Deserialize with an error: " + errorMsg, None)) + } + } + def deserializeMsg(msg: String, des: Deserializer[EventStreamBatch[T]]): EventStreamBatch[T] = des.from(msg) } diff --git a/client/src/main/scala/org/zalando/nakadi/client/actor/EventReceivingActor.scala b/client/src/main/scala/org/zalando/nakadi/client/actor/EventReceivingActor.scala index c9f924d..b3496d5 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/actor/EventReceivingActor.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/actor/EventReceivingActor.scala @@ -12,42 +12,47 @@ import akka.stream.actor.ActorPublisherMessage.Request object EventReceivingActor { case class NextEvent(lastReceivedCursor: Option[Cursor]) - case class HttpError(status:Int, msg:Option[String]) + case class HttpError( + url: String, + cursor: Option[Cursor], + status: Int, + msg: Option[String]) } +/** + * This actor is a Publisher and serves as source for the pipeline.
      + * 1. It receives cursors from the EventConsumingActor. + * 2. It queues the received cursors. + * 3. It sends the next cursor(s) from the queue(FIFO) to the pipeline depending on the demand. + */ class EventReceivingActor(url: String) extends Actor with ActorLogging with ActorPublisher[Option[Cursor]] { import EventReceivingActor._ - var prev: Option[Cursor] = None - var curr: Option[Cursor] = None - val queue: Queue[Option[Cursor]] = Queue() override def receive: Receive = { case NextEvent(cursor) => - log.info("[EventReceivingActor] Request next event chunk {} ", cursor) + log.debug("[EventReceivingActor] Request next event chunk {} ", cursor) queue.enqueue(cursor) sendEvents() case Request(cnt) => - log.info("[EventReceivingActor] Requested {} event chunks", cnt) + log.debug("[EventReceivingActor] Requested {} event chunks", cnt) sendEvents() case Cancel => - log.info("[EventReceivingActor] Stopping receiving events!") + log.debug("[EventReceivingActor] Stopping the stream of events!") context.stop(self) - case HttpError(status,_) => - log.error("[EventReceivingActor] {}",status) + case HttpError(url, cursor, status, _) => + log.error("[EventReceivingActor] Cursor {} on URL {} caused error {}", cursor, url, status) case e => log.error(e.toString()) } - def sendEvents() { - while (isActive && totalDemand > 0 && !queue.isEmpty) { -// prev = curr - val cursor = queue.dequeue() - log.info("Requesting events 4 cursor {} at url {} ", cursor, url) - onNext(cursor) - } + def sendEvents() = while (isActive && totalDemand > 0 && !queue.isEmpty) { + val cursor = queue.dequeue() + log.debug("[EventReceivingActor] Requesting events [cursor {} at url {}] ", cursor, url) + onNext(cursor) } + } diff --git a/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala index cd3c6cc..355c895 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala @@ -17,7 +17,6 @@ import akka.stream.actor.{ ActorSubscriber, RequestStrategy } import akka.stream.scaladsl.{ Flow, Sink, Source } import akka.util.ByteString import javax.net.ssl.{ SSLContext, TrustManager, X509TrustManager } -import org.zalando.nakadi.client.actor.EventConsumer.ShutdownMsg import org.zalando.nakadi.client.Deserializer import org.zalando.nakadi.client.Serializer import org.zalando.nakadi.client.scala.model.Event @@ -217,40 +216,37 @@ sealed class ConnectionImpl(val host: String, val port: Int, val tokenProvider: def subscribe[T <: Event](url: String, cursor: Option[Cursor], listener: Listener[T])(implicit des: Deserializer[EventStreamBatch[T]]) = { logger.info("Subscribing listener {} to uri {}", listener.id, url) - import EventConsumer._ import EventReceivingActor._ //Create the Actors val eventReceiver = actorSystem.actorOf(Props(classOf[EventReceivingActor], url)) - val eventConsumer = actorSystem.actorOf(Props(classOf[EventConsumer[T]], url, listener, eventReceiver, des)) val receiver = ActorPublisher[Option[Cursor]](eventReceiver) + + val eventConsumer = actorSystem.actorOf(Props(classOf[EventConsumer[T]], url, listener, eventReceiver, des)) val consumer = ActorSubscriber[ByteString](eventConsumer) - Source.fromPublisher(receiver) - .via(requesCreator(url)) + val pipeline = Flow[Option[Cursor]].via(requestCreator(url)) .via(connection) .buffer(RECEIVE_BUFFER_SIZE, OverflowStrategy.backpressure) .via(requestRenderer) + + Source.fromPublisher(receiver) + .via(pipeline) .runForeach(_.runWith(Sink.fromSubscriber(consumer))) - eventReceiver ! NextEvent(cursor) + eventReceiver ! NextEvent(cursor) } - def requesCreator(url: String): Flow[Option[Cursor], HttpRequest, NotUsed] = Flow[Option[Cursor]].map(withHttpRequest(url, _, None, tokenProvider)) + def requestCreator(url: String): Flow[Option[Cursor], HttpRequest, NotUsed] = + Flow[Option[Cursor]].map(withHttpRequest(url, _, None, tokenProvider)) - def requestRenderer = { //: Flow[HttpResponse,ByteString,NotUsed] = { + def requestRenderer: Flow[HttpResponse, Source[ByteString, Any], NotUsed] = Flow[HttpResponse].filter(x => x.status.isSuccess()) - .map { - x => x.entity.dataBytes.via(delimiterFlow) - } - } + .map(_.entity.dataBytes.via(delimiterFlow)) def delimiterFlow = Flow[ByteString] .via(Framing.delimiter(ByteString(EVENT_DELIMITER), maximumFrameLength = RECEIVE_BUFFER_SIZE, allowTruncation = true)) - def toRequest(url: String)(implicit m: ActorMaterializer): Flow[Option[Cursor], HttpRequest, NotUsed] = - Flow[Option[Cursor]].map(withHttpRequest(url, _, None, tokenProvider)) - def subscribeJava[T <: org.zalando.nakadi.client.java.model.Event](url: String, request: HttpRequest, listener: org.zalando.nakadi.client.java.Listener[T])(implicit des: Deserializer[T]) = ??? } diff --git a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala index 4c098a9..9e2f98a 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala @@ -59,7 +59,7 @@ object EventListenerExample extends App { val parameters = new StreamParameters( cursor = Some(cursor) // - , batchLimit = Some(100) // Maximum number of `Event`s in each chunk (and therefore per partition) of the stream. + , batchLimit = Some(200) // Maximum number of `Event`s in each chunk (and therefore per partition) of the stream. // , streamLimit = Some(2) // Maximum number of `Event`s to stream (over all partitions being streamed in this //connection). , batchFlushTimeout = Some(15) // Maximum time in seconds to wait for the flushing of each chunk (per partition). diff --git a/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala b/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala index 9283598..4509dae 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala @@ -2,8 +2,8 @@ package org.zalando.nakadi.client.scala object ClientFactory { import sys.process._ import scala.language.postfixOps - def host():String = "nakadi-sandbox.aruha-test.zalan.do" - def OAuth2Token(): () => String = () => "bc79ea48-520b-42ee-b3b7-a7b6ed27ce9f " + def host():String = "nakadi-sandbox.my-test.fer.do" + def OAuth2Token(): () => String = () => def getToken():String = OAuth2Token().apply() def port():Integer = 443 def connection():Connection = Connection.newConnection(host, port, OAuth2Token(), true, false) From 33b590c232f6d0497e9fb37c7f76ece887dd135c Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Tue, 3 May 2016 12:32:39 +0200 Subject: [PATCH 040/183] techjira:LAAS-60 TESTED AND working as expected! Up tot the Java part! --- .../scala/org/zalando/nakadi/client/scala/ClientFactory.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala b/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala index 4509dae..a635e8a 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala @@ -3,7 +3,7 @@ object ClientFactory { import sys.process._ import scala.language.postfixOps def host():String = "nakadi-sandbox.my-test.fer.do" - def OAuth2Token(): () => String = () => + def OAuth2Token(): () => String = () => "" def getToken():String = OAuth2Token().apply() def port():Integer = 443 def connection():Connection = Connection.newConnection(host, port, OAuth2Token(), true, false) From 9335c96a1dc70169b92911c9743eb5975810f54a Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Tue, 3 May 2016 13:05:56 +0200 Subject: [PATCH 041/183] techjira:LAAS-60 TESTED AND working as expected! Up tot the Java part! --- .../org/zalando/nakadi/client/java/Listener.java | 6 +----- .../org/zalando/nakadi/client/scala/Listener.scala | 2 -- .../zalando/nakadi/client/scala/Connection.scala | 2 +- .../examples/scala/EventListenerExample.scala | 14 +++++--------- 4 files changed, 7 insertions(+), 17 deletions(-) diff --git a/api/src/main/java/org/zalando/nakadi/client/java/Listener.java b/api/src/main/java/org/zalando/nakadi/client/java/Listener.java index 1dc68c7..5c79547 100644 --- a/api/src/main/java/org/zalando/nakadi/client/java/Listener.java +++ b/api/src/main/java/org/zalando/nakadi/client/java/Listener.java @@ -6,11 +6,7 @@ import org.zalando.nakadi.client.java.model.Event; public interface Listener { - String id(); - - void onSubscribed(); - - void onUnsubscribed(); + String getId(); void onReceive(String sourceUrl, Cursor cursor, List event); diff --git a/api/src/main/scala/org/zalando/nakadi/client/scala/Listener.scala b/api/src/main/scala/org/zalando/nakadi/client/scala/Listener.scala index c9b4d7c..cfd67e4 100644 --- a/api/src/main/scala/org/zalando/nakadi/client/scala/Listener.scala +++ b/api/src/main/scala/org/zalando/nakadi/client/scala/Listener.scala @@ -8,8 +8,6 @@ import org.zalando.nakadi.client.scala.model.Event trait Listener[T <: Event] { def id: String - def onSubscribed(): Unit - def onUnsubscribed(): Unit def onReceive(sourceUrl: String, cursor: Cursor, event: Seq[T]): Unit def onError(sourceUrl: String, cursor: Cursor, error: ClientError): Unit } \ No newline at end of file diff --git a/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala index 355c895..9d24f1e 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala @@ -242,7 +242,7 @@ sealed class ConnectionImpl(val host: String, val port: Int, val tokenProvider: def requestRenderer: Flow[HttpResponse, Source[ByteString, Any], NotUsed] = Flow[HttpResponse].filter(x => x.status.isSuccess()) - .map(_.entity.dataBytes.via(delimiterFlow)) + .map(_.entity.withSizeLimit(Long.MaxValue).dataBytes.via(delimiterFlow)) def delimiterFlow = Flow[ByteString] .via(Framing.delimiter(ByteString(EVENT_DELIMITER), maximumFrameLength = RECEIVE_BUFFER_SIZE, allowTruncation = true)) diff --git a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala index 9e2f98a..bcf3a04 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala @@ -12,17 +12,13 @@ import java.util.concurrent.atomic.AtomicLong /** * Your listener will have to implement the necessary */ -class MeetingsListener(val id: String) extends Listener[MeetingsEvent] { +class EventCounterListener(val id: String) extends Listener[MeetingsEvent] { private var eventCount: AtomicLong = new AtomicLong(0); def onError(sourceUrl: String, cursor: Cursor, error: ClientError): Unit = { println("An error occurred") } - def onSubscribed(): Unit = ??? - - def onUnsubscribed(): Unit = ??? - def onReceive(sourceUrl: String, cursor: Cursor, events: Seq[MeetingsEvent]): Unit = { eventCount.addAndGet(events.size.toLong) println("#####################################") @@ -49,7 +45,7 @@ object EventListenerExample extends App { /** * Initialize our Listener */ - val listener = new MeetingsListener("Tesst") + val listener = new EventCounterListener("Test") /** * Create the Parameters with the cursor. @@ -59,12 +55,12 @@ object EventListenerExample extends App { val parameters = new StreamParameters( cursor = Some(cursor) // - , batchLimit = Some(200) // Maximum number of `Event`s in each chunk (and therefore per partition) of the stream. + , batchLimit = Some(100) // Maximum number of `Event`s in each chunk (and therefore per partition) of the stream. // , streamLimit = Some(2) // Maximum number of `Event`s to stream (over all partitions being streamed in this //connection). - , batchFlushTimeout = Some(15) // Maximum time in seconds to wait for the flushing of each chunk (per partition). +// , batchFlushTimeout = Some(15) // Maximum time in seconds to wait for the flushing of each chunk (per partition). // ,streamKeepAliveLimit=Some(4) - , streamTimeout = Some(30) +// , streamTimeout = Some(30) ) /** From c4be64ba444784e2a3ba33820466524a0161196b Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Tue, 3 May 2016 17:55:06 +0200 Subject: [PATCH 042/183] techjira:LAAS-60 Desploment works! --- .../zalando/nakadi/client/java/Listener.java | 2 +- .../zalando/nakadi/client/scala/Client.scala | 24 ++--- .../nakadi/client/scala/Connection.scala | 9 +- .../examples/java/EventListenerExample.java | 96 +++++++++++++++++++ .../nakadi/client/scala/ClientFactory.scala | 4 +- project/Build.scala | 53 ++++++---- project/Testing.scala | 35 ------- 7 files changed, 153 insertions(+), 70 deletions(-) create mode 100644 it/src/main/java/org/zalando/nakadi/client/examples/java/EventListenerExample.java delete mode 100644 project/Testing.scala diff --git a/api/src/main/java/org/zalando/nakadi/client/java/Listener.java b/api/src/main/java/org/zalando/nakadi/client/java/Listener.java index 5c79547..980101b 100644 --- a/api/src/main/java/org/zalando/nakadi/client/java/Listener.java +++ b/api/src/main/java/org/zalando/nakadi/client/java/Listener.java @@ -8,7 +8,7 @@ public interface Listener { String getId(); - void onReceive(String sourceUrl, Cursor cursor, List event); + void onReceive(String sourceUrl, Cursor cursor, List events); void onError(String sourceUrl, Cursor cursor, ClientError error); } diff --git a/api/src/main/scala/org/zalando/nakadi/client/scala/Client.scala b/api/src/main/scala/org/zalando/nakadi/client/scala/Client.scala index 48ea341..0807722 100644 --- a/api/src/main/scala/org/zalando/nakadi/client/scala/Client.scala +++ b/api/src/main/scala/org/zalando/nakadi/client/scala/Client.scala @@ -101,7 +101,7 @@ trait Client { def publishEvent[T <: Event](eventTypeName: String, event: T, ser: Serializer[T]): Future[Option[ClientError]] /** - * Publishes a single Events for the given EventType. + * Publishes a single Events to the given EventType. * {{{ * curl --request POST -d @fileWithEvent /event-types/{name}/events * }}} @@ -112,7 +112,7 @@ trait Client { def publishEvent[T <: Event](eventTypeName: String, event: T): Future[Option[ClientError]] /** - * List the partitions for the given EventType. + * List the partitions to the given EventType. * {{{ * curl --request GET /event-types/{name}/partitions * }}} @@ -155,26 +155,26 @@ trait Client { /** * Registers the subscription of a listener to start streaming events from a partition in non-blocking fashion. * - * @eventType - Name of the EventType to listen for. - * @parameters - Parameters for the streaming of events. - * @listener - Listener to pass the event to when it is received. - * @des - Json Marshaller(implicit) to deserialize the event to Json. + * @param eventTypeName - Name of the EventType to listen for. + * @param parameters - Parameters for the streaming of events. + * @param listener - Listener to pass the event to when it is received. + * @param des - Json Marshaller(implicit) to deserialize the event to Json. */ def subscribe[T <: Event](eventTypeName: String, parameters: StreamParameters, listener: Listener[T])(implicit des: Deserializer[EventStreamBatch[T]]): Future[Option[ClientError]] /** * Registers the subscription of a listener to start streaming events from a partition in non-blocking fashion. * - * @eventType - Name of the EventType to listen for. - * @parameters - Parameters for the streaming of events. - * @listener - Listener to pass the event to when it is received. - * @typeRef - TypeReference/Helper for using with the Jackson-Objectmapper to deserializing the event to json. + * @param eventTypeName - Name of the EventType to listen for. + * @param parameters - Parameters for the streaming of events. + * @param listener - Listener to pass the event to when it is received. + * @param typeRef - TypeReference/Helper for using with the Jackson-Objectmapper to deserializing the event to json. */ def subscribe[T <: Event](eventTypeName: String, parameters: StreamParameters, listener: Listener[T], typeRef: TypeReference[EventStreamBatch[T]]): Future[Option[ClientError]] /** * Removes the subscription of a listener, to stop streaming events from a partition. * - * @eventType - Name of the EventType. - * @listener - Listener to unsubscribe from the streaming events. + * @param eventTypeName - Name of the EventType. + * @param listener - Listener to unsubscribe from the streaming events. */ def unsubscribe[T <: Event](eventTypeName: String, listener: Listener[T]): Future[Option[ClientError]] diff --git a/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala index 9d24f1e..a3c82ea 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala @@ -237,16 +237,19 @@ sealed class ConnectionImpl(val host: String, val port: Int, val tokenProvider: eventReceiver ! NextEvent(cursor) } - def requestCreator(url: String): Flow[Option[Cursor], HttpRequest, NotUsed] = + private def requestCreator(url: String): Flow[Option[Cursor], HttpRequest, NotUsed] = Flow[Option[Cursor]].map(withHttpRequest(url, _, None, tokenProvider)) - def requestRenderer: Flow[HttpResponse, Source[ByteString, Any], NotUsed] = + private def requestRenderer: Flow[HttpResponse, Source[ByteString, Any], NotUsed] = Flow[HttpResponse].filter(x => x.status.isSuccess()) .map(_.entity.withSizeLimit(Long.MaxValue).dataBytes.via(delimiterFlow)) - def delimiterFlow = Flow[ByteString] + private def delimiterFlow = Flow[ByteString] .via(Framing.delimiter(ByteString(EVENT_DELIMITER), maximumFrameLength = RECEIVE_BUFFER_SIZE, allowTruncation = true)) def subscribeJava[T <: org.zalando.nakadi.client.java.model.Event](url: String, request: HttpRequest, listener: org.zalando.nakadi.client.java.Listener[T])(implicit des: Deserializer[T]) = ??? + + + } diff --git a/it/src/main/java/org/zalando/nakadi/client/examples/java/EventListenerExample.java b/it/src/main/java/org/zalando/nakadi/client/examples/java/EventListenerExample.java new file mode 100644 index 0000000..18e57ed --- /dev/null +++ b/it/src/main/java/org/zalando/nakadi/client/examples/java/EventListenerExample.java @@ -0,0 +1,96 @@ +package org.zalando.nakadi.client.examples.java; + +import java.util.List; +import java.util.concurrent.atomic.AtomicLong; + +import org.zalando.nakadi.client.java.Client; +import org.zalando.nakadi.client.java.ClientError; +import org.zalando.nakadi.client.java.model.Cursor; +import org.zalando.nakadi.client.java.model.Event; +import org.zalando.nakadi.client.scala.ClientFactory; +import org.zalando.nakadi.client.utils.ClientBuilder; +import org.zalando.nakadi.client.java.Listener; + +public class EventListenerExample { + + /** + * Define how your event should look like + */ + public class MeetingsEvent implements Event { + private final String date; + private final String topic; + + public MeetingsEvent(String date, String topic) { + this.date = date; + this.topic = topic; + } + + public String getDate() { + return date; + } + + public String getTopic() { + return topic; + } + + } + + /** + * Implement the Listener interface + */ + class EventCounterListener implements org.zalando.nakadi.client.java.Listener { + private final String id; + private AtomicLong eventCount = new AtomicLong(0); + + public EventCounterListener(String id) { + this.id = id; + } + + @Override + public String getId() { + return id; + } + + @Override + public void onReceive(String eventUrl, Cursor cursor, List events) { + eventCount.addAndGet(events.size()); + System.out.println("#####################################"); + System.out.println("Received " + events.size()); + System.out.println(String.format("Has a total of %d events",eventCount.get()) ); + System.out.println("#####################################"); + + } + + @Override + public void onError(String eventUrl, Cursor cursor, ClientError error) { + System.err.println(String.format("Error %s %s %s",eventUrl, cursor, error)); + + } + + } + + public static void main(String[] args) { + EventListenerExample example= new EventListenerExample(); + /** + * Create client + */ + final Client client = new ClientBuilder()// + .withHost(ClientFactory.host())// + .withSecuredConnection(true) // s + .withVerifiedSslCertificate(false) // s + .withTokenProvider4Java(() -> ClientFactory.getToken())// + .buildJavaClient(); + + + + /** + * Initialize our Listener + */ +// Listener listener = example.new EventCounterListener("Test"); + + + + + + } +} diff --git a/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala b/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala index a635e8a..883c271 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala @@ -2,8 +2,8 @@ package org.zalando.nakadi.client.scala object ClientFactory { import sys.process._ import scala.language.postfixOps - def host():String = "nakadi-sandbox.my-test.fer.do" - def OAuth2Token(): () => String = () => "" + def host():String = "nakadi-sandbox.my-test.fernando.do" + def OAuth2Token(): () => String = () => "" def getToken():String = OAuth2Token().apply() def port():Integer = 443 def connection():Connection = Connection.newConnection(host, port, OAuth2Token(), true, false) diff --git a/project/Build.scala b/project/Build.scala index 9343579..784c347 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -10,6 +10,15 @@ EclipseKeys.createSrc := EclipseCreateSrc.Default + EclipseCreateSrc.Resource EclipseKeys.withSource := true +def whereToPublishTo(isItSnapshot:Boolean) = { + val nexus = "https://maven.zalando.net/" + if (isItSnapshot) + Some("snapshots" at nexus + "content/repositories/snapshots") + else + Some("releases" at nexus + "content/repositories/releases") + } + + val defaultOptions= Seq( "-deprecation", // Emit warning and location for usages of deprecated APIs. "-feature", // Emit warning and location for usages of features that should be imported explicitly. @@ -22,20 +31,21 @@ val defaultOptions= Seq( "-Ywarn-nullary-override", // Warn when non-nullary overrides nullary, e.g. def foo() over def foo. "-Ywarn-numeric-widen" // Warn when numerics are widened. ) -lazy val commonSettings = Seq( - organization := "org.zalando", - version := "0.1.0", - scalaVersion := "2.11.7" -) + +lazy val root = project.in(file(".")) + .settings(publishTo := whereToPublishTo(isSnapshot.value)) + .aggregate(api, client) lazy val api = withDefaults( "nakadi-klients-api", project.in(file("api")) + ,true ).settings(libraryDependencies ++= apiDeps) lazy val client = withDefaults( "nakadi-klients", project.in(file("client")).dependsOn(api) + ,true ).settings(libraryDependencies ++= clientDeps) @@ -51,18 +61,27 @@ lazy val client = withDefaults( ).settings(libraryDependencies ++= clientDeps) -def withDefaults(projectName:String, project:sbt.Project)={ - project.settings( - name := projectName, - organization := "org.zalando.nakadi.client", - scalaVersion := "2.11.7", - resolvers += Resolver.mavenLocal, - resolvers += "Maven Central Server" at "http://repo1.maven.org/maven2", - scalacOptions ++= defaultOptions) - .configs(Configs.all: _*) - .settings(Testing.settings: _*) - -} + def withDefaults(projectName:String, project:sbt.Project, publish:Boolean = false)={ + project.settings( + name := projectName, + organization := "org.zalando.nakadi.client", + version := "2.0-SNAPSHOT", + crossPaths := false, + scalaVersion := "2.11.7", + publishTo := whereToPublishTo(isSnapshot.value), + resolvers += Resolver.mavenLocal, + resolvers += "Maven Central Server" at "http://repo1.maven.org/maven2", + scalacOptions ++= defaultOptions) + .configs(Configs.all: _*) + /*.settings( + publishArtifact in (Compile, packageDoc) := true //To Publish or Not to Publish scala doc jar + ,publishArtifact in (Compile, packageSrc) := true //To Publish or Not to Publish src jar + ,publishArtifact := publish // To Publish or Not to Publish + ,publishArtifact in Test := false //To Publish or Not to Publish test jar + ,sources in (Compile,doc) := Seq.empty + ) + */ + } } diff --git a/project/Testing.scala b/project/Testing.scala deleted file mode 100644 index ea14154..0000000 --- a/project/Testing.scala +++ /dev/null @@ -1,35 +0,0 @@ -import sbt._ -import Keys._ -import sbt.Keys._ - - - -object Testing { - import Configs._ - lazy val testAll = TaskKey[Unit]("test-all") - - private lazy val itSettings = - inConfig(IntegrationTest)(Defaults.testSettings) ++ Seq( - fork in IntegrationTest := false, - parallelExecution in IntegrationTest := false - ,unmanagedResourceDirectories in Compile += baseDirectory.value / "src/it/scala" - //scalaSource in IntegrationTest := baseDirectory.value / "src/it/scala", - //resourceDirectory in IntegrationTest := baseDirectory.value / "src/it/resources" - //,sourceDirectory in Compile := baseDirectory.value / "src/it/scala" - //,sourceDirectory in IntegrationTestTest <<= baseDirectory(_ / "src/test/scala") - //,resourceDirectory in Compile <<= baseDirectory(_ / "resources") - ) - - private lazy val e2eSettings = - inConfig(EndToEndTest)(Defaults.testSettings) ++ - Seq( - fork in EndToEndTest := false, - parallelExecution in EndToEndTest := false, - scalaSource in EndToEndTest := baseDirectory.value / "src/e2e/scala", - resourceDirectory in IntegrationTest := baseDirectory.value / "src/it/resources" - ) - - lazy val settings = itSettings ++ e2eSettings ++ Seq( - testAll <<= (test in EndToEndTest).dependsOn((test in IntegrationTest).dependsOn(test in Test)) - ) -} From 187262c2c9753b1ffe271836ee2ac081d6df9d8f Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Wed, 4 May 2016 10:14:50 +0200 Subject: [PATCH 043/183] techjira:LAAS-60 StreamParameters were not correct! --- .../zalando/nakadi/client/java/Listener.java | 4 +- .../nakadi/client/java/StreamParameters.java | 64 ++++++++++++++++--- .../nakadi/client/java/model/Cursor.java | 2 + .../zalando/nakadi/client/scala/Client.scala | 2 +- .../examples/scala/EventListenerExample.scala | 2 +- 5 files changed, 61 insertions(+), 13 deletions(-) diff --git a/api/src/main/java/org/zalando/nakadi/client/java/Listener.java b/api/src/main/java/org/zalando/nakadi/client/java/Listener.java index 980101b..9d67619 100644 --- a/api/src/main/java/org/zalando/nakadi/client/java/Listener.java +++ b/api/src/main/java/org/zalando/nakadi/client/java/Listener.java @@ -8,7 +8,7 @@ public interface Listener { String getId(); - void onReceive(String sourceUrl, Cursor cursor, List events); + void onReceive(String eventUrl, Cursor cursor, List events); - void onError(String sourceUrl, Cursor cursor, ClientError error); + void onError(String eventUrl, Cursor cursor, ClientError error); } diff --git a/api/src/main/java/org/zalando/nakadi/client/java/StreamParameters.java b/api/src/main/java/org/zalando/nakadi/client/java/StreamParameters.java index 14fdf60..13be205 100644 --- a/api/src/main/java/org/zalando/nakadi/client/java/StreamParameters.java +++ b/api/src/main/java/org/zalando/nakadi/client/java/StreamParameters.java @@ -1,20 +1,66 @@ package org.zalando.nakadi.client.java; +import java.util.Optional; + +import org.zalando.nakadi.client.java.model.Cursor; + public class StreamParameters { - private final Integer partition; - private final Integer offset; + private final Optional cursor; + private final Optional batchLimit; + private final Optional streamLimit; + private final Optional batchFlushTimeout; + private final Optional streamTimeout; + private final Optional streamKeepAliveLimit; + private final Optional flowId; + + public StreamParameters(Optional cursor, Optional batchLimit, Optional streamLimit, + Optional batchFlushTimeout, Optional streamTimeout, Optional streamKeepAliveLimit, + Optional flowId) { + this.cursor = cursor; + this.batchLimit = batchLimit; + this.streamLimit = streamLimit; + this.batchFlushTimeout = batchFlushTimeout; + this.streamTimeout = streamTimeout; + this.streamKeepAliveLimit = streamKeepAliveLimit; + this.flowId = flowId; + } + + public StreamParameters() { + this.cursor = Optional.empty(); + this.batchLimit = Optional.empty(); + this.streamLimit = Optional.empty(); + this.batchFlushTimeout = Optional.empty(); + this.streamTimeout = Optional.empty(); + this.streamKeepAliveLimit = Optional.empty(); + this.flowId = Optional.empty(); + } + + public Optional getCursor() { + return cursor; + } + + public Optional getBatchLimit() { + return batchLimit; + } + + public Optional getStreamLimit() { + return streamLimit; + } + + public Optional getBatchFlushTimeout() { + return batchFlushTimeout; + } - public StreamParameters(Integer partition, Integer offset) { - this.partition = partition; - this.offset = offset; + public Optional getStreamTimeout() { + return streamTimeout; } - public Integer getPartition() { - return partition; + public Optional getStreamKeepAliveLimit() { + return streamKeepAliveLimit; } - public Integer getOffset() { - return offset; + public Optional getFlowId() { + return flowId; } } diff --git a/api/src/main/java/org/zalando/nakadi/client/java/model/Cursor.java b/api/src/main/java/org/zalando/nakadi/client/java/model/Cursor.java index 92dd2f1..c9656e6 100644 --- a/api/src/main/java/org/zalando/nakadi/client/java/model/Cursor.java +++ b/api/src/main/java/org/zalando/nakadi/client/java/model/Cursor.java @@ -11,6 +11,8 @@ public class Cursor { * Offset of the event being pointed to. */ public Cursor(Integer partition, Integer offset) { + + this.partition = partition; this.offset = offset; } diff --git a/api/src/main/scala/org/zalando/nakadi/client/scala/Client.scala b/api/src/main/scala/org/zalando/nakadi/client/scala/Client.scala index 0807722..82d0bca 100644 --- a/api/src/main/scala/org/zalando/nakadi/client/scala/Client.scala +++ b/api/src/main/scala/org/zalando/nakadi/client/scala/Client.scala @@ -112,7 +112,7 @@ trait Client { def publishEvent[T <: Event](eventTypeName: String, event: T): Future[Option[ClientError]] /** - * List the partitions to the given EventType. + * List the partitions tola the given EventType. * {{{ * curl --request GET /event-types/{name}/partitions * }}} diff --git a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala index bcf3a04..812b300 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala @@ -16,7 +16,7 @@ class EventCounterListener(val id: String) extends Listener[MeetingsEvent] { private var eventCount: AtomicLong = new AtomicLong(0); def onError(sourceUrl: String, cursor: Cursor, error: ClientError): Unit = { - println("An error occurred") + println("Error %s %s %s".format(sourceUrl,cursor,error)) } def onReceive(sourceUrl: String, cursor: Cursor, events: Seq[MeetingsEvent]): Unit = { From f3d42a88d0a88800d22c2e47dad25e2dc1c585ff Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Wed, 4 May 2016 10:27:31 +0200 Subject: [PATCH 044/183] techjira:LAAS-60 Java Client uses wrong streamParameters from Scala! --- api/src/main/java/org/zalando/nakadi/client/java/Client.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/src/main/java/org/zalando/nakadi/client/java/Client.java b/api/src/main/java/org/zalando/nakadi/client/java/Client.java index 6c1097a..10d6d62 100644 --- a/api/src/main/java/org/zalando/nakadi/client/java/Client.java +++ b/api/src/main/java/org/zalando/nakadi/client/java/Client.java @@ -13,7 +13,7 @@ import org.zalando.nakadi.client.java.model.EventType; import org.zalando.nakadi.client.java.model.Metrics; import org.zalando.nakadi.client.java.model.Partition; -import org.zalando.nakadi.client.scala.StreamParameters; +import org.zalando.nakadi.client.java.StreamParameters; public interface Client { From edcc7dc20f6536c6eca681059741dfb4de237950 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Wed, 4 May 2016 10:29:38 +0200 Subject: [PATCH 045/183] techjira:LAAS-60 Java Client uses wrong streamParameters from Scala! --- .../main/java/org/zalando/nakadi/client/java/ClientImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java b/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java index 21c3286..077f650 100644 --- a/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java +++ b/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java @@ -144,7 +144,7 @@ public Future stop() { @Override public Future subscribe(String eventTypeName, - org.zalando.nakadi.client.scala.StreamParameters parameters, + StreamParameters parameters, Listener listener, Deserializer deserializer) { // TODO Auto-generated method stub return null; From e98fedab4ba6cc4e964d42b8dac7ed029fa0364e Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Wed, 4 May 2016 11:07:30 +0200 Subject: [PATCH 046/183] techjira:LAAS-60 Java Client uses wrong models! --- .../nakadi/client/java/ClientImpl.java | 2 +- .../client/java/utils/SerializationUtils.java | 53 +++++++++++++++++++ .../nakadi/client/scala/Connection.scala | 13 +++-- .../nakadi/client/scala/ClientFactory.scala | 4 +- 4 files changed, 64 insertions(+), 8 deletions(-) create mode 100644 client/src/main/java/org/zalando/nakadi/client/java/utils/SerializationUtils.java diff --git a/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java b/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java index 077f650..3999ffb 100644 --- a/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java +++ b/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java @@ -147,7 +147,7 @@ public Future subscribe(String eventTypeName, StreamParameters parameters, Listener listener, Deserializer deserializer) { // TODO Auto-generated method stub - return null; + return null;//connection.subscribeJava(eventTypeName, request, listener, des);; } @Override diff --git a/client/src/main/java/org/zalando/nakadi/client/java/utils/SerializationUtils.java b/client/src/main/java/org/zalando/nakadi/client/java/utils/SerializationUtils.java new file mode 100644 index 0000000..3d5ca4d --- /dev/null +++ b/client/src/main/java/org/zalando/nakadi/client/java/utils/SerializationUtils.java @@ -0,0 +1,53 @@ +package org.zalando.nakadi.client.java.utils; + +import java.util.List; + +import org.zalando.nakadi.client.Deserializer; +import org.zalando.nakadi.client.Serializer; +import org.zalando.nakadi.client.java.enumerator.*; +import org.zalando.nakadi.client.java.model.*; + +public class SerializationUtils { + + public Serializer defaultSerializer() { + + return JavaJacksonJsonMarshaller.serializer(); + + } + + public Deserializer metricsDeserializer() { + return JavaJacksonJsonMarshaller.deserializer(JavaJacksonJsonMarshaller.metricsTR()); + } + + public Deserializer eventTypeDeserializer() { + return JavaJacksonJsonMarshaller.deserializer(JavaJacksonJsonMarshaller.eventTypeTR()); + } + + // public Deserializer customDeserializer(TypeReference[T] in) { + // return JavaJacksonJsonMarshaller.deserializer(tr); + // } + + public Deserializer partitionDeserializer() { + return JavaJacksonJsonMarshaller.deserializer(JavaJacksonJsonMarshaller.partitionTR()); + } + + public Deserializer> seqOfEventTypeDeserializer() { + return JavaJacksonJsonMarshaller.deserializer(JavaJacksonJsonMarshaller.listOfEventTypeTR()); + } + + public Deserializer> seqOfPartitionDeserializer() { + return JavaJacksonJsonMarshaller.deserializer(JavaJacksonJsonMarshaller.listOfPartitionTR()); + } + + public Deserializer> seqOfEventValidationStrategy() { + return JavaJacksonJsonMarshaller.deserializer(JavaJacksonJsonMarshaller.listOfEventValidationStrategyTR()); + } + + public Deserializer> seqOfEventEnrichmentStrategy() { + return JavaJacksonJsonMarshaller.deserializer(JavaJacksonJsonMarshaller.listOfEventEnrichmentStrategyTR()); + } + + public Deserializer> seqOfPartitionStrategy() { + return JavaJacksonJsonMarshaller.deserializer(JavaJacksonJsonMarshaller.listOfPartitionStrategyTR()); + } +} diff --git a/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala index a3c82ea..6ccd8f1 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala @@ -57,7 +57,9 @@ trait Connection extends HttpFactory { def post4Java[T](endpoint: String, model: T)(implicit serializer: Serializer[T]): java.util.concurrent.Future[Void] def put[T](endpoint: String, model: T)(implicit serializer: Serializer[T]): Future[HttpResponse] def subscribe[T <: Event](url: String, cursor: Option[Cursor], listener: Listener[T])(implicit des: Deserializer[EventStreamBatch[T]]) - def subscribeJava[T <: org.zalando.nakadi.client.java.model.Event](url: String, request: HttpRequest, listener: org.zalando.nakadi.client.java.Listener[T])(implicit des: Deserializer[T]) + def subscribeJava[T <: org.zalando.nakadi.client.java.model.Event]( + url: String, cursor: Option[org.zalando.nakadi.client.java.model.Cursor], + listener: org.zalando.nakadi.client.java.Listener[T])(implicit des: Deserializer[org.zalando.nakadi.client.java.model.EventStreamBatch[T]]) def stop(): Future[Terminated] def materializer(): ActorMaterializer @@ -236,6 +238,7 @@ sealed class ConnectionImpl(val host: String, val port: Int, val tokenProvider: eventReceiver ! NextEvent(cursor) } + private def requestCreator(url: String): Flow[Option[Cursor], HttpRequest, NotUsed] = Flow[Option[Cursor]].map(withHttpRequest(url, _, None, tokenProvider)) @@ -247,9 +250,9 @@ sealed class ConnectionImpl(val host: String, val port: Int, val tokenProvider: private def delimiterFlow = Flow[ByteString] .via(Framing.delimiter(ByteString(EVENT_DELIMITER), maximumFrameLength = RECEIVE_BUFFER_SIZE, allowTruncation = true)) - def subscribeJava[T <: org.zalando.nakadi.client.java.model.Event](url: String, request: HttpRequest, listener: org.zalando.nakadi.client.java.Listener[T])(implicit des: Deserializer[T]) = ??? - - - + def subscribeJava[T <: org.zalando.nakadi.client.java.model.Event]( + url: String, cursor: Option[org.zalando.nakadi.client.java.model.Cursor], + listener: org.zalando.nakadi.client.java.Listener[T])(implicit des: Deserializer[org.zalando.nakadi.client.java.model.EventStreamBatch[T]]) = ??? + } diff --git a/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala b/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala index 883c271..f93570e 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala @@ -2,8 +2,8 @@ package org.zalando.nakadi.client.scala object ClientFactory { import sys.process._ import scala.language.postfixOps - def host():String = "nakadi-sandbox.my-test.fernando.do" - def OAuth2Token(): () => String = () => "" + def host():String = "nakadi-sandbox.aruha-test.zalan.do" + def OAuth2Token(): () => String = () => "9eb412e6-3ffc-4ccd-b167-33af553a4c80" def getToken():String = OAuth2Token().apply() def port():Integer = 443 def connection():Connection = Connection.newConnection(host, port, OAuth2Token(), true, false) From 4e09d3994ed8b12633b8c446f1fc9efc56109dda Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Wed, 4 May 2016 22:42:04 +0200 Subject: [PATCH 047/183] techjira:LAAS-60 Java Event Listener is working! --- .../zalando/nakadi/client/java/Client.java | 15 +- .../nakadi/client/java/ClientError.java | 8 +- .../zalando/nakadi/client/java/Listener.java | 3 +- .../nakadi/client/java/model/Cursor.java | 18 +- .../client/java/model/EventStreamBatch.java | 13 +- .../nakadi/client/scala/Listener.scala | 6 +- .../nakadi/client/scala/model/Model.scala | 4 +- .../nakadi/client/java/ClientImpl.java | 258 ++++++++---------- .../client/java/utils/SerializationUtils.java | 43 ++- .../client/actor/EventConsumingActor.scala | 44 ++- .../client/actor/EventReceivingActor.scala | 1 + .../nakadi/client/scala/Connection.scala | 100 ++++--- .../nakadi/client/scala/HttpFactory.scala | 11 +- .../nakadi/client/scala/MessageHandler.scala | 111 ++++++++ .../client/utils/FutureConversions.scala | 3 + .../nakadi/client/utils/ImplicitHelper.scala | 31 --- .../nakadi/client/utils/ModelConverter.scala | 79 ++++++ .../nakadi/client/utils/TestScalaEntity.scala | 2 +- .../examples/java/EventCounterListener.java | 38 +++ .../examples/java/EventCreationExample.java | 9 +- .../examples/java/EventListenerExample.java | 97 ++----- .../client/examples/java/MeetingsEvent.java | 26 ++ .../examples/scala/EventListenerExample.scala | 10 +- .../nakadi/client/scala/ClientFactory.scala | 2 +- .../nakadi/client/subscription/LeClient.scala | 26 -- .../client/subscription/MyEventExample.scala | 5 - .../client/subscription/MyListener.scala | 16 -- .../client/subscription/Subscriber.scala | 63 ----- .../client/ClientSubscriptionTest.scala | 2 +- .../client/done/EventConsumerActor.scala | 27 -- .../client/done/EventPublishingActor.scala | 18 -- .../nakadi/client/example2/Ecample3.scala | 96 ------- .../client/example2/HttpClientExample.scala | 96 ------- project/Build.scala | 2 +- 34 files changed, 590 insertions(+), 693 deletions(-) create mode 100644 client/src/main/scala/org/zalando/nakadi/client/scala/MessageHandler.scala delete mode 100644 client/src/main/scala/org/zalando/nakadi/client/utils/ImplicitHelper.scala create mode 100644 client/src/main/scala/org/zalando/nakadi/client/utils/ModelConverter.scala create mode 100644 it/src/main/java/org/zalando/nakadi/client/examples/java/EventCounterListener.java create mode 100644 it/src/main/java/org/zalando/nakadi/client/examples/java/MeetingsEvent.java delete mode 100644 it/src/main/scala/org/zalando/nakadi/client/subscription/LeClient.scala delete mode 100644 it/src/main/scala/org/zalando/nakadi/client/subscription/MyEventExample.scala delete mode 100644 it/src/main/scala/org/zalando/nakadi/client/subscription/MyListener.scala delete mode 100644 it/src/main/scala/org/zalando/nakadi/client/subscription/Subscriber.scala delete mode 100644 it/src/test/scala/org/zalando/nakadi/client/done/EventConsumerActor.scala delete mode 100644 it/src/test/scala/org/zalando/nakadi/client/done/EventPublishingActor.scala delete mode 100644 it/src/test/scala/org/zalando/nakadi/client/example2/Ecample3.scala delete mode 100644 it/src/test/scala/org/zalando/nakadi/client/example2/HttpClientExample.scala diff --git a/api/src/main/java/org/zalando/nakadi/client/java/Client.java b/api/src/main/java/org/zalando/nakadi/client/java/Client.java index 10d6d62..3a9a9e4 100644 --- a/api/src/main/java/org/zalando/nakadi/client/java/Client.java +++ b/api/src/main/java/org/zalando/nakadi/client/java/Client.java @@ -9,11 +9,14 @@ import org.zalando.nakadi.client.java.enumerator.EventEnrichmentStrategy; import org.zalando.nakadi.client.java.enumerator.EventValidationStrategy; import org.zalando.nakadi.client.java.enumerator.PartitionStrategy; +import org.zalando.nakadi.client.java.model.Cursor; import org.zalando.nakadi.client.java.model.Event; +import org.zalando.nakadi.client.java.model.EventStreamBatch; import org.zalando.nakadi.client.java.model.EventType; import org.zalando.nakadi.client.java.model.Metrics; import org.zalando.nakadi.client.java.model.Partition; -import org.zalando.nakadi.client.java.StreamParameters; + +import com.fasterxml.jackson.core.type.TypeReference; public interface Client { @@ -141,7 +144,15 @@ public interface Client { * @param listener Listener to pass the event to when it is received. * @return Void in case of success */ - public Future subscribe(String eventTypeName, StreamParameters parameters, Listener listener, Deserializer deserializer); + Future subscribe(String eventTypeName, Optional cursor, Listener listener, Deserializer> deserializer); + /** + * Registers the subscription of a listener to start streaming events from a partition in non-blocking fashion. + * @param eventTypeName The unique name (id) of the EventType target + * @param parameters Parameters for customizing the details of the streaming. + * @param typeRef TypeReference for unmarshalling with the Jackson ObjectMapper. + * @return Void in case of success + */ + Future subscribe(String eventTypeName, Optional cursor, Listener listener, TypeReference> typeRef); /** * Removes the subscription of a listener, to stop streaming events from a partition. * @param eventTypeName The unique name (id) of the EventType target diff --git a/api/src/main/java/org/zalando/nakadi/client/java/ClientError.java b/api/src/main/java/org/zalando/nakadi/client/java/ClientError.java index 9444c93..dd7c920 100644 --- a/api/src/main/java/org/zalando/nakadi/client/java/ClientError.java +++ b/api/src/main/java/org/zalando/nakadi/client/java/ClientError.java @@ -1,10 +1,12 @@ package org.zalando.nakadi.client.java; +import java.util.Optional; + public class ClientError { private final String msg; - private final Integer status; + private final Optional status; - public ClientError(String msg, Integer status) { + public ClientError(String msg, Optional status) { this.msg = msg; this.status = status; } @@ -13,7 +15,7 @@ public String getMsg() { return msg; } - public Integer getStatus() { + public Optional getStatus() { return status; } diff --git a/api/src/main/java/org/zalando/nakadi/client/java/Listener.java b/api/src/main/java/org/zalando/nakadi/client/java/Listener.java index 9d67619..873d58b 100644 --- a/api/src/main/java/org/zalando/nakadi/client/java/Listener.java +++ b/api/src/main/java/org/zalando/nakadi/client/java/Listener.java @@ -1,6 +1,7 @@ package org.zalando.nakadi.client.java; import java.util.List; +import java.util.Optional; import org.zalando.nakadi.client.java.model.Cursor; import org.zalando.nakadi.client.java.model.Event; @@ -10,5 +11,5 @@ public interface Listener { void onReceive(String eventUrl, Cursor cursor, List events); - void onError(String eventUrl, Cursor cursor, ClientError error); + void onError(String eventUrl, Optional error); } diff --git a/api/src/main/java/org/zalando/nakadi/client/java/model/Cursor.java b/api/src/main/java/org/zalando/nakadi/client/java/model/Cursor.java index c9656e6..5f530a8 100644 --- a/api/src/main/java/org/zalando/nakadi/client/java/model/Cursor.java +++ b/api/src/main/java/org/zalando/nakadi/client/java/model/Cursor.java @@ -1,8 +1,11 @@ package org.zalando.nakadi.client.java.model; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + public class Cursor { - private final Integer partition; - private final Integer offset; + private final String partition; + private final String offset; /** * @param partition @@ -10,18 +13,23 @@ public class Cursor { * @param offset * Offset of the event being pointed to. */ - public Cursor(Integer partition, Integer offset) { + @JsonCreator + public Cursor( + @JsonProperty("partition") + String partition, + @JsonProperty("offset") + String offset) { this.partition = partition; this.offset = offset; } - public Integer getPartition() { + public String getPartition() { return partition; } - public Integer getOffset() { + public String getOffset() { return offset; } diff --git a/api/src/main/java/org/zalando/nakadi/client/java/model/EventStreamBatch.java b/api/src/main/java/org/zalando/nakadi/client/java/model/EventStreamBatch.java index caecdf7..e0553be 100644 --- a/api/src/main/java/org/zalando/nakadi/client/java/model/EventStreamBatch.java +++ b/api/src/main/java/org/zalando/nakadi/client/java/model/EventStreamBatch.java @@ -2,6 +2,9 @@ import java.util.List; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + /** * One chunk of events in a stream. A batch consists of an array of `Event`s * plus a `Cursor` pointing to the offset of the last Event in the stream. The @@ -10,6 +13,7 @@ * /event-type/{name}/events`) the events array will be omitted. Sequential * batches might repeat the cursor if no new events arrive. */ + public class EventStreamBatch { private final Cursor cursor; private final List events; @@ -37,7 +41,14 @@ public class EventStreamBatch { * below. */ - public EventStreamBatch(Cursor cursor, List events) { + @JsonCreator + public EventStreamBatch( + @JsonProperty("cursor") + Cursor cursor, + + @JsonProperty("events") + List events + ) { super(); this.cursor = cursor; this.events = events; diff --git a/api/src/main/scala/org/zalando/nakadi/client/scala/Listener.scala b/api/src/main/scala/org/zalando/nakadi/client/scala/Listener.scala index cfd67e4..2e19902 100644 --- a/api/src/main/scala/org/zalando/nakadi/client/scala/Listener.scala +++ b/api/src/main/scala/org/zalando/nakadi/client/scala/Listener.scala @@ -6,8 +6,8 @@ import org.zalando.nakadi.client.scala.model.Event -trait Listener[T <: Event] { +trait Listener[T] { def id: String - def onReceive(sourceUrl: String, cursor: Cursor, event: Seq[T]): Unit - def onError(sourceUrl: String, cursor: Cursor, error: ClientError): Unit + def onReceive(eventUrl: String, cursor: Cursor, events: Seq[T]): Unit + def onError(eventUrl: String, error: Option[ClientError]): Unit } \ No newline at end of file diff --git a/api/src/main/scala/org/zalando/nakadi/client/scala/model/Model.scala b/api/src/main/scala/org/zalando/nakadi/client/scala/model/Model.scala index 979139b..a052595 100644 --- a/api/src/main/scala/org/zalando/nakadi/client/scala/model/Model.scala +++ b/api/src/main/scala/org/zalando/nakadi/client/scala/model/Model.scala @@ -33,8 +33,8 @@ trait Event { * @param offset Offset of the event being pointed to. */ case class Cursor( - partition: Integer, - offset: Integer) + partition: String, + offset: String) /** * diff --git a/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java b/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java index 3999ffb..f77507a 100644 --- a/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java +++ b/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java @@ -10,151 +10,133 @@ import org.zalando.nakadi.client.java.enumerator.EventEnrichmentStrategy; import org.zalando.nakadi.client.java.enumerator.EventValidationStrategy; import org.zalando.nakadi.client.java.enumerator.PartitionStrategy; +import org.zalando.nakadi.client.java.model.Cursor; import org.zalando.nakadi.client.java.model.Event; +import org.zalando.nakadi.client.java.model.EventStreamBatch; import org.zalando.nakadi.client.java.model.EventType; import org.zalando.nakadi.client.java.model.Metrics; import org.zalando.nakadi.client.java.model.Partition; +import org.zalando.nakadi.client.java.utils.SerializationUtils; import org.zalando.nakadi.client.scala.Connection; -import org.zalando.nakadi.client.utils.Serialization; import org.zalando.nakadi.client.utils.Uri; +import com.fasterxml.jackson.core.type.TypeReference; + public class ClientImpl implements Client { - private final Connection connection; - - // Deserializers - private final Deserializer metricsDeserializer = Serialization - .metricsDeserializer(); - private final Deserializer partitionDeserializer = Serialization - .partitionDeserializer(); - // List Deserializers - private final Deserializer> seqOfEventTypeDeserializer = Serialization - .seqOfEventTypeDeserializer(); - private final Deserializer> seqOfPartitionDeserializer = Serialization - .seqOfPartitionDeserializer(); - private final Deserializer> seqOfEventValidationStrategy = Serialization - .seqOfEventValidationStrategy(); - private final Deserializer> seqOfEventEnrichmentStrategy = Serialization - .seqOfEventEnrichmentStrategy(); - private final Deserializer> seqOfPartitionStrategy = Serialization - .seqOfPartitionStrategy(); - // Serializers - private final Serializer eventTypeSerializer = Serialization - .defaultSerializer(); - private final Deserializer eventTypeDeserializer = Serialization - .eventTypeDeserializer(); - - public ClientImpl(Connection connection) { - this.connection = connection; - } - - @Override - public Future> getMetrics() { - - return connection.get4Java(Uri.URI_METRICS(), metricsDeserializer); - } - - @Override - public Future>> getEventTypes() { - - return connection.get4Java(Uri.URI_EVENT_TYPES(), - seqOfEventTypeDeserializer); - } - - @Override - public Future createEventType(EventType eventType) { - return connection.post4Java(Uri.URI_EVENT_TYPES(), eventType, - eventTypeSerializer); - } - - @Override - public Future> getEventType(String eventTypeName) { - // TODO Auto-generated method stub - return null; - } - - @Override - public Future updateEventType(String eventTypeName, - EventType eventType) { - // TODO Auto-generated method stub - return null; - } - - @Override - public Future deleteEventType(String eventTypeName) { - // TODO Auto-generated method stub - return null; - } - - @Override - public Future publishEvent(String eventTypeName, - T event, Serializer> serializer) { - return publishEvents(eventTypeName, Arrays.asList(event), serializer); - } - - @Override - public Future publishEvent(String eventTypeName, - T event) { - return publishEvents(eventTypeName, Arrays.asList(event)); - } - - @Override - public Future publishEvents(String eventTypeName, - List events, Serializer> serializer) { - return connection.post4Java(Uri.getEventStreamingUri(eventTypeName), - events, serializer); - } - - @Override - public Future publishEvents(String eventTypeName, - List events) { - return publishEvents(eventTypeName, events, - Serialization.defaultSerializer()); - } - - @Override - public Future>> getPartitions(String eventTypeName) { - // TODO Auto-generated method stub - return null; - } - - @Override - public Future>> getValidationStrategies() { - // TODO Auto-generated method stub - return null; - } - - @Override - public Future>> getEnrichmentStrategies() { - // TODO Auto-generated method stub - return null; - } - - @Override - public Future>> getPartitioningStrategies() { - - return connection.get4Java(Uri.URI_PARTITIONING_STRATEGIES(), - seqOfPartitionStrategy); - } - - @Override - public Future stop() { - // TODO Auto-generated method stub - return null; - } - - @Override - public Future subscribe(String eventTypeName, - StreamParameters parameters, - Listener listener, Deserializer deserializer) { - // TODO Auto-generated method stub - return null;//connection.subscribeJava(eventTypeName, request, listener, des);; - } - - @Override - public Future unsubscribe(String eventTypeName, - Listener listener) { - // TODO Auto-generated method stub - return null; - } + private final Connection connection; + + // Deserializers + private final Deserializer metricsDeserializer = SerializationUtils.metricsDeserializer(); + private final Deserializer partitionDeserializer = SerializationUtils.partitionDeserializer(); + // List Deserializers + private final Deserializer> seqOfEventTypeDeserializer = SerializationUtils.seqOfEventTypeDeserializer(); + private final Deserializer> seqOfPartitionDeserializer = SerializationUtils.seqOfPartitionDeserializer(); + private final Deserializer> seqOfEventValidationStrategy = SerializationUtils + .seqOfEventValidationStrategy(); + private final Deserializer> seqOfEventEnrichmentStrategy = SerializationUtils + .seqOfEventEnrichmentStrategy(); + private final Deserializer> seqOfPartitionStrategy = SerializationUtils.seqOfPartitionStrategy(); + // Serializers + private final Serializer eventTypeSerializer = SerializationUtils.defaultSerializer(); + private final Deserializer eventTypeDeserializer = SerializationUtils.eventTypeDeserializer(); + + public ClientImpl(Connection connection) { + this.connection = connection; + } + + @Override + public Future> getMetrics() { + + return connection.get4Java(Uri.URI_METRICS(), metricsDeserializer); + } + + @Override + public Future>> getEventTypes() { + + return connection.get4Java(Uri.URI_EVENT_TYPES(), seqOfEventTypeDeserializer); + } + + @Override + public Future createEventType(EventType eventType) { + return connection.post4Java(Uri.URI_EVENT_TYPES(), eventType, eventTypeSerializer); + } + + @Override + public Future> getEventType(String eventTypeName) { + return null; + } + + @Override + public Future updateEventType(String eventTypeName, EventType eventType) { + return null; + } + + @Override + public Future deleteEventType(String eventTypeName) { + return null; + } + + @Override + public Future publishEvent(String eventTypeName, T event, Serializer> serializer) { + return publishEvents(eventTypeName, Arrays.asList(event), serializer); + } + + @Override + public Future publishEvent(String eventTypeName, T event) { + return publishEvents(eventTypeName, Arrays.asList(event)); + } + + @Override + public Future publishEvents(String eventTypeName, List events, Serializer> serializer) { + return connection.post4Java(Uri.getEventStreamingUri(eventTypeName), events, serializer); + } + + @Override + public Future publishEvents(String eventTypeName, List events) { + return publishEvents(eventTypeName, events, SerializationUtils.defaultSerializer()); + } + + @Override + public Future>> getPartitions(String eventTypeName) { + return null; + } + + @Override + public Future>> getValidationStrategies() { + return null; + } + + @Override + public Future>> getEnrichmentStrategies() { + return null; + } + + @Override + public Future>> getPartitioningStrategies() { + + return connection.get4Java(Uri.URI_PARTITIONING_STRATEGIES(), seqOfPartitionStrategy); + } + + @Override + public Future stop() { + return null; + } + + @Override + public Future subscribe(String eventTypeName, Optional cursor, Listener listener, + Deserializer> deserializer) { + return connection.subscribeJava(Uri.getEventStreamingUri(eventTypeName), cursor, listener, deserializer); + } + + @Override + public Future subscribe(String eventTypeName, Optional cursor, Listener listener, + TypeReference> typeRef) { + return connection.subscribeJava(Uri.getEventStreamingUri(eventTypeName), cursor, listener, SerializationUtils.withCustomDeserializer(typeRef)); + } + + @Override + public Future unsubscribe(String eventTypeName, Listener listener) { + return null; + } } \ No newline at end of file diff --git a/client/src/main/java/org/zalando/nakadi/client/java/utils/SerializationUtils.java b/client/src/main/java/org/zalando/nakadi/client/java/utils/SerializationUtils.java index 3d5ca4d..8d32370 100644 --- a/client/src/main/java/org/zalando/nakadi/client/java/utils/SerializationUtils.java +++ b/client/src/main/java/org/zalando/nakadi/client/java/utils/SerializationUtils.java @@ -4,50 +4,63 @@ import org.zalando.nakadi.client.Deserializer; import org.zalando.nakadi.client.Serializer; -import org.zalando.nakadi.client.java.enumerator.*; -import org.zalando.nakadi.client.java.model.*; +import org.zalando.nakadi.client.java.enumerator.EventEnrichmentStrategy; +import org.zalando.nakadi.client.java.enumerator.EventValidationStrategy; +import org.zalando.nakadi.client.java.enumerator.PartitionStrategy; +import org.zalando.nakadi.client.java.model.EventType; +import org.zalando.nakadi.client.java.model.JavaJacksonJsonMarshaller; +import org.zalando.nakadi.client.java.model.Metrics; +import org.zalando.nakadi.client.java.model.Partition; + +import com.fasterxml.jackson.core.type.TypeReference; public class SerializationUtils { - public Serializer defaultSerializer() { + public static Serializer defaultSerializer() { return JavaJacksonJsonMarshaller.serializer(); } + + public static Serializer> defaultListSerializer() { + + return JavaJacksonJsonMarshaller.serializer(); + + } + + public static Deserializer withCustomDeserializer(TypeReference in){ + return JavaJacksonJsonMarshaller.deserializer(in); + } - public Deserializer metricsDeserializer() { + public static Deserializer metricsDeserializer() { return JavaJacksonJsonMarshaller.deserializer(JavaJacksonJsonMarshaller.metricsTR()); } - public Deserializer eventTypeDeserializer() { + public static Deserializer eventTypeDeserializer() { return JavaJacksonJsonMarshaller.deserializer(JavaJacksonJsonMarshaller.eventTypeTR()); } - // public Deserializer customDeserializer(TypeReference[T] in) { - // return JavaJacksonJsonMarshaller.deserializer(tr); - // } - - public Deserializer partitionDeserializer() { + public static Deserializer partitionDeserializer() { return JavaJacksonJsonMarshaller.deserializer(JavaJacksonJsonMarshaller.partitionTR()); } - public Deserializer> seqOfEventTypeDeserializer() { + public static Deserializer> seqOfEventTypeDeserializer() { return JavaJacksonJsonMarshaller.deserializer(JavaJacksonJsonMarshaller.listOfEventTypeTR()); } - public Deserializer> seqOfPartitionDeserializer() { + public static Deserializer> seqOfPartitionDeserializer() { return JavaJacksonJsonMarshaller.deserializer(JavaJacksonJsonMarshaller.listOfPartitionTR()); } - public Deserializer> seqOfEventValidationStrategy() { + public static Deserializer> seqOfEventValidationStrategy() { return JavaJacksonJsonMarshaller.deserializer(JavaJacksonJsonMarshaller.listOfEventValidationStrategyTR()); } - public Deserializer> seqOfEventEnrichmentStrategy() { + public static Deserializer> seqOfEventEnrichmentStrategy() { return JavaJacksonJsonMarshaller.deserializer(JavaJacksonJsonMarshaller.listOfEventEnrichmentStrategyTR()); } - public Deserializer> seqOfPartitionStrategy() { + public static Deserializer> seqOfPartitionStrategy() { return JavaJacksonJsonMarshaller.deserializer(JavaJacksonJsonMarshaller.listOfPartitionStrategyTR()); } } diff --git a/client/src/main/scala/org/zalando/nakadi/client/actor/EventConsumingActor.scala b/client/src/main/scala/org/zalando/nakadi/client/actor/EventConsumingActor.scala index afc49cc..18c557b 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/actor/EventConsumingActor.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/actor/EventConsumingActor.scala @@ -3,14 +3,12 @@ package org.zalando.nakadi.client.actor import scala.util.Failure import scala.util.Success import scala.util.Try - import org.zalando.nakadi.client.Deserializer import org.zalando.nakadi.client.scala.ClientError import org.zalando.nakadi.client.scala.Listener import org.zalando.nakadi.client.scala.model.Cursor import org.zalando.nakadi.client.scala.model.Event import org.zalando.nakadi.client.scala.model.EventStreamBatch - import EventReceivingActor.NextEvent import akka.actor.Actor import akka.actor.ActorLogging @@ -22,7 +20,12 @@ import akka.stream.actor.ActorSubscriberMessage.OnError import akka.stream.actor.ActorSubscriberMessage.OnNext import akka.stream.actor._ import akka.util.ByteString - +import org.zalando.nakadi.client.utils.ModelConverter +import org.zalando.nakadi.client.scala.EventHandler +import org.zalando.nakadi.client.scala.ScalaResult +import org.zalando.nakadi.client.scala.JavaResult +import org.zalando.nakadi.client.scala.ErrorResult +import org.zalando.nakadi.client.java.model.{ Event => JEvent } /** * This actor serves as Sink for the pipeline.
      * 1. It receives the message and the cursor from the payload. @@ -32,12 +35,11 @@ import akka.util.ByteString * */ -class EventConsumer[T <: Event](url: String, // - listener: Listener[T], // - receivingActor: ActorRef, // - des: Deserializer[EventStreamBatch[T]]) +class EventConsumingActor[J <: JEvent, S <: Event](url: String, + receivingActor: ActorRef, // + handler: EventHandler[J, S]) extends Actor with ActorLogging with ActorSubscriber { - + import ModelConverter._ var currCursor: Cursor = null var prevCursor: Cursor = null @@ -50,37 +52,33 @@ class EventConsumer[T <: Event](url: String, // override def receive: Receive = { case OnNext(msg: ByteString) => val message = msg.utf8String - log.debug("[EventConsumer] Event for lstnr {} and url {} with msg {}", listener.id, url, message) + log.debug("[EventConsumer] Event - url {} - msg {}", url, message) handleMsg(message) case OnError(err: Throwable) => - log.error(err, "[EventConsumer] onError [preCursor {} currCursor {} listner {} url {}]", prevCursor, currCursor, listener.id, url) + log.error(err, "[EventConsumer] onError [preCursor {} currCursor {} url {}]", prevCursor, currCursor, url) context.stop(self) case OnComplete => - log.info("[EventConsumer] onComplete [preCursor {} currCursor {} listner {} url {}]", prevCursor, currCursor, listener.id, url) + log.info("[EventConsumer] onComplete [preCursor {} currCursor {} url {}]", prevCursor, currCursor, url) } def request4NextEvent(cursor: Cursor) = { prevCursor = currCursor currCursor = cursor - log.debug("[EventConsumer] NextEvent [preCursor {} currCursor {} listner {} url {}]", prevCursor, currCursor, listener.id, url) + log.debug("[EventConsumer] NextEvent [preCursor {} currCursor {} url {}]", prevCursor, currCursor, url) receivingActor ! NextEvent(Option(cursor)) } def handleMsg(message: String) = { - Try(deserializeMsg(message, des)) match { - case Success(EventStreamBatch(cursor, Some(events))) => - listener.onReceive(url, cursor, events) - request4NextEvent(cursor) - case Success(EventStreamBatch(cursor, None)) => - log.debug("[EventConsumer] Keep alive msg at {}", cursor) + + handler.handle(url, message) match { + case Right(Some(cursor)) => request4NextEvent(cursor) - case Failure(error) => - val errorMsg = "[EventConsumer] DeserializationError [preCursor %s currCursor %s listner %s url %s error %s]".format(prevCursor, currCursor, listener.id, url, error.getMessage) - log.error(error, message) - listener.onError(url, null, ClientError("Failed to Deserialize with an error: " + errorMsg, None)) + case Right(None) => + log.error("Message lacks of a cursor [{}]", message) + case Left(ErrorResult(error)) => + log.error("Handler could not handle message {}", error.getMessage) } } - def deserializeMsg(msg: String, des: Deserializer[EventStreamBatch[T]]): EventStreamBatch[T] = des.from(msg) } diff --git a/client/src/main/scala/org/zalando/nakadi/client/actor/EventReceivingActor.scala b/client/src/main/scala/org/zalando/nakadi/client/actor/EventReceivingActor.scala index b3496d5..dd43611 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/actor/EventReceivingActor.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/actor/EventReceivingActor.scala @@ -39,6 +39,7 @@ class EventReceivingActor(url: String) extends Actor with ActorLogging with Acto case Request(cnt) => log.debug("[EventReceivingActor] Requested {} event chunks", cnt) sendEvents() + Thread.sleep(100000) case Cancel => log.debug("[EventReceivingActor] Stopping the stream of events!") context.stop(self) diff --git a/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala index 6ccd8f1..004779e 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala @@ -2,45 +2,59 @@ package org.zalando.nakadi.client.scala import java.security.SecureRandom import java.security.cert.X509Certificate +import java.util.Optional import scala.collection.immutable.Seq import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.Future -import scala.concurrent.duration.DurationInt +import scala.util.Failure +import scala.util.Success +import scala.util.Try import org.slf4j.LoggerFactory -import org.zalando.nakadi.client.actor.EventConsumer -import com.typesafe.scalalogging.Logger -import akka.actor.{ Props, ActorSystem, ActorLogging, _ } -import akka.http.scaladsl.{ Http, HttpsConnectionContext } -import akka.http.scaladsl.model.{ HttpHeader, _ } -import akka.stream.{ ActorMaterializer, OverflowStrategy } -import akka.stream.actor.{ ActorSubscriber, RequestStrategy } -import akka.stream.scaladsl.{ Flow, Sink, Source } -import akka.util.ByteString -import javax.net.ssl.{ SSLContext, TrustManager, X509TrustManager } import org.zalando.nakadi.client.Deserializer import org.zalando.nakadi.client.Serializer -import org.zalando.nakadi.client.scala.model.Event -import akka.http.scaladsl.unmarshalling.Unmarshal -import scala.util.Try -import scala.util.Failure -import scala.util.Success -import akka.http.scaladsl.model.{ HttpHeader, HttpMethod, HttpMethods, HttpResponse, MediaRange } -import java.util.Optional -import org.zalando.nakadi.client.utils.FutureConversions -import org.zalando.nakadi.client.scala.model.Cursor +import org.zalando.nakadi.client.actor.EventConsumingActor import org.zalando.nakadi.client.actor.EventReceivingActor -import akka.stream.actor.ActorPublisher +import org.zalando.nakadi.client.actor.EventReceivingActor.NextEvent +import org.zalando.nakadi.client.scala.model.Cursor +import org.zalando.nakadi.client.scala.model.Event import org.zalando.nakadi.client.scala.model.EventStreamBatch -import akka.stream.Outlet -import akka.stream.ActorMaterializerSettings -import akka.stream.Attributes +import org.zalando.nakadi.client.java.model.{Event => JEvent} +import org.zalando.nakadi.client.utils.FutureConversions +import com.typesafe.scalalogging.Logger import akka.NotUsed -import akka.http.scaladsl.settings.ConnectionPoolSettings +import akka.actor.Actor +import akka.actor.ActorSystem +import akka.actor.Props +import akka.actor.Terminated +import akka.actor.actorRef2Scala +import akka.http.scaladsl.Http import akka.http.scaladsl.Http.HostConnectionPool +import akka.http.scaladsl.HttpsConnectionContext +import akka.http.scaladsl.model.HttpHeader +import akka.http.scaladsl.model.HttpMethods +import akka.http.scaladsl.model.HttpRequest +import akka.http.scaladsl.model.HttpResponse +import akka.http.scaladsl.unmarshalling.Unmarshal +import akka.stream.ActorMaterializer +import akka.stream.ActorMaterializerSettings +import akka.stream.OverflowStrategy +import akka.stream.actor.ActorPublisher +import akka.stream.actor.ActorSubscriber +import akka.stream.scaladsl.Flow import akka.stream.scaladsl.Framing +import akka.stream.scaladsl.Sink +import akka.stream.scaladsl.Source +import akka.util.ByteString +import javax.net.ssl.SSLContext +import javax.net.ssl.TrustManager +import javax.net.ssl.X509TrustManager +import org.zalando.nakadi.client.utils.ModelConverter -trait Connection extends HttpFactory { +trait JEmptyEvent extends JEvent +trait SEmptyEvent extends Event +trait Connection extends HttpFactory { + //Connection details def host: String def port: Int @@ -58,8 +72,8 @@ trait Connection extends HttpFactory { def put[T](endpoint: String, model: T)(implicit serializer: Serializer[T]): Future[HttpResponse] def subscribe[T <: Event](url: String, cursor: Option[Cursor], listener: Listener[T])(implicit des: Deserializer[EventStreamBatch[T]]) def subscribeJava[T <: org.zalando.nakadi.client.java.model.Event]( - url: String, cursor: Option[org.zalando.nakadi.client.java.model.Cursor], - listener: org.zalando.nakadi.client.java.Listener[T])(implicit des: Deserializer[org.zalando.nakadi.client.java.model.EventStreamBatch[T]]) + url: String, cursor: Optional[org.zalando.nakadi.client.java.model.Cursor], + listener: org.zalando.nakadi.client.java.Listener[T])(implicit des: Deserializer[org.zalando.nakadi.client.java.model.EventStreamBatch[T]]): java.util.concurrent.Future[Void] def stop(): Future[Terminated] def materializer(): ActorMaterializer @@ -217,28 +231,36 @@ sealed class ConnectionImpl(val host: String, val port: Int, val tokenProvider: def stop(): Future[Terminated] = actorSystem.terminate() def subscribe[T <: Event](url: String, cursor: Option[Cursor], listener: Listener[T])(implicit des: Deserializer[EventStreamBatch[T]]) = { - logger.info("Subscribing listener {} to uri {}", listener.id, url) + val msgHandler:EventHandler[JEmptyEvent, T] = new EventHandler[JEmptyEvent, T](None,Option((des,listener))) + handleSubscription(url, cursor, msgHandler) + } + private def handleSubscription[J <: JEvent, S <: Event](url: String, cursor: Option[Cursor], msgHandler: EventHandler[J, S] ) = { + logger.info("Handler [{}] cursor {} uri [{}]", msgHandler.id, cursor, url) + + import EventReceivingActor._ //Create the Actors val eventReceiver = actorSystem.actorOf(Props(classOf[EventReceivingActor], url)) val receiver = ActorPublisher[Option[Cursor]](eventReceiver) - val eventConsumer = actorSystem.actorOf(Props(classOf[EventConsumer[T]], url, listener, eventReceiver, des)) + val eventConsumer = actorSystem.actorOf(Props(classOf[EventConsumingActor[J,S]], url,eventReceiver, msgHandler)) val consumer = ActorSubscriber[ByteString](eventConsumer) + //Setup the pipeline flow val pipeline = Flow[Option[Cursor]].via(requestCreator(url)) .via(connection) .buffer(RECEIVE_BUFFER_SIZE, OverflowStrategy.backpressure) .via(requestRenderer) + //Put all pieces together Source.fromPublisher(receiver) .via(pipeline) .runForeach(_.runWith(Sink.fromSubscriber(consumer))) + //HOPAAA!! eventReceiver ! NextEvent(cursor) } - private def requestCreator(url: String): Flow[Option[Cursor], HttpRequest, NotUsed] = Flow[Option[Cursor]].map(withHttpRequest(url, _, None, tokenProvider)) @@ -251,8 +273,18 @@ sealed class ConnectionImpl(val host: String, val port: Int, val tokenProvider: .via(Framing.delimiter(ByteString(EVENT_DELIMITER), maximumFrameLength = RECEIVE_BUFFER_SIZE, allowTruncation = true)) def subscribeJava[T <: org.zalando.nakadi.client.java.model.Event]( - url: String, cursor: Option[org.zalando.nakadi.client.java.model.Cursor], - listener: org.zalando.nakadi.client.java.Listener[T])(implicit des: Deserializer[org.zalando.nakadi.client.java.model.EventStreamBatch[T]]) = ??? + url: String, cursor: Optional[org.zalando.nakadi.client.java.model.Cursor], + listener: org.zalando.nakadi.client.java.Listener[T])(implicit des: Deserializer[org.zalando.nakadi.client.java.model.EventStreamBatch[T]]) = { + FutureConversions.fromFuture2FutureVoid { + Future { + import ModelConverter._ + val scalaCursor = toScalaCursor(cursor) + val scalaListener = toScalaListener(listener) + val msgHandler:EventHandler[T, SEmptyEvent] = new EventHandler[T, SEmptyEvent](Option((des,listener)),None) + handleSubscription(url, scalaCursor, msgHandler) -} + } + } + } +} diff --git a/client/src/main/scala/org/zalando/nakadi/client/scala/HttpFactory.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/HttpFactory.scala index 13148fc..8352c06 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/scala/HttpFactory.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/scala/HttpFactory.scala @@ -13,7 +13,6 @@ import org.zalando.nakadi.client.scala.model.Cursor import org.slf4j.LoggerFactory import com.typesafe.scalalogging.Logger - // trait HttpFactory { type TokenProvider = () => String @@ -34,9 +33,9 @@ trait HttpFactory { case None => Nil } } - - def withUrl(url:String, params: Option[StreamParameters])={ - val paramsList = withQueryParams(params) + + def withUrl(url: String, params: Option[StreamParameters]) = { + val paramsList = withQueryParams(params) val urlParams = if (!paramsList.isEmpty) paramsList.mkString("?", "&", "") else "" url + urlParams } @@ -44,7 +43,7 @@ trait HttpFactory { def withHttpRequest(url: String, httpMethod: HttpMethod, additionalHeaders: Seq[HttpHeader], tokenProvider: TokenProvider, params: Option[StreamParameters]): HttpRequest = { val allHeaders: Seq[HttpHeader] = additionalHeaders :+ headers.Authorization(OAuth2BearerToken(tokenProvider())) val paramsList = withQueryParams(params) - + HttpRequest(uri = url, method = httpMethod).withHeaders(allHeaders) } @@ -67,7 +66,6 @@ trait HttpFactory { } def withHttpRequest(url: String, cursor: Option[Cursor], flowId: Option[String], tokenProvider: () => String): HttpRequest = { - println("############### URL %s cursor %s".format(url,cursor)) val customHeaders = withDefaultHeaders(cursor, flowId) :+ RawHeader("Accept", "application/x-json-stream") val tokenHeader = headers.Authorization(OAuth2BearerToken(tokenProvider())) val allHeaders: Seq[HttpHeader] = customHeaders :+ tokenHeader @@ -75,4 +73,3 @@ trait HttpFactory { } } -// diff --git a/client/src/main/scala/org/zalando/nakadi/client/scala/MessageHandler.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/MessageHandler.scala new file mode 100644 index 0000000..a585381 --- /dev/null +++ b/client/src/main/scala/org/zalando/nakadi/client/scala/MessageHandler.scala @@ -0,0 +1,111 @@ +package org.zalando.nakadi.client.scala + +import scala.util.Failure +import scala.util.Success +import scala.util.Try +import org.zalando.nakadi.client.Deserializer +import org.zalando.nakadi.client.java.model.{ Cursor => JCursor } +import org.zalando.nakadi.client.java.model.{ Event => JEvent } +import org.zalando.nakadi.client.java.model.{ EventStreamBatch => JEventStreamBatch } +import org.zalando.nakadi.client.java.{ Listener => JListener } +import org.zalando.nakadi.client.java.{ ClientError => JClientError } +import org.zalando.nakadi.client.scala.model.Cursor +import org.zalando.nakadi.client.scala.model.Event +import org.zalando.nakadi.client.scala.model.EventStreamBatch +import org.zalando.nakadi.client.utils.ModelConverter +import java.util.{ List => JList } +import java.util.Optional +import org.slf4j.LoggerFactory +import com.typesafe.scalalogging.Logger + +private object EventHandler { + import ModelConverter._ + def transformScala[S <: Event](message: String, des: Deserializer[EventStreamBatch[S]]): Either[ErrorResult, ScalaResult[S]] = Try(des.from(message)) match { + case Success(eventBatch) => + eventBatch match { + case EventStreamBatch(cursor, events) => Right(ScalaResult(events, Some(cursor))) + } + case Failure(err) => Left(ErrorResult(err)) + } + def transformJava[J <: JEvent](message: String, des: Deserializer[JEventStreamBatch[J]]): Either[ErrorResult, JavaResult[J]] = { + Try(des.from(message)) match { + case Success(eventBatch) => + val events = if (eventBatch.getEvents != null && !eventBatch.getEvents.isEmpty()) Some(eventBatch.getEvents) else None + val sCursor = toScalaCursor(eventBatch.getCursor) + val jcursor = Option(eventBatch.getCursor) + Right(JavaResult(events, sCursor, jcursor)) + case Failure(err) => + Left(ErrorResult(err)) + } + } +} +trait Result + +case class JavaResult[J <: JEvent]( + val javaEvents: Option[java.util.List[J]], + val scalaCursor: Option[Cursor], + val javaCursor: Option[JCursor]) extends Result + +case class ScalaResult[S <: Event]( + scalaEvents: Option[Seq[S]] = None, + scalaCursor: Option[Cursor]) extends Result +case class ErrorResult(error: Throwable) extends Result + +class EventHandler[J <: JEvent, S <: Event](java: Option[(Deserializer[JEventStreamBatch[J]], JListener[J])], scala: Option[(Deserializer[EventStreamBatch[S]], Listener[S])]) { + import EventHandler._ + val log = Logger(LoggerFactory.getLogger(this.getClass)) + + private def createException(msg: String) = new IllegalStateException(msg) + lazy val id: String = { + (java, scala) match { + case (Some((_, listener)), _) => listener.getId + case (_, Some((_, listener))) => listener.id + case _ => "COULD NOT BE DEFINED" + } + } + def handle(url: String, msg: String): Either[ErrorResult, Option[Cursor]] = { + // log.debug(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>") + // log.debug(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>") + // log.debug(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>") + // log.debug(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>") + // log.debug(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>") + // log.debug(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>") + (java, scala) match { + case (Some((des, listener)), _) => // Java + transformJava(msg, des).right.flatMap { + case JavaResult(Some(events), Some(sCursor), Some(jCursor)) => + log.debug("RECEIVED SOMETHING 1") + listener.onReceive(url, jCursor, events) + Right(Option(sCursor)) + case JavaResult(None, Some(sCursor), Some(jCursor)) => + log.debug("RECEIVED SOMETHING 2") + Right(Option(sCursor)) + case _ => + log.debug("RECEIVED SOMETHING 3") + val errorMsg = s"Could not handle JAVA Transformation url [$url] listener [${listener.getId}] msg [$msg]" + listener.onError(errorMsg, Optional.empty()) + Left(ErrorResult(createException(errorMsg))) + } + case (_, Some((des, listener))) => //Scala + transformScala(msg, des).right.flatMap { + case ScalaResult(Some(events), Some(cursor)) => + log.debug("RECEIVED SOMETHING 4") + listener.onReceive(url, cursor, events) + Right(Option(cursor)) + case ScalaResult(None, Some(cursor)) => + log.debug("RECEIVED SOMETHING 5") + Right(Option(cursor)) + case _ => + log.debug("RECEIVED SOMETHING 6") + val errorMsg = s"Could not handle SCALA Transformation url [$url] listener [${listener.id}] msg [$msg]" + listener.onError(errorMsg, None) + Left(ErrorResult(createException(errorMsg))) + + } + case _ => + val errorMsg = s"Could not handle SCALA Transformation url [$url] msg [$msg]" + Left(ErrorResult(createException(errorMsg))) + } + } + +} diff --git a/client/src/main/scala/org/zalando/nakadi/client/utils/FutureConversions.scala b/client/src/main/scala/org/zalando/nakadi/client/utils/FutureConversions.scala index e4afefb..7292e29 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/utils/FutureConversions.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/utils/FutureConversions.scala @@ -27,6 +27,9 @@ object FutureConversions { def fromFuture2Future[T](in: scala.concurrent.Future[T]): java.util.concurrent.Future[T] = { new MFuture[T, T](in, a => a) } + def fromFuture2FutureVoid[T](in: scala.concurrent.Future[T]): java.util.concurrent.Future[Void] = { + new MFuture[T, Void](in, a => null) + } // /** // * Transforms the sequence in the option wrapped in the either right inside a diff --git a/client/src/main/scala/org/zalando/nakadi/client/utils/ImplicitHelper.scala b/client/src/main/scala/org/zalando/nakadi/client/utils/ImplicitHelper.scala deleted file mode 100644 index 31269ff..0000000 --- a/client/src/main/scala/org/zalando/nakadi/client/utils/ImplicitHelper.scala +++ /dev/null @@ -1,31 +0,0 @@ -package org.zalando.nakadi.client.utils - -import org.zalando.nakadi.client.java.model._ -import org.zalando.nakadi.client.Deserializer -import org.zalando.nakadi.client.Serializer -import com.fasterxml.jackson.core.`type`.TypeReference -import org.zalando.nakadi.client.java.enumerator._ -import org.zalando.nakadi.client.java._ -import org.zalando.nakadi.client.java.model.JavaJacksonJsonMarshaller - - - -/** - * Meant for usage in the Java client side. - */ -object Serialization { - - def defaultSerializer[T](): Serializer[T] = JavaJacksonJsonMarshaller.serializer[T] - def metricsDeserializer(): Deserializer[Metrics] = JavaJacksonJsonMarshaller.deserializer(JavaJacksonJsonMarshaller.metricsTR) - def eventTypeDeserializer(): Deserializer[EventType] = JavaJacksonJsonMarshaller.deserializer(JavaJacksonJsonMarshaller.eventTypeTR) - def customDeserializer[T](tr: TypeReference[T]): Deserializer[T] = JavaJacksonJsonMarshaller.deserializer(tr) - def partitionDeserializer(): Deserializer[Partition] = JavaJacksonJsonMarshaller.deserializer(JavaJacksonJsonMarshaller.partitionTR) - - //Sequence - def seqOfEventTypeDeserializer(): Deserializer[java.util.List[EventType]] = JavaJacksonJsonMarshaller.deserializer(JavaJacksonJsonMarshaller.listOfEventTypeTR) - def seqOfPartitionDeserializer(): Deserializer[java.util.List[Partition]] = JavaJacksonJsonMarshaller.deserializer(JavaJacksonJsonMarshaller.listOfPartitionTR) - def seqOfEventValidationStrategy(): Deserializer[java.util.List[EventValidationStrategy]] = JavaJacksonJsonMarshaller.deserializer(JavaJacksonJsonMarshaller.listOfEventValidationStrategyTR) - def seqOfEventEnrichmentStrategy(): Deserializer[java.util.List[EventEnrichmentStrategy]] = JavaJacksonJsonMarshaller.deserializer(JavaJacksonJsonMarshaller.listOfEventEnrichmentStrategyTR) - def seqOfPartitionStrategy(): Deserializer[java.util.List[PartitionStrategy]] = JavaJacksonJsonMarshaller.deserializer(JavaJacksonJsonMarshaller.listOfPartitionStrategyTR) - -} \ No newline at end of file diff --git a/client/src/main/scala/org/zalando/nakadi/client/utils/ModelConverter.scala b/client/src/main/scala/org/zalando/nakadi/client/utils/ModelConverter.scala new file mode 100644 index 0000000..783e3cd --- /dev/null +++ b/client/src/main/scala/org/zalando/nakadi/client/utils/ModelConverter.scala @@ -0,0 +1,79 @@ +package org.zalando.nakadi.client.utils + +import org.zalando.nakadi.client.scala.model.Cursor +import org.zalando.nakadi.client.scala.model.Event +import org.zalando.nakadi.client.scala.ClientError +import org.zalando.nakadi.client.scala.Listener +import org.zalando.nakadi.client.java.{ ClientError => JClientError } +import org.zalando.nakadi.client.java.model.{ Event => JEvent } +import org.zalando.nakadi.client.java.model.{ EventStreamBatch => JEventStreamBatch } +import org.zalando.nakadi.client.java.model.{ Cursor => JCursor } +import java.util.Optional +import scala.collection.JavaConversions._ +import org.slf4j.LoggerFactory +import com.typesafe.scalalogging.Logger +import org.zalando.nakadi.client.scala.model.EventStreamBatch +object ModelConverter { + def toScalaCursor(in: Optional[JCursor]): Option[Cursor] = { + if (in.isPresent()) { + toScalaCursor(in.get) + } else { + None + } + } + def toScalaCursor(in: JCursor): Option[Cursor] = Option(in) match { + case None => None + case Some(c) => Some(Cursor(c.getPartition, c.getOffset)) + + } + def toJavaCursor(in: Cursor): JCursor = in match { + case Cursor(partition, offset) => + new JCursor(partition, offset) + case null => null + } + + def toScalaListener[T <: JEvent](in: org.zalando.nakadi.client.java.Listener[T]): Listener[T] = { + if (in == null) + null + else { + createListenerWrapper(in) + } + } + + def getScalaCursor[T <: JEvent](in: JEventStreamBatch[T]): Option[Cursor] = { + in.getCursor + Option(in) match { + case None => None + case Some(in) => toScalaCursor(in.getCursor) + } + } + + def getJavaEvents[T <: JEvent](in: JEventStreamBatch[T]): Option[java.util.List[T]] = { + Option(in) match { + case None => None + case Some(in) => Option(in.getEvents) + } + } + + def toJavaClientError(error: Option[ClientError]): Optional[JClientError] = error match { + case Some(ClientError(msg, Some(httpStatusCode))) => Optional.of(new JClientError(msg, Optional.of(httpStatusCode))) + case Some(ClientError(msg, None)) => Optional.of(new JClientError(msg, Optional.empty())) + case None => Optional.empty() + } + + private def createListenerWrapper[T <: org.zalando.nakadi.client.java.model.Event, B <: Event](in: org.zalando.nakadi.client.java.Listener[T]): Listener[T] = { + new Listener[T] { + val logger = Logger(LoggerFactory.getLogger(this.getClass)) + def id: String = in.getId + def onReceive(eventUrl: String, cursor: Cursor, events: Seq[T]): Unit = { + logger.debug("[ListenerWrapper] cursor {} url {} events {}", cursor, eventUrl, events) + in.onReceive(eventUrl, toJavaCursor(cursor), seqAsJavaList(events)) + } + def onError(eventUrl: String, error: Option[ClientError]) = { + logger.debug("[ListenerWrapper] cursor {} url {} error {}", eventUrl, error) + in.onError(eventUrl, toJavaClientError(error)) + } + } + + } +} \ No newline at end of file diff --git a/client/src/test/scala/org/zalando/nakadi/client/utils/TestScalaEntity.scala b/client/src/test/scala/org/zalando/nakadi/client/utils/TestScalaEntity.scala index 41afb18..770a0fb 100644 --- a/client/src/test/scala/org/zalando/nakadi/client/utils/TestScalaEntity.scala +++ b/client/src/test/scala/org/zalando/nakadi/client/utils/TestScalaEntity.scala @@ -8,7 +8,7 @@ object TestScalaEntity { val problem = new Problem("problemType", "title", 312, Option("detail"), Option("instance")) val metrics = new Metrics(Map("metrics" -> "test")) val partition = new Partition(0, 132, 4423) - val cursor = new Cursor(0, 120) + val cursor = new Cursor("0", "120") val eventTypeSchema = new EventTypeSchema(SchemaType.JSON, "schema") val eventValidationStrategy = EventValidationStrategy.NONE val partitionResolutionStrategy = PartitionStrategy.HASH diff --git a/it/src/main/java/org/zalando/nakadi/client/examples/java/EventCounterListener.java b/it/src/main/java/org/zalando/nakadi/client/examples/java/EventCounterListener.java new file mode 100644 index 0000000..e52f038 --- /dev/null +++ b/it/src/main/java/org/zalando/nakadi/client/examples/java/EventCounterListener.java @@ -0,0 +1,38 @@ +package org.zalando.nakadi.client.examples.java; + +import java.util.List; +import java.util.concurrent.atomic.AtomicLong; + +import org.zalando.nakadi.client.java.ClientError; +import org.zalando.nakadi.client.java.model.Cursor; + +public class EventCounterListener implements org.zalando.nakadi.client.java.Listener { + private final String id; + private AtomicLong eventCount = new AtomicLong(0); + + public EventCounterListener(String id) { + this.id = id; + } + + @Override + public String getId() { + return id; + } + + @Override + public void onReceive(String eventUrl, Cursor cursor, List events) { + eventCount.addAndGet(events.size()); + System.out.println("#####################################"); + System.out.println("Received " + events.size()); + System.out.println(String.format("Has a total of %d events", eventCount.get())); + System.out.println("#####################################"); + + } + + @Override + public void onError(String eventUrl, java.util.Optional error) { + // TODO Auto-generated method stub + + } + +} \ No newline at end of file diff --git a/it/src/main/java/org/zalando/nakadi/client/examples/java/EventCreationExample.java b/it/src/main/java/org/zalando/nakadi/client/examples/java/EventCreationExample.java index a5fe100..6586c46 100644 --- a/it/src/main/java/org/zalando/nakadi/client/examples/java/EventCreationExample.java +++ b/it/src/main/java/org/zalando/nakadi/client/examples/java/EventCreationExample.java @@ -15,9 +15,9 @@ import org.zalando.nakadi.client.java.model.EventType; import org.zalando.nakadi.client.java.model.EventTypeSchema; import org.zalando.nakadi.client.java.model.EventTypeStatistics; +import org.zalando.nakadi.client.java.utils.SerializationUtils; import org.zalando.nakadi.client.scala.ClientFactory; import org.zalando.nakadi.client.utils.ClientBuilder; -import org.zalando.nakadi.client.utils.Serialization; import com.google.common.collect.Lists; @@ -126,14 +126,15 @@ public static void main(String[] args) throws InterruptedException, result.get(); // Single Event with Serializer, result = client.publishEvent(eventTypeName, event1, - Serialization.defaultSerializer()); + SerializationUtils.defaultSerializer()); result.get(); // Multi Event result = client.publishEvents(eventTypeName, Arrays.asList(event2)); result.get(); // Multi Event with Serializer - result = client.publishEvents(eventTypeName, Arrays.asList(event3), - Serialization.defaultSerializer()); + + + result = client.publishEvents(eventTypeName, Arrays.asList(event3), SerializationUtils.defaultSerializer()); result.get(); } diff --git a/it/src/main/java/org/zalando/nakadi/client/examples/java/EventListenerExample.java b/it/src/main/java/org/zalando/nakadi/client/examples/java/EventListenerExample.java index 18e57ed..126d2fc 100644 --- a/it/src/main/java/org/zalando/nakadi/client/examples/java/EventListenerExample.java +++ b/it/src/main/java/org/zalando/nakadi/client/examples/java/EventListenerExample.java @@ -1,96 +1,57 @@ package org.zalando.nakadi.client.examples.java; import java.util.List; +import java.util.concurrent.ExecutionException; import java.util.concurrent.atomic.AtomicLong; +import org.zalando.nakadi.client.Deserializer; import org.zalando.nakadi.client.java.Client; import org.zalando.nakadi.client.java.ClientError; +import org.zalando.nakadi.client.java.Listener; import org.zalando.nakadi.client.java.model.Cursor; import org.zalando.nakadi.client.java.model.Event; +import org.zalando.nakadi.client.java.model.EventStreamBatch; import org.zalando.nakadi.client.scala.ClientFactory; import org.zalando.nakadi.client.utils.ClientBuilder; -import org.zalando.nakadi.client.java.Listener; - -public class EventListenerExample { - - /** - * Define how your event should look like - */ - public class MeetingsEvent implements Event { - private final String date; - private final String topic; - - public MeetingsEvent(String date, String topic) { - this.date = date; - this.topic = topic; - } - public String getDate() { - return date; - } +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.core.type.TypeReference; +import com.google.common.base.Optional; - public String getTopic() { - return topic; - } +public class EventListenerExample { - } + /** * Implement the Listener interface */ - class EventCounterListener implements org.zalando.nakadi.client.java.Listener { - private final String id; - private AtomicLong eventCount = new AtomicLong(0); - - public EventCounterListener(String id) { - this.id = id; - } - - @Override - public String getId() { - return id; - } - @Override - public void onReceive(String eventUrl, Cursor cursor, List events) { - eventCount.addAndGet(events.size()); - System.out.println("#####################################"); - System.out.println("Received " + events.size()); - System.out.println(String.format("Has a total of %d events",eventCount.get()) ); - System.out.println("#####################################"); - - } - - @Override - public void onError(String eventUrl, Cursor cursor, ClientError error) { - System.err.println(String.format("Error %s %s %s",eventUrl, cursor, error)); - - } - - } - - public static void main(String[] args) { - EventListenerExample example= new EventListenerExample(); + public static void main(String[] args) throws InterruptedException, ExecutionException { + EventListenerExample example = new EventListenerExample(); /** * Create client */ final Client client = new ClientBuilder()// - .withHost(ClientFactory.host())// - .withSecuredConnection(true) // s - .withVerifiedSslCertificate(false) // s - .withTokenProvider4Java(() -> ClientFactory.getToken())// - .buildJavaClient(); - - - + .withHost(ClientFactory.host())// + .withSecuredConnection(true) // s + .withVerifiedSslCertificate(false) // s + .withTokenProvider4Java(() -> ClientFactory.getToken())// + .buildJavaClient(); + /** * Initialize our Listener */ -// Listener listener = example.new EventCounterListener("Test"); - - - - - + Listener listener = new EventCounterListener("Java-Test"); + Cursor cursor = new Cursor("0", "BEGIN"); + + String eventTypeName = "MeetingsEvent-example-E"; + TypeReference> typeRef = new TypeReference>() { + }; + + java.util.concurrent.Future result = client.subscribe(eventTypeName, java.util.Optional.of(cursor), listener, typeRef); + + result.get(); + } } diff --git a/it/src/main/java/org/zalando/nakadi/client/examples/java/MeetingsEvent.java b/it/src/main/java/org/zalando/nakadi/client/examples/java/MeetingsEvent.java new file mode 100644 index 0000000..a266b83 --- /dev/null +++ b/it/src/main/java/org/zalando/nakadi/client/examples/java/MeetingsEvent.java @@ -0,0 +1,26 @@ +package org.zalando.nakadi.client.examples.java; + +import org.zalando.nakadi.client.java.model.Event; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +public class MeetingsEvent implements Event { + private final String date; + private final String topic; + + @JsonCreator + public MeetingsEvent(@JsonProperty("date") String date, @JsonProperty("topic") String topic) { + this.date = date; + this.topic = topic; + } + + public String getDate() { + return date; + } + + public String getTopic() { + return topic; + } + +} \ No newline at end of file diff --git a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala index 812b300..345e816 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala @@ -15,8 +15,8 @@ import java.util.concurrent.atomic.AtomicLong class EventCounterListener(val id: String) extends Listener[MeetingsEvent] { private var eventCount: AtomicLong = new AtomicLong(0); - def onError(sourceUrl: String, cursor: Cursor, error: ClientError): Unit = { - println("Error %s %s %s".format(sourceUrl,cursor,error)) + def onError(sourceUrl: String, error: Option[ClientError]): Unit = { + println("Error %s %s %s".format(sourceUrl,error)) } def onReceive(sourceUrl: String, cursor: Cursor, events: Seq[MeetingsEvent]): Unit = { @@ -51,14 +51,14 @@ object EventListenerExample extends App { * Create the Parameters with the cursor. */ - val cursor = Cursor(0, 4) + val cursor = Cursor("0", "BEGIN") val parameters = new StreamParameters( cursor = Some(cursor) // - , batchLimit = Some(100) // Maximum number of `Event`s in each chunk (and therefore per partition) of the stream. + , batchLimit = Some(1) // Maximum number of `Event`s in each chunk (and therefore per partition) of the stream. // , streamLimit = Some(2) // Maximum number of `Event`s to stream (over all partitions being streamed in this //connection). -// , batchFlushTimeout = Some(15) // Maximum time in seconds to wait for the flushing of each chunk (per partition). + , batchFlushTimeout = Some(5) // Maximum time in seconds to wait for the flushing of each chunk (per partition). // ,streamKeepAliveLimit=Some(4) // , streamTimeout = Some(30) ) diff --git a/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala b/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala index f93570e..4268564 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala @@ -3,7 +3,7 @@ object ClientFactory { import sys.process._ import scala.language.postfixOps def host():String = "nakadi-sandbox.aruha-test.zalan.do" - def OAuth2Token(): () => String = () => "9eb412e6-3ffc-4ccd-b167-33af553a4c80" + def OAuth2Token(): () => String = () => "" def getToken():String = OAuth2Token().apply() def port():Integer = 443 def connection():Connection = Connection.newConnection(host, port, OAuth2Token(), true, false) diff --git a/it/src/main/scala/org/zalando/nakadi/client/subscription/LeClient.scala b/it/src/main/scala/org/zalando/nakadi/client/subscription/LeClient.scala deleted file mode 100644 index 7b7f53f..0000000 --- a/it/src/main/scala/org/zalando/nakadi/client/subscription/LeClient.scala +++ /dev/null @@ -1,26 +0,0 @@ -package org.zalando.nakadi.client.subscription - -import org.zalando.nakadi.client.scala.ClientFactory -import com.fasterxml.jackson.core.`type`.TypeReference -import org.zalando.nakadi.client.scala.model.JacksonJsonMarshaller -import org.zalando.nakadi.client.scala.model.Event - -object Client extends App { - val client = new LeClient - client.send(100) -} - -class LeClient { - import ClientFactory._ - case class MyEventExample(orderNumber: String) extends Event - implicit def myEventExampleTR: TypeReference[MyEventExample] = new TypeReference[MyEventExample] {} - import JacksonJsonMarshaller._ - val eventType = "test-client-integration-event-1936085527-148383828851369665" - def send(in: Int) { - val events = for { - a <- 1 to in - } yield MyEventExample("order-" + a) - - client.publishEvents(eventType, events) - } -} \ No newline at end of file diff --git a/it/src/main/scala/org/zalando/nakadi/client/subscription/MyEventExample.scala b/it/src/main/scala/org/zalando/nakadi/client/subscription/MyEventExample.scala deleted file mode 100644 index 3f3428d..0000000 --- a/it/src/main/scala/org/zalando/nakadi/client/subscription/MyEventExample.scala +++ /dev/null @@ -1,5 +0,0 @@ -package org.zalando.nakadi.client.subscription - -import org.zalando.nakadi.client.scala.model.Event - -case class MyEventExample(orderNumber: String) extends Event \ No newline at end of file diff --git a/it/src/main/scala/org/zalando/nakadi/client/subscription/MyListener.scala b/it/src/main/scala/org/zalando/nakadi/client/subscription/MyListener.scala deleted file mode 100644 index 3838900..0000000 --- a/it/src/main/scala/org/zalando/nakadi/client/subscription/MyListener.scala +++ /dev/null @@ -1,16 +0,0 @@ -package org.zalando.nakadi.client.subscription - -import org.zalando.nakadi.client.scala.model.Cursor -import org.zalando.nakadi.client.scala.Listener -import org.zalando.nakadi.client.scala.ClientError - -class MyListener extends Listener[MyEventExample] { - def id: String = "test" - def onError(sourceUrl: String, cursor: Cursor, error: ClientError): Unit = { - println("YOOOOOOOOOOOOOO ") - } - def onSubscribed(): Unit = ??? - def onUnsubscribed(): Unit = ??? - def onReceive(sourceUrl: String, cursor: Cursor, event: Seq[MyEventExample]): Unit = ??? - -} \ No newline at end of file diff --git a/it/src/main/scala/org/zalando/nakadi/client/subscription/Subscriber.scala b/it/src/main/scala/org/zalando/nakadi/client/subscription/Subscriber.scala deleted file mode 100644 index 1d35390..0000000 --- a/it/src/main/scala/org/zalando/nakadi/client/subscription/Subscriber.scala +++ /dev/null @@ -1,63 +0,0 @@ -package org.zalando.nakadi.client.subscription - -import scala.concurrent.Await -import scala.concurrent.duration.DurationInt - -import org.zalando.nakadi.client.scala.ClientFactory -import org.zalando.nakadi.client.scala.StreamParameters -import org.zalando.nakadi.client.scala.model.Cursor -import org.zalando.nakadi.client.scala.model.JacksonJsonMarshaller - -import com.fasterxml.jackson.core.`type`.TypeReference - - -object Subscriber extends App { - val a = new Subscriber() - a.startListening() - // a.printPartitions() - // a.printEventTypes() - // a.sendEvents(30) -} - -class Subscriber { - import ClientFactory._ - import JacksonJsonMarshaller._ - val eventType = "test-client-integration-event-1936085527-148383828851369665" - implicit def myEventExampleTR: TypeReference[MyEventExample] = new TypeReference[MyEventExample] {} - def startListening() = { - - val listener = new MyListener - val url = "/event-types/test-client-integration-event-1936085527-148383828851369665/events" - val cr = Cursor(0, 170000) - val params = new StreamParameters( - cursor = Some(cr), batchLimit = Some(500) // ,streamLimit=Some(10) -// ,streamTimeout=Some(10) - // ,streamKeepAliveLimit =Some(10) - ) -// client.subscribe(eventType, params, listener) - } - - def printPartitions() = { - val result = Await.result(client.getPartitions(eventType), 5.seconds) - print("partitions", result) - } - - def printEventTypes() = { - val result = Await.result(client.getEventTypes(), 5.seconds) - print("eventTypes", result) - } - - def print(msg: String, obj: Any) = { - println("###########################") - println(s"$msg - " + obj) - println("###########################") - } - def sendEvents(in: Int) = { - val events = for { - a <- 1 to in - } yield MyEventExample("order-" + a) - client.publishEvents(eventType, events) - } -} - - \ No newline at end of file diff --git a/it/src/test/scala/org/zalando/nakadi/client/ClientSubscriptionTest.scala b/it/src/test/scala/org/zalando/nakadi/client/ClientSubscriptionTest.scala index c1f2c31..336379e 100644 --- a/it/src/test/scala/org/zalando/nakadi/client/ClientSubscriptionTest.scala +++ b/it/src/test/scala/org/zalando/nakadi/client/ClientSubscriptionTest.scala @@ -26,7 +26,7 @@ class ClientSubscriptionTest extends WordSpec with Matchers with ModelFactory { val listener = new Listener[MyEventExample] { def id: String = "test" - def onError(sourceUrl: String, cursor: Cursor, error: ClientError): Unit = { + def onError(sourceUrl: String, error: Option[ClientError]): Unit = { println("YOOOOOOOOOOOOOO ") } def onSubscribed(): Unit = ??? diff --git a/it/src/test/scala/org/zalando/nakadi/client/done/EventConsumerActor.scala b/it/src/test/scala/org/zalando/nakadi/client/done/EventConsumerActor.scala deleted file mode 100644 index 6c870fe..0000000 --- a/it/src/test/scala/org/zalando/nakadi/client/done/EventConsumerActor.scala +++ /dev/null @@ -1,27 +0,0 @@ -package org.zalando.nakadi.client.done - -import akka.actor.Actor -import akka.actor.ActorLogging -import akka.stream.actor.ActorSubscriber -import akka.stream.actor.ActorSubscriberMessage.OnNext -import akka.stream.actor.RequestStrategy -import akka.util.ByteString - -class EventConsumingActor extends Actor with ActorLogging with ActorSubscriber { - - var count = 0 - - override protected def requestStrategy: RequestStrategy = new RequestStrategy { - override def requestDemand(remainingRequested: Int): Int = { - Math.max(remainingRequested, 10) - } - } - - override def receive: Receive = { - case OnNext(input: ByteString) => - count += 1 - println(s"[Got an event($count) "+ input.utf8String+"]") - case OnNext(_) => - println("Got something") - } -} diff --git a/it/src/test/scala/org/zalando/nakadi/client/done/EventPublishingActor.scala b/it/src/test/scala/org/zalando/nakadi/client/done/EventPublishingActor.scala deleted file mode 100644 index 571e90b..0000000 --- a/it/src/test/scala/org/zalando/nakadi/client/done/EventPublishingActor.scala +++ /dev/null @@ -1,18 +0,0 @@ -package org.zalando.nakadi.client.done - -//class EventPublishingActor extends Actor with ActorLogging with ActorPublisher[Url] { -// -// val queue: mutable.Queue[Url] = mutable.Queue() -// val visited: mutable.Set[String] = mutable.Set() -// -// override def receive: Receive = { -// case url: Url => -// if (!visited(url.url)) { -// visited += url.url -// queue.enqueue(url) -// if (isActive && totalDemand > 0) { -// onNext(queue.dequeue()) -// } -// } -// } -//} \ No newline at end of file diff --git a/it/src/test/scala/org/zalando/nakadi/client/example2/Ecample3.scala b/it/src/test/scala/org/zalando/nakadi/client/example2/Ecample3.scala deleted file mode 100644 index 323a63c..0000000 --- a/it/src/test/scala/org/zalando/nakadi/client/example2/Ecample3.scala +++ /dev/null @@ -1,96 +0,0 @@ -package org.zalando.nakadi.client.example2 - -import akka.stream.actor.ActorPublisher -import akka.actor.Actor -import akka.stream.actor.RequestStrategy -import akka.actor.ActorLogging -import akka.stream.actor.ActorSubscriber -import akka.stream.actor.ActorSubscriberMessage.OnNext -import akka.actor.Props -import akka.stream.scaladsl.Sink -import akka.stream.scaladsl.Source -import akka.actor.ActorSystem -import akka.http.scaladsl.model.HttpResponse -import scala.util.Try -import java.net.URI -import akka.stream.scaladsl.Flow -import org.zalando.nakadi.client.scala.Connection -import akka.http.scaladsl.model.HttpRequest -import scala.concurrent.Future -import org.zalando.nakadi.client.scala.HttpFactory -import akka.http.scaladsl.unmarshalling._ -import scala.concurrent.ExecutionContext.Implicits.global -import akka.http.scaladsl.Http -import org.zalando.nakadi.client.scala.model.JacksonJsonMarshaller -import org.zalando.nakadi.client.scala.ClientFactory -import org.zalando.nakadi.client.scala.StreamParameters -import akka.stream.ActorMaterializer -import akka.http.scaladsl.model.headers.RawHeader -import akka.http.scaladsl.model.HttpMethods -import com.fasterxml.jackson.core.`type`.TypeReference -import org.zalando.nakadi.client.done.EventConsumingActor - -case class MoreMessages() - -class ProducerActor(httpRequest: HttpRequest) extends Actor with ActorPublisher[HttpRequest] with HttpFactory with ActorLogging { - - override def receive: Receive = { - case request: MoreMessages => - log.info("Got a request for more messages") - onNext(httpRequest) - } -} - - -case class Url(url: String, depth: Long) - -object Test extends App with HttpFactory { - import ClientFactory._ - import JacksonJsonMarshaller._ - private implicit val actorSystem = ActorSystem("Nakadi-Client-Connections") - private implicit val http = Http(actorSystem) - implicit val materializer = ActorMaterializer() - - // Request Parameters - val eventName = "/event-types/test-client-integration-event-1936085527-148383828851369665/events" - val params = Some(StreamParameters()) - val headers = withHeaders(params) :+ RawHeader("Accept", "application/x-json-stream") - val request = withHttpRequest(eventName, HttpMethods.GET, headers, OAuth2Token(),None) - - //Model - case class MyEventExample(orderNumber: String) - implicit val myEvent = new TypeReference[MyEventExample] {} - - private implicit val system = ActorSystem("Nakadi-Client-Connections") - // private implicit val http = Http(actorSystem) - // implicit val materializer = ActorMaterializer() - - val producerRef = system.actorOf(Props(classOf[EventConsumingActor], request)) - val consumerRef = system.actorOf(Props(classOf[EventConsumingActor])) - val publisher = ActorPublisher[HttpRequest](producerRef) - val subscriber = ActorSubscriber[HttpResponse](consumerRef) - - val flow: Flow[HttpRequest, HttpResponse, Future[Http.OutgoingConnection]] = connection.connection() - - import Transformers._ - - Source.fromPublisher(publisher) - .via(flow).via(validRequestFilter)//.via(toByteString) - .runWith(Sink.fromSubscriber(subscriber)) - - // .map(url => pipeline(Get(url.url)).map((url, _))) - // .mapAsync(4)(identity) - // .map(parseUrls) - // .mapConcat(identity) - // .map(url => Url(url.url, url.depth - 1)) // reduce our depth on each step - // .filter(_.depth >= 0) // our recursion exit condition - // .filter(_.url.contains("akka")) // let’s say we only want to follow urls that contain ‘akka’ - // .runWith(Sink.fromSubscriber(subscriber)) - - producerRef ! Url("https://en.wikipedia.org/wiki/Akka_(toolkit)", 2) - - def parseUrls: ((String, HttpResponse)) => Future[String] = { - case (url, resp) => - Unmarshal(resp.entity).to[String] - } -} \ No newline at end of file diff --git a/it/src/test/scala/org/zalando/nakadi/client/example2/HttpClientExample.scala b/it/src/test/scala/org/zalando/nakadi/client/example2/HttpClientExample.scala deleted file mode 100644 index c031ad4..0000000 --- a/it/src/test/scala/org/zalando/nakadi/client/example2/HttpClientExample.scala +++ /dev/null @@ -1,96 +0,0 @@ -package org.zalando.nakadi.client.example2 - -import scala.concurrent.ExecutionContext.Implicits.global -import scala.concurrent.Future - -import org.zalando.nakadi.client.Deserializer -import org.zalando.nakadi.client.done.EventConsumingActor -import org.zalando.nakadi.client.scala.{ ClientFactory, Connection, HttpFactory } -import org.zalando.nakadi.client.scala.model.{ Cursor, JacksonJsonMarshaller } -import org.zalando.nakadi.client.scala.StreamParameters - -import com.fasterxml.jackson.core.`type`.TypeReference - -import akka.NotUsed -import akka.actor.{ ActorLogging, ActorSystem, Props, actorRef2Scala } -import akka.http.scaladsl.Http -import akka.http.scaladsl.model.{ HttpHeader, HttpMethods, HttpRequest, HttpResponse } -import akka.http.scaladsl.model.headers.RawHeader -import akka.http.scaladsl.unmarshalling.Unmarshal -import akka.stream.{ ActorMaterializer, ClosedShape, FlowShape, Outlet, OverflowStrategy } -import akka.stream.actor.ActorSubscriber -import akka.stream.actor.RequestStrategy -import akka.stream.scaladsl.{ Flow, GraphDSL, RunnableGraph, Sink, Source } -import akka.util.ByteString - -object HttpClient extends App with HttpFactory { - import ClientFactory._ - import JacksonJsonMarshaller._ - private implicit val actorSystem = ActorSystem("Nakadi-Client-Connections") - private implicit val http = Http(actorSystem) - implicit val materializer = ActorMaterializer() - val eventName = "/event-types/test-client-integration-event-1936085527-148383828851369665/events" - val cursor = Cursor(0,30115) - val params = Some(StreamParameters()) - val headers = withHeaders(params) :+ RawHeader("Accept", "application/x-json-stream") - val request = withHttpRequest(eventName, HttpMethods.GET, headers, OAuth2Token(),None) - case class MyEventExample(orderNumber: String) - implicit val myEvent = new TypeReference[MyEventExample] {} - val receiver = new ReceiverGraph[MyEventExample](eventName, connection, request, headers) - receiver.listen() -} - -class ReceiverGraph[T](eventName: String, connection: Connection, request: HttpRequest, headers: Seq[HttpHeader]) // -(implicit system: ActorSystem, m: ActorMaterializer, des: Deserializer[T]) { - - import GraphDSL.Implicits._ - def listen() = { - - val subscriber = ActorSubscriber[ByteString](system.actorOf(Props[EventConsumingActor])) - val sink2 = Sink.fromSubscriber(subscriber) - val flow: Flow[HttpRequest, HttpResponse, Future[Http.OutgoingConnection]] = connection.connection() - - import Transformers._ - val req = Source.single(request) // - .via(flow) // - .buffer(100, OverflowStrategy.backpressure) // - .via(validRequestFilter).map(_.entity.dataBytes) - .runForeach(_.runWith(sink2)).onComplete { _ => - println("Shutting down") - system.terminate() - } - } - - def test() = { - import Transformers._ - val g: RunnableGraph[_] = RunnableGraph.fromGraph { - GraphDSL.create() { implicit builder => - val out: Outlet[HttpResponse] = builder.add(Source.single(request).via(connection.connection())).out - - //flows - // val validFilter: FlowShape[HttpResponse, HttpResponse] = builder.add(validRequestFilter) - val transform2String: FlowShape[HttpResponse, String] = builder.add(toByteString) - // val deserializer: FlowShape[String, T] = builder.add(transformToObject) - // val printer: Inlet[Any] = builder.add(Sink.foreach[Any](x => println(" >>> " + x))).in - val in = builder.add(Sink.foreach[String] { x: String => println(s" 1>> $x ") }).in - out ~> transform2String ~> in - - ClosedShape // - } - } - g.run() - } - -} - -object Transformers { - - def failure(s: String) = Future.failed(new IllegalArgumentException(s"Error $s")) - def toByteString(implicit m: ActorMaterializer): Flow[HttpResponse, String, NotUsed] = Flow[HttpResponse].mapAsync(1)(Unmarshal(_).to[String]) - val validRequestFilter: Flow[HttpResponse, HttpResponse, NotUsed] = Flow[HttpResponse].filter(_.status.isSuccess()) - def transformToObject[T](implicit des: Deserializer[T]): Flow[String, T, NotUsed] = Flow[String].map { x => - println(" x " + x) - des.from(x) - } - -} \ No newline at end of file diff --git a/project/Build.scala b/project/Build.scala index 784c347..10abe39 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -65,7 +65,7 @@ lazy val client = withDefaults( project.settings( name := projectName, organization := "org.zalando.nakadi.client", - version := "2.0-SNAPSHOT", + version := "2.0.1-SNAPSHOT", crossPaths := false, scalaVersion := "2.11.7", publishTo := whereToPublishTo(isSnapshot.value), From 2795a743988f0b23a328628499202cc0da35b996 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Fri, 6 May 2016 13:46:26 +0200 Subject: [PATCH 048/183] techjira:LAAS-60 Java Event Listener is working! --- .../nakadi/client/scala/Connection.scala | 11 ++++++++-- .../nakadi/client/scala/MessageHandler.scala | 22 +++++++------------ .../examples/java/EventListenerExample.java | 11 +--------- .../examples/scala/EventCreationExample.scala | 6 ++--- .../examples/scala/EventListenerExample.scala | 4 ++-- .../nakadi/client/scala/ClientFactory.scala | 2 +- 6 files changed, 24 insertions(+), 32 deletions(-) diff --git a/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala index 004779e..1c0d158 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala @@ -254,10 +254,17 @@ sealed class ConnectionImpl(val host: String, val port: Int, val tokenProvider: .via(requestRenderer) //Put all pieces together - Source.fromPublisher(receiver) + val result= Source.fromPublisher(receiver) .via(pipeline) .runForeach(_.runWith(Sink.fromSubscriber(consumer))) - + + result.onFailure{ + case error => + logger.error(error.getMessage) + } + + + //HOPAAA!! eventReceiver ! NextEvent(cursor) } diff --git a/client/src/main/scala/org/zalando/nakadi/client/scala/MessageHandler.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/MessageHandler.scala index a585381..8fda573 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/scala/MessageHandler.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/scala/MessageHandler.scala @@ -64,48 +64,42 @@ class EventHandler[J <: JEvent, S <: Event](java: Option[(Deserializer[JEventStr } } def handle(url: String, msg: String): Either[ErrorResult, Option[Cursor]] = { - // log.debug(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>") - // log.debug(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>") - // log.debug(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>") - // log.debug(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>") - // log.debug(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>") - // log.debug(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>") (java, scala) match { case (Some((des, listener)), _) => // Java transformJava(msg, des).right.flatMap { case JavaResult(Some(events), Some(sCursor), Some(jCursor)) => - log.debug("RECEIVED SOMETHING 1") listener.onReceive(url, jCursor, events) Right(Option(sCursor)) case JavaResult(None, Some(sCursor), Some(jCursor)) => - log.debug("RECEIVED SOMETHING 2") Right(Option(sCursor)) case _ => - log.debug("RECEIVED SOMETHING 3") val errorMsg = s"Could not handle JAVA Transformation url [$url] listener [${listener.getId}] msg [$msg]" + log.error(errorMsg) listener.onError(errorMsg, Optional.empty()) Left(ErrorResult(createException(errorMsg))) } case (_, Some((des, listener))) => //Scala transformScala(msg, des).right.flatMap { case ScalaResult(Some(events), Some(cursor)) => - log.debug("RECEIVED SOMETHING 4") listener.onReceive(url, cursor, events) Right(Option(cursor)) case ScalaResult(None, Some(cursor)) => - log.debug("RECEIVED SOMETHING 5") Right(Option(cursor)) case _ => - log.debug("RECEIVED SOMETHING 6") val errorMsg = s"Could not handle SCALA Transformation url [$url] listener [${listener.id}] msg [$msg]" listener.onError(errorMsg, None) + log.error(errorMsg) Left(ErrorResult(createException(errorMsg))) - } case _ => - val errorMsg = s"Could not handle SCALA Transformation url [$url] msg [$msg]" + val errorMsg = s"Could not find a listener and serialiation url [$url] msg [$msg]" + log.error(errorMsg) Left(ErrorResult(createException(errorMsg))) } } + + def handleError(msg:String, error:Throwable)={ + + } } diff --git a/it/src/main/java/org/zalando/nakadi/client/examples/java/EventListenerExample.java b/it/src/main/java/org/zalando/nakadi/client/examples/java/EventListenerExample.java index 126d2fc..faf3f29 100644 --- a/it/src/main/java/org/zalando/nakadi/client/examples/java/EventListenerExample.java +++ b/it/src/main/java/org/zalando/nakadi/client/examples/java/EventListenerExample.java @@ -1,23 +1,15 @@ package org.zalando.nakadi.client.examples.java; -import java.util.List; import java.util.concurrent.ExecutionException; -import java.util.concurrent.atomic.AtomicLong; -import org.zalando.nakadi.client.Deserializer; import org.zalando.nakadi.client.java.Client; -import org.zalando.nakadi.client.java.ClientError; import org.zalando.nakadi.client.java.Listener; import org.zalando.nakadi.client.java.model.Cursor; -import org.zalando.nakadi.client.java.model.Event; import org.zalando.nakadi.client.java.model.EventStreamBatch; import org.zalando.nakadi.client.scala.ClientFactory; import org.zalando.nakadi.client.utils.ClientBuilder; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.core.type.TypeReference; -import com.google.common.base.Optional; public class EventListenerExample { @@ -28,7 +20,6 @@ public class EventListenerExample { */ public static void main(String[] args) throws InterruptedException, ExecutionException { - EventListenerExample example = new EventListenerExample(); /** * Create client */ @@ -45,7 +36,7 @@ public static void main(String[] args) throws InterruptedException, ExecutionExc Listener listener = new EventCounterListener("Java-Test"); Cursor cursor = new Cursor("0", "BEGIN"); - String eventTypeName = "MeetingsEvent-example-E"; + String eventTypeName = "MeetingsEvent-example-E-2"; TypeReference> typeRef = new TypeReference>() { }; diff --git a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala index 05432d3..233eab6 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala @@ -48,7 +48,7 @@ object EventCreationExample extends App { //First the eventType name, wich will be part of the URL: https://nakadi.test.io/event-types/{eventTypeName} //See the API for more information on the EventType model //https://github.com/zalando/nakadi/blob/nakadi-jvm/api/nakadi-event-bus-api.yaml#L1240 - val eventTypeName = "MeetingsEvent-example-E" + val eventTypeName = "MeetingsEvent-example-E-2" val owner = "team-laas" val category = EventTypeCategory.UNDEFINED // We want just to pass data without through Nakadi, simple schema-validation is enough! @@ -71,13 +71,13 @@ object EventCreationExample extends App { //You need to import the default Serializer if you don't sepecify your own! import JacksonJsonMarshaller._ - // client.createEventType(eventType) +// client.createEventType(eventType) // Thread.sleep(10000) // 4. Publish the EventType // val event = new MeetingsEvent("2016-04-28T13:28:15+00:00", "Hackaton") var events = for { - a <- 1 to 445 + a <- 1 to 49999 } yield MeetingsEvent("2016-04-28T13:28:15+00:00", "Hackaton" + a) // Thread.sleep(1000) diff --git a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala index 345e816..a412aa7 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala @@ -55,10 +55,10 @@ object EventListenerExample extends App { val parameters = new StreamParameters( cursor = Some(cursor) // - , batchLimit = Some(1) // Maximum number of `Event`s in each chunk (and therefore per partition) of the stream. + , batchLimit = Some(200) // Maximum number of `Event`s in each chunk (and therefore per partition) of the stream. // , streamLimit = Some(2) // Maximum number of `Event`s to stream (over all partitions being streamed in this //connection). - , batchFlushTimeout = Some(5) // Maximum time in seconds to wait for the flushing of each chunk (per partition). +// , batchFlushTimeout = Some(5) // Maximum time in seconds to wait for the flushing of each chunk (per partition). // ,streamKeepAliveLimit=Some(4) // , streamTimeout = Some(30) ) diff --git a/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala b/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala index 4268564..3ec8fe2 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala @@ -3,7 +3,7 @@ object ClientFactory { import sys.process._ import scala.language.postfixOps def host():String = "nakadi-sandbox.aruha-test.zalan.do" - def OAuth2Token(): () => String = () => "" + def OAuth2Token(): () => String = () => "021e8f9d-a1e3-4d30-9259-39a39f4ddb9c" def getToken():String = OAuth2Token().apply() def port():Integer = 443 def connection():Connection = Connection.newConnection(host, port, OAuth2Token(), true, false) From 64aa793a8748c9c9c92d99627a45128f76ced0ee Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Fri, 6 May 2016 14:29:18 +0200 Subject: [PATCH 049/183] techjira:LAAS-60 Renamed artifacts to nakadi-clients! --- .../examples/java/EventListenerExample.java | 4 ++-- .../examples/scala/EventCreationExample.scala | 18 +++++++++--------- .../examples/scala/EventListenerExample.scala | 2 +- project/Build.scala | 8 ++++---- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/it/src/main/java/org/zalando/nakadi/client/examples/java/EventListenerExample.java b/it/src/main/java/org/zalando/nakadi/client/examples/java/EventListenerExample.java index faf3f29..235d86e 100644 --- a/it/src/main/java/org/zalando/nakadi/client/examples/java/EventListenerExample.java +++ b/it/src/main/java/org/zalando/nakadi/client/examples/java/EventListenerExample.java @@ -34,9 +34,9 @@ public static void main(String[] args) throws InterruptedException, ExecutionExc * Initialize our Listener */ Listener listener = new EventCounterListener("Java-Test"); - Cursor cursor = new Cursor("0", "BEGIN"); + Cursor cursor = new Cursor("153000", "BEGIN"); - String eventTypeName = "MeetingsEvent-example-E-2"; + String eventTypeName = "MeetingsEvent-example-E"; TypeReference> typeRef = new TypeReference>() { }; diff --git a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala index 233eab6..5d7a604 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala @@ -48,7 +48,7 @@ object EventCreationExample extends App { //First the eventType name, wich will be part of the URL: https://nakadi.test.io/event-types/{eventTypeName} //See the API for more information on the EventType model //https://github.com/zalando/nakadi/blob/nakadi-jvm/api/nakadi-event-bus-api.yaml#L1240 - val eventTypeName = "MeetingsEvent-example-E-2" + val eventTypeName = "Event-example-with-0-messages" val owner = "team-laas" val category = EventTypeCategory.UNDEFINED // We want just to pass data without through Nakadi, simple schema-validation is enough! @@ -71,18 +71,18 @@ object EventCreationExample extends App { //You need to import the default Serializer if you don't sepecify your own! import JacksonJsonMarshaller._ -// client.createEventType(eventType) + client.createEventType(eventType) // Thread.sleep(10000) // 4. Publish the EventType // val event = new MeetingsEvent("2016-04-28T13:28:15+00:00", "Hackaton") - var events = for { - a <- 1 to 49999 - } yield MeetingsEvent("2016-04-28T13:28:15+00:00", "Hackaton" + a) - - // Thread.sleep(1000) - - Await.result(client.publishEvents(eventTypeName, events), 120.seconds) +// var events = for { +// a <- 1 to 49999 +// } yield MeetingsEvent("2016-04-28T13:28:15+00:00", "Hackaton" + a) +// +// // Thread.sleep(1000) +// +// Await.result(client.publishEvents(eventTypeName, events), 120.seconds) client.stop() } \ No newline at end of file diff --git a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala index a412aa7..f9e833b 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala @@ -51,7 +51,7 @@ object EventListenerExample extends App { * Create the Parameters with the cursor. */ - val cursor = Cursor("0", "BEGIN") + val cursor = Cursor("153000", "BEGIN") val parameters = new StreamParameters( cursor = Some(cursor) // diff --git a/project/Build.scala b/project/Build.scala index 10abe39..3b4cdaf 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -37,13 +37,13 @@ lazy val root = project.in(file(".")) .aggregate(api, client) lazy val api = withDefaults( - "nakadi-klients-api", + "nakadi-clients-api", project.in(file("api")) ,true ).settings(libraryDependencies ++= apiDeps) lazy val client = withDefaults( - "nakadi-klients", + "nakadi-clients", project.in(file("client")).dependsOn(api) ,true ).settings(libraryDependencies ++= clientDeps) @@ -64,8 +64,8 @@ lazy val client = withDefaults( def withDefaults(projectName:String, project:sbt.Project, publish:Boolean = false)={ project.settings( name := projectName, - organization := "org.zalando.nakadi.client", - version := "2.0.1-SNAPSHOT", + organization := "org.zalando.laas", + version := "2.0-SNAPSHOT", crossPaths := false, scalaVersion := "2.11.7", publishTo := whereToPublishTo(isSnapshot.value), From 7f99baea16089d25bbb3458687dc1ea7095c87a1 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Fri, 6 May 2016 16:56:17 +0200 Subject: [PATCH 050/183] techjira:LAAS-60 Renamed artifacts to nakadi-clients! --- .../examples/java/EventCounterListener.java | 17 +++++++++++------ .../examples/scala/EventListenerExample.scala | 11 +++++++---- .../nakadi/client/scala/ClientFactory.scala | 4 ++-- 3 files changed, 20 insertions(+), 12 deletions(-) diff --git a/it/src/main/java/org/zalando/nakadi/client/examples/java/EventCounterListener.java b/it/src/main/java/org/zalando/nakadi/client/examples/java/EventCounterListener.java index e52f038..f74256d 100644 --- a/it/src/main/java/org/zalando/nakadi/client/examples/java/EventCounterListener.java +++ b/it/src/main/java/org/zalando/nakadi/client/examples/java/EventCounterListener.java @@ -3,12 +3,15 @@ import java.util.List; import java.util.concurrent.atomic.AtomicLong; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.zalando.nakadi.client.java.ClientError; import org.zalando.nakadi.client.java.model.Cursor; public class EventCounterListener implements org.zalando.nakadi.client.java.Listener { private final String id; private AtomicLong eventCount = new AtomicLong(0); + private Logger log = LoggerFactory.getLogger(this.getClass()); public EventCounterListener(String id) { this.id = id; @@ -22,17 +25,19 @@ public String getId() { @Override public void onReceive(String eventUrl, Cursor cursor, List events) { eventCount.addAndGet(events.size()); - System.out.println("#####################################"); - System.out.println("Received " + events.size()); - System.out.println(String.format("Has a total of %d events", eventCount.get())); - System.out.println("#####################################"); + log.info("#####################################"); + log.info("Received " + events.size()); + log.info(String.format("Has a total of %d events", eventCount.get())); + log.info("#####################################"); } @Override public void onError(String eventUrl, java.util.Optional error) { - // TODO Auto-generated method stub + if (error.isPresent()) { + ClientError clientError = error.get(); + log.error("An error occurred" + clientError.getMsg()); + } } - } \ No newline at end of file diff --git a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala index f9e833b..f452c25 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala @@ -8,11 +8,14 @@ import org.zalando.nakadi.client.scala.model._ import EventCreationExample._ import com.fasterxml.jackson.core.`type`.TypeReference import java.util.concurrent.atomic.AtomicLong +import org.slf4j.LoggerFactory +import com.typesafe.scalalogging.Logger /** * Your listener will have to implement the necessary */ class EventCounterListener(val id: String) extends Listener[MeetingsEvent] { + val log = Logger(LoggerFactory.getLogger(this.getClass)) private var eventCount: AtomicLong = new AtomicLong(0); def onError(sourceUrl: String, error: Option[ClientError]): Unit = { @@ -21,10 +24,10 @@ class EventCounterListener(val id: String) extends Listener[MeetingsEvent] { def onReceive(sourceUrl: String, cursor: Cursor, events: Seq[MeetingsEvent]): Unit = { eventCount.addAndGet(events.size.toLong) - println("#####################################") - println(s"Received " + events.size.toLong) - println(s"Has a total of $eventCount events") - println("#####################################") + log.info("#####################################") + log.info(s"Received " + events.size.toLong) + log.info(s"Has a total of $eventCount events") + log.info("#####################################") } } diff --git a/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala b/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala index 3ec8fe2..05bb34f 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala @@ -2,8 +2,8 @@ package org.zalando.nakadi.client.scala object ClientFactory { import sys.process._ import scala.language.postfixOps - def host():String = "nakadi-sandbox.aruha-test.zalan.do" - def OAuth2Token(): () => String = () => "021e8f9d-a1e3-4d30-9259-39a39f4ddb9c" + def host():String = "nakadi-sandbox.my-test.fernan.do" + def OAuth2Token(): () => String = () => "" def getToken():String = OAuth2Token().apply() def port():Integer = 443 def connection():Connection = Connection.newConnection(host, port, OAuth2Token(), true, false) From 38a714f956aa2f045dd0333eddbcb0e5bd8d898b Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Mon, 9 May 2016 01:59:16 +0200 Subject: [PATCH 051/183] techjira:LAAS-60 Renamed artifacts to nakadi-clients! --- .../nakadi/client/java/ClientImpl.java | 18 ++++++++------- .../org/zalando/nakadi/client/utils/Uri.scala | 12 +++++----- .../examples/java/EventListenerExample.java | 22 +++++++++---------- 3 files changed, 27 insertions(+), 25 deletions(-) diff --git a/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java b/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java index f77507a..7f169df 100644 --- a/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java +++ b/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java @@ -20,6 +20,8 @@ import org.zalando.nakadi.client.scala.Connection; import org.zalando.nakadi.client.utils.Uri; +import sun.reflect.generics.reflectiveObjects.NotImplementedException; + import com.fasterxml.jackson.core.type.TypeReference; public class ClientImpl implements Client { @@ -63,17 +65,17 @@ public Future createEventType(EventType eventType) { @Override public Future> getEventType(String eventTypeName) { - return null; + return connection.get4Java(Uri.getEventTypeByName(eventTypeName), eventTypeDeserializer); } @Override public Future updateEventType(String eventTypeName, EventType eventType) { - return null; + throw new NotImplementedException(); } @Override public Future deleteEventType(String eventTypeName) { - return null; + throw new NotImplementedException(); } @Override @@ -98,17 +100,17 @@ public Future publishEvents(String eventTypeName, List>> getPartitions(String eventTypeName) { - return null; + return connection.get4Java(Uri.getPartitions(eventTypeName), seqOfPartitionDeserializer); } @Override public Future>> getValidationStrategies() { - return null; + return connection.get4Java(Uri.URI_VALIDATION_STRATEGIES(), seqOfEventValidationStrategy); } @Override public Future>> getEnrichmentStrategies() { - return null; + return connection.get4Java(Uri.URI_ENRICHMENT_STRATEGIES(), seqOfEventEnrichmentStrategy); } @Override @@ -119,7 +121,7 @@ public Future>> getPartitioningStrategies() { @Override public Future stop() { - return null; + throw new NotImplementedException(); } @Override @@ -136,7 +138,7 @@ public Future subscribe(String eventTypeName, Optional Future unsubscribe(String eventTypeName, Listener listener) { - return null; + throw new NotImplementedException(); } } \ No newline at end of file diff --git a/client/src/main/scala/org/zalando/nakadi/client/utils/Uri.scala b/client/src/main/scala/org/zalando/nakadi/client/utils/Uri.scala index 20e47b1..ec7942a 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/utils/Uri.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/utils/Uri.scala @@ -2,17 +2,19 @@ package org.zalando.nakadi.client.utils object Uri { - def URI_METRICS = "/metrics" + final def URI_METRICS = "/metrics" /*Events*/ - def URI_EVENT_TYPES = "/event-types" - def URI_EVENT_TYPE_BY_NAME = "/event-types/%s" - def URI_EVENTS_OF_EVENT_TYPE() = "/event-types/%s/events" - def getEventStreamingUri(in:String) = s"/event-types/$in/events" + final def URI_EVENT_TYPES = "/event-types" + final def URI_EVENT_TYPE_BY_NAME = "/event-types/%s" + final def getEventTypeByName(eventTypeName:String) = s"/event-types/$eventTypeName" + final def URI_EVENTS_OF_EVENT_TYPE() = "/event-types/%s/events" + final def getEventStreamingUri(eventTypeName:String) = s"/event-types/$eventTypeName/events" /*Partitions*/ final def URI_PARTITIONS_BY_EVENT_TYPE = "/event-types/%s/partitions" + final def getPartitions(eventTypeName:String) = s"/event-types/$eventTypeName/partitions" /*Strategies*/ final def URI_VALIDATION_STRATEGIES = "/registry/validation-strategies" diff --git a/it/src/main/java/org/zalando/nakadi/client/examples/java/EventListenerExample.java b/it/src/main/java/org/zalando/nakadi/client/examples/java/EventListenerExample.java index 235d86e..8df2850 100644 --- a/it/src/main/java/org/zalando/nakadi/client/examples/java/EventListenerExample.java +++ b/it/src/main/java/org/zalando/nakadi/client/examples/java/EventListenerExample.java @@ -13,8 +13,6 @@ public class EventListenerExample { - - /** * Implement the Listener interface */ @@ -30,19 +28,19 @@ public static void main(String[] args) throws InterruptedException, ExecutionExc .withTokenProvider4Java(() -> ClientFactory.getToken())// .buildJavaClient(); - /** - * Initialize our Listener - */ - Listener listener = new EventCounterListener("Java-Test"); - Cursor cursor = new Cursor("153000", "BEGIN"); +/** + * Initialize our Listener + */ +Listener listener = new EventCounterListener("Java-Test"); +Cursor cursor = new Cursor("0", "BEGIN"); - String eventTypeName = "MeetingsEvent-example-E"; - TypeReference> typeRef = new TypeReference>() { - }; +String eventTypeName = "MeetingsEvent-example-E"; +TypeReference> typeRef = new TypeReference>() { +}; - java.util.concurrent.Future result = client.subscribe(eventTypeName, java.util.Optional.of(cursor), listener, typeRef); +java.util.concurrent.Future result = client.subscribe(eventTypeName, java.util.Optional.of(cursor), listener, typeRef); - result.get(); +//result.get(); } } From d428d862bcb79886e4b8a8f935c751b695d54a6e Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Fri, 20 May 2016 09:46:18 +0200 Subject: [PATCH 052/183] techjira:LAAS-60 fixes #35, #31 --- .../nakadi/client/java/ClientError.java | 15 +++- .../zalando/nakadi/client/scala/Client.scala | 4 +- .../nakadi/client/java/ClientImpl.java | 1 - .../client/actor/EventConsumingActor.scala | 9 +- .../client/actor/EventReceivingActor.scala | 11 ++- .../nakadi/client/scala/Connection.scala | 87 +++++++++++-------- .../nakadi/client/scala/HttpFactory.scala | 32 +++++-- .../nakadi/client/scala/MessageHandler.scala | 28 ++++-- .../nakadi/client/utils/ClientBuilder.scala | 23 +++-- .../client/utils/GeneralConversions.scala | 15 ++-- .../nakadi/client/utils/ModelConverter.scala | 14 +-- ...onsTest.scala => ModelConverterTest.scala} | 2 +- .../client/examples/java/ClientExample.java | 2 +- .../examples/java/EventCreationExample.java | 7 +- .../examples/java/EventListenerExample.java | 58 ++++++------- .../examples/scala/EventCreationExample.scala | 26 +++--- .../examples/scala/EventListenerExample.scala | 8 +- .../nakadi/client/scala/ClientFactory.scala | 39 +++++++-- 18 files changed, 229 insertions(+), 152 deletions(-) rename client/src/test/scala/org/zalando/nakadi/client/utils/{ConversionsTest.scala => ModelConverterTest.scala} (92%) diff --git a/api/src/main/java/org/zalando/nakadi/client/java/ClientError.java b/api/src/main/java/org/zalando/nakadi/client/java/ClientError.java index dd7c920..2dda0a4 100644 --- a/api/src/main/java/org/zalando/nakadi/client/java/ClientError.java +++ b/api/src/main/java/org/zalando/nakadi/client/java/ClientError.java @@ -5,10 +5,19 @@ public class ClientError { private final String msg; private final Optional status; + private final Optional exception; - public ClientError(String msg, Optional status) { + public ClientError(String msg, Optional status, + Optional exception) { this.msg = msg; this.status = status; + this.exception = exception; + } + + public ClientError(String msg) { + this.msg = msg; + this.status = Optional.empty(); + this.exception = Optional.empty(); } public String getMsg() { @@ -19,4 +28,8 @@ public Optional getStatus() { return status; } + public Optional getException() { + return exception; + } + } \ No newline at end of file diff --git a/api/src/main/scala/org/zalando/nakadi/client/scala/Client.scala b/api/src/main/scala/org/zalando/nakadi/client/scala/Client.scala index 82d0bca..cc6dcf0 100644 --- a/api/src/main/scala/org/zalando/nakadi/client/scala/Client.scala +++ b/api/src/main/scala/org/zalando/nakadi/client/scala/Client.scala @@ -7,7 +7,7 @@ import org.zalando.nakadi.client.scala.model._ import org.zalando.nakadi.client._ import com.fasterxml.jackson.core.`type`.TypeReference -case class ClientError(msg: String, status: Option[Int]) +case class ClientError(msg: String, status: Option[Integer] = None, exception: Option[Throwable] = None) trait Client { @@ -169,7 +169,7 @@ trait Client { * @param listener - Listener to pass the event to when it is received. * @param typeRef - TypeReference/Helper for using with the Jackson-Objectmapper to deserializing the event to json. */ - def subscribe[T <: Event](eventTypeName: String, parameters: StreamParameters, listener: Listener[T], typeRef: TypeReference[EventStreamBatch[T]]): Future[Option[ClientError]] + def subscribe[T <: Event](eventTypeName: String, parameters: StreamParameters, listener: Listener[T], typeRef: TypeReference[EventStreamBatch[T]]): Future[Option[ClientError]] /** * Removes the subscription of a listener, to stop streaming events from a partition. * diff --git a/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java b/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java index 7f169df..cb3d906 100644 --- a/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java +++ b/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java @@ -29,7 +29,6 @@ public class ClientImpl implements Client { // Deserializers private final Deserializer metricsDeserializer = SerializationUtils.metricsDeserializer(); - private final Deserializer partitionDeserializer = SerializationUtils.partitionDeserializer(); // List Deserializers private final Deserializer> seqOfEventTypeDeserializer = SerializationUtils.seqOfEventTypeDeserializer(); private final Deserializer> seqOfPartitionDeserializer = SerializationUtils.seqOfPartitionDeserializer(); diff --git a/client/src/main/scala/org/zalando/nakadi/client/actor/EventConsumingActor.scala b/client/src/main/scala/org/zalando/nakadi/client/actor/EventConsumingActor.scala index 18c557b..bc711a3 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/actor/EventConsumingActor.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/actor/EventConsumingActor.scala @@ -9,7 +9,7 @@ import org.zalando.nakadi.client.scala.Listener import org.zalando.nakadi.client.scala.model.Cursor import org.zalando.nakadi.client.scala.model.Event import org.zalando.nakadi.client.scala.model.EventStreamBatch -import EventReceivingActor.NextEvent +import EventPublishingActor.NextEvent import akka.actor.Actor import akka.actor.ActorLogging import akka.actor.ActorRef @@ -35,9 +35,9 @@ import org.zalando.nakadi.client.java.model.{ Event => JEvent } * */ -class EventConsumingActor[J <: JEvent, S <: Event](url: String, +class EventConsumingActor(url: String, receivingActor: ActorRef, // - handler: EventHandler[J, S]) + handler: EventHandler) extends Actor with ActorLogging with ActorSubscriber { import ModelConverter._ var currCursor: Cursor = null @@ -58,7 +58,8 @@ class EventConsumingActor[J <: JEvent, S <: Event](url: String, log.error(err, "[EventConsumer] onError [preCursor {} currCursor {} url {}]", prevCursor, currCursor, url) context.stop(self) case OnComplete => - log.info("[EventConsumer] onComplete [preCursor {} currCursor {} url {}]", prevCursor, currCursor, url) + log.info("[EventConsumer] onComplete [preCursor {} currCursor {} url {}]", prevCursor, currCursor, url) + context.stop(self) } def request4NextEvent(cursor: Cursor) = { diff --git a/client/src/main/scala/org/zalando/nakadi/client/actor/EventReceivingActor.scala b/client/src/main/scala/org/zalando/nakadi/client/actor/EventReceivingActor.scala index dd43611..5611ed2 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/actor/EventReceivingActor.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/actor/EventReceivingActor.scala @@ -9,9 +9,13 @@ import akka.actor.ActorLogging import akka.stream.actor.ActorPublisher import akka.stream.actor.ActorPublisherMessage.Cancel import akka.stream.actor.ActorPublisherMessage.Request +import org.zalando.nakadi.client.scala.EventHandler -object EventReceivingActor { +object EventPublishingActor { + case class Subscribe(handler:EventHandler) + case class Unsubscribe(handler:EventHandler) case class NextEvent(lastReceivedCursor: Option[Cursor]) + case class Restart() case class HttpError( url: String, cursor: Option[Cursor], @@ -27,7 +31,7 @@ object EventReceivingActor { */ class EventReceivingActor(url: String) extends Actor with ActorLogging with ActorPublisher[Option[Cursor]] { - import EventReceivingActor._ + import EventPublishingActor._ val queue: Queue[Option[Cursor]] = Queue() @@ -39,14 +43,13 @@ class EventReceivingActor(url: String) extends Actor with ActorLogging with Acto case Request(cnt) => log.debug("[EventReceivingActor] Requested {} event chunks", cnt) sendEvents() - Thread.sleep(100000) case Cancel => log.debug("[EventReceivingActor] Stopping the stream of events!") context.stop(self) case HttpError(url, cursor, status, _) => log.error("[EventReceivingActor] Cursor {} on URL {} caused error {}", cursor, url, status) case e => - log.error(e.toString()) + log.error("[EventReceivingActor] an Error occurred {}",e.toString()) } def sendEvents() = while (isActive && totalDemand > 0 && !queue.isEmpty) { diff --git a/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala index 1c0d158..02839a7 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala @@ -13,12 +13,12 @@ import org.slf4j.LoggerFactory import org.zalando.nakadi.client.Deserializer import org.zalando.nakadi.client.Serializer import org.zalando.nakadi.client.actor.EventConsumingActor -import org.zalando.nakadi.client.actor.EventReceivingActor -import org.zalando.nakadi.client.actor.EventReceivingActor.NextEvent +import org.zalando.nakadi.client.actor.EventPublishingActor +import org.zalando.nakadi.client.actor.EventPublishingActor.NextEvent import org.zalando.nakadi.client.scala.model.Cursor import org.zalando.nakadi.client.scala.model.Event import org.zalando.nakadi.client.scala.model.EventStreamBatch -import org.zalando.nakadi.client.java.model.{Event => JEvent} +import org.zalando.nakadi.client.java.model.{ Event => JEvent } import org.zalando.nakadi.client.utils.FutureConversions import com.typesafe.scalalogging.Logger import akka.NotUsed @@ -49,16 +49,21 @@ import javax.net.ssl.SSLContext import javax.net.ssl.TrustManager import javax.net.ssl.X509TrustManager import org.zalando.nakadi.client.utils.ModelConverter +import akka.actor.ActorRef +import org.reactivestreams.Publisher +import org.reactivestreams.Subscriber +import org.zalando.nakadi.client.actor.EventReceivingActor +import java.util.concurrent.TimeoutException -trait JEmptyEvent extends JEvent -trait SEmptyEvent extends Event +trait EmptyJavaEvent extends JEvent +trait EmptyScalaEvent extends Event trait Connection extends HttpFactory { - + //Connection details def host: String def port: Int - def tokenProvider(): TokenProvider + def tokenProvider(): Option[TokenProvider] def connection(): Flow[HttpRequest, HttpResponse, Future[Http.OutgoingConnection]] def get(endpoint: String): Future[HttpResponse] @@ -105,7 +110,7 @@ object Connection { /** * Creates a new Connection */ - def newConnection(host: String, port: Int, tokenProvider: () => String, securedConnection: Boolean, verifySSlCertificate: Boolean): Connection = + def newConnection(host: String, port: Int, tokenProvider: Option[() => String], securedConnection: Boolean, verifySSlCertificate: Boolean): Connection = new ConnectionImpl(host, port, tokenProvider, securedConnection, verifySSlCertificate) } @@ -113,7 +118,7 @@ object Connection { * Class for handling the basic http calls. */ -sealed class ConnectionImpl(val host: String, val port: Int, val tokenProvider: () => String, securedConnection: Boolean, verifySSlCertificate: Boolean) extends Connection { +sealed class ConnectionImpl(val host: String, val port: Int, val tokenProvider: Option[() => String], securedConnection: Boolean, verifySSlCertificate: Boolean) extends Connection { import Connection._ type HttpFlow[T] = Flow[(HttpRequest, T), (Try[HttpResponse], T), HostConnectionPool] @@ -231,42 +236,50 @@ sealed class ConnectionImpl(val host: String, val port: Int, val tokenProvider: def stop(): Future[Terminated] = actorSystem.terminate() def subscribe[T <: Event](url: String, cursor: Option[Cursor], listener: Listener[T])(implicit des: Deserializer[EventStreamBatch[T]]) = { - val msgHandler:EventHandler[JEmptyEvent, T] = new EventHandler[JEmptyEvent, T](None,Option((des,listener))) - handleSubscription(url, cursor, msgHandler) + val eventHandler: EventHandler = new EventHandlerImpl[EmptyJavaEvent, T](None, Option((des, listener))) + handleSubscription(url, cursor, eventHandler) } - private def handleSubscription[J <: JEvent, S <: Event](url: String, cursor: Option[Cursor], msgHandler: EventHandler[J, S] ) = { - logger.info("Handler [{}] cursor {} uri [{}]", msgHandler.id, cursor, url) - - - import EventReceivingActor._ + private def handleSubscription[J <: JEvent, S <: Event](url: String, cursor: Option[Cursor], eventHandler: EventHandler) = { + logger.info("Handler [{}] cursor {} uri [{}]", eventHandler.id, cursor, url) + + import EventPublishingActor._ //Create the Actors - val eventReceiver = actorSystem.actorOf(Props(classOf[EventReceivingActor], url)) - val receiver = ActorPublisher[Option[Cursor]](eventReceiver) + val publishingActor = actorSystem.actorOf(Props(classOf[EventReceivingActor], url)) + val publisher = ActorPublisher[Option[Cursor]](publishingActor) + + val subscribingActor = actorSystem.actorOf(Props(classOf[EventConsumingActor], url, publishingActor, eventHandler)) + val subscriber = ActorSubscriber[ByteString](subscribingActor) + + val pipeline = createPipeline(publisher, subscriber, url, eventHandler) - val eventConsumer = actorSystem.actorOf(Props(classOf[EventConsumingActor[J,S]], url,eventReceiver, msgHandler)) - val consumer = ActorSubscriber[ByteString](eventConsumer) + //Start + publishingActor ! NextEvent(cursor) + } - //Setup the pipeline flow - val pipeline = Flow[Option[Cursor]].via(requestCreator(url)) + private def createPipeline(publisher: Publisher[Option[Cursor]], subscriber: Subscriber[ByteString], url: String, eventHandler: EventHandler) = { + //Setup a flow for the request + val requestFlow = Flow[Option[Cursor]].via(requestCreator(url)) .via(connection) .buffer(RECEIVE_BUFFER_SIZE, OverflowStrategy.backpressure) .via(requestRenderer) - //Put all pieces together - val result= Source.fromPublisher(receiver) - .via(pipeline) - .runForeach(_.runWith(Sink.fromSubscriber(consumer))) - - result.onFailure{ - case error => - logger.error(error.getMessage) + //create the pipeline + val result = Source.fromPublisher(publisher) + .via(requestFlow) + .runForeach(_.runWith(Sink.fromSubscriber(subscriber))) + + result.onFailure { + case exception:TimeoutException => + logger.error("Received an Exception timeout, restarting the client {}",exception.getMessage) + eventHandler.handleError(url, None, exception) } - - - - //HOPAAA!! - eventReceiver ! NextEvent(cursor) + + result + } + + private def start() = { + } private def requestCreator(url: String): Flow[Option[Cursor], HttpRequest, NotUsed] = @@ -287,8 +300,8 @@ sealed class ConnectionImpl(val host: String, val port: Int, val tokenProvider: import ModelConverter._ val scalaCursor = toScalaCursor(cursor) val scalaListener = toScalaListener(listener) - val msgHandler:EventHandler[T, SEmptyEvent] = new EventHandler[T, SEmptyEvent](Option((des,listener)),None) - handleSubscription(url, scalaCursor, msgHandler) + val handler: EventHandler = new EventHandlerImpl[T, EmptyScalaEvent](Option((des, listener)), None) + handleSubscription(url, scalaCursor, handler) } } diff --git a/client/src/main/scala/org/zalando/nakadi/client/scala/HttpFactory.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/HttpFactory.scala index 8352c06..20838b7 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/scala/HttpFactory.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/scala/HttpFactory.scala @@ -40,18 +40,27 @@ trait HttpFactory { url + urlParams } - def withHttpRequest(url: String, httpMethod: HttpMethod, additionalHeaders: Seq[HttpHeader], tokenProvider: TokenProvider, params: Option[StreamParameters]): HttpRequest = { - val allHeaders: Seq[HttpHeader] = additionalHeaders :+ headers.Authorization(OAuth2BearerToken(tokenProvider())) + def withHttpRequest(url: String, httpMethod: HttpMethod, additionalHeaders: Seq[HttpHeader], tokenProvider: Option[TokenProvider], params: Option[StreamParameters]): HttpRequest = { + val allHeaders: Seq[HttpHeader] = tokenProvider match { + case None => additionalHeaders + case Some(token) => additionalHeaders :+ headers.Authorization(OAuth2BearerToken(token())) + } + val paramsList = withQueryParams(params) HttpRequest(uri = url, method = httpMethod).withHeaders(allHeaders) } - def withHttpRequestAndPayload(url: String, entity: String, httpMethod: HttpMethod, tokenProvider: TokenProvider): HttpRequest = { - HttpRequest(uri = url, method = httpMethod) // - .withHeaders(headers.Authorization(OAuth2BearerToken(tokenProvider())), + def withHttpRequestAndPayload(url: String, entity: String, httpMethod: HttpMethod, tokenProvider: Option[TokenProvider]): HttpRequest = { + val request = HttpRequest(uri = url, method = httpMethod) + tokenProvider match { + case None => request.withHeaders(headers.Accept(MediaRange(`application/json`))) + .withEntity(ContentType(`application/json`), entity) + case Some(token) => request.withHeaders(headers.Authorization(OAuth2BearerToken(token())), headers.Accept(MediaRange(`application/json`))) - .withEntity(ContentType(`application/json`), entity) + .withEntity(ContentType(`application/json`), entity) + } + } def withDefaultHeaders(cursor: Option[Cursor], flowId: Option[String]): Seq[HttpHeader] = { @@ -65,11 +74,16 @@ trait HttpFactory { for { (key, optional) <- parameters; value <- optional } yield RawHeader(key, value) } - def withHttpRequest(url: String, cursor: Option[Cursor], flowId: Option[String], tokenProvider: () => String): HttpRequest = { + def withHttpRequest(url: String, cursor: Option[Cursor], flowId: Option[String], tokenProvider: Option[TokenProvider]): HttpRequest = { val customHeaders = withDefaultHeaders(cursor, flowId) :+ RawHeader("Accept", "application/x-json-stream") - val tokenHeader = headers.Authorization(OAuth2BearerToken(tokenProvider())) - val allHeaders: Seq[HttpHeader] = customHeaders :+ tokenHeader + + val allHeaders = tokenProvider match { + case None => customHeaders + case Some(token) => customHeaders :+ headers.Authorization(OAuth2BearerToken(token())) + } + HttpRequest(uri = url, method = HttpMethods.GET).withHeaders(allHeaders) } + } diff --git a/client/src/main/scala/org/zalando/nakadi/client/scala/MessageHandler.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/MessageHandler.scala index 8fda573..135922a 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/scala/MessageHandler.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/scala/MessageHandler.scala @@ -12,14 +12,13 @@ import org.zalando.nakadi.client.java.{ ClientError => JClientError } import org.zalando.nakadi.client.scala.model.Cursor import org.zalando.nakadi.client.scala.model.Event import org.zalando.nakadi.client.scala.model.EventStreamBatch -import org.zalando.nakadi.client.utils.ModelConverter +import org.zalando.nakadi.client.utils.ModelConverter._ import java.util.{ List => JList } import java.util.Optional import org.slf4j.LoggerFactory import com.typesafe.scalalogging.Logger private object EventHandler { - import ModelConverter._ def transformScala[S <: Event](message: String, des: Deserializer[EventStreamBatch[S]]): Either[ErrorResult, ScalaResult[S]] = Try(des.from(message)) match { case Success(eventBatch) => eventBatch match { @@ -39,6 +38,7 @@ private object EventHandler { } } } + trait Result case class JavaResult[J <: JEvent]( @@ -51,7 +51,13 @@ case class ScalaResult[S <: Event]( scalaCursor: Option[Cursor]) extends Result case class ErrorResult(error: Throwable) extends Result -class EventHandler[J <: JEvent, S <: Event](java: Option[(Deserializer[JEventStreamBatch[J]], JListener[J])], scala: Option[(Deserializer[EventStreamBatch[S]], Listener[S])]) { +trait EventHandler { + def id(): String + def handle(url: String, msg: String): Either[ErrorResult, Option[Cursor]] + def handleError(url: String, msg: Option[String], exception: Throwable) +} + +class EventHandlerImpl[J <: JEvent, S <: Event](java: Option[(Deserializer[JEventStreamBatch[J]], JListener[J])], scala: Option[(Deserializer[EventStreamBatch[S]], Listener[S])]) extends EventHandler { import EventHandler._ val log = Logger(LoggerFactory.getLogger(this.getClass)) @@ -97,9 +103,19 @@ class EventHandler[J <: JEvent, S <: Event](java: Option[(Deserializer[JEventStr Left(ErrorResult(createException(errorMsg))) } } - - def handleError(msg:String, error:Throwable)={ - + + def handleError(url: String, msg: Option[String], exception: Throwable) = { + val errorMsg = if (msg.isDefined) msg.get else exception.getMessage + val clientError = Some(ClientError(errorMsg, exception = Some(exception))) + (java, scala) match { + case (Some((des, listener)), _) => // Java + listener.onError(errorMsg, toJavaClientError(clientError)) + case (_, Some((des, listener))) => //Scala + listener.onError(errorMsg, clientError) + case _ => + val errorMsg = s"Could not find a listener to pass the error, url [$url] msg [$msg]" + log.error(errorMsg) + } } } diff --git a/client/src/main/scala/org/zalando/nakadi/client/utils/ClientBuilder.scala b/client/src/main/scala/org/zalando/nakadi/client/utils/ClientBuilder.scala index d30ea6c..8b27daf 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/utils/ClientBuilder.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/utils/ClientBuilder.scala @@ -5,22 +5,21 @@ import org.zalando.nakadi.client.scala.ClientImpl import org.zalando.nakadi.client.scala.Connection import java.util.function.Supplier - object ClientBuilder { def apply( host: String = null, port: Int = DEFAULT_PORT, - tokenProvider: () => String = null, + tokenProvider: Option[() => String] = None, securedConnection: Boolean = true, verifySSlCertificate: Boolean = true) = new ClientBuilder(host, port, tokenProvider, securedConnection, verifySSlCertificate) private val DEFAULT_PORT = 443 } -class ClientBuilder private(host: String = "", // +class ClientBuilder private (host: String = "", // port: Int, // - tokenProvider: () => String = () => "", // + tokenProvider: Option[() => String] = None, // securedConnection: Boolean = true, // verifySSlCertificate: Boolean = true) { def this() = this(null, ClientBuilder.DEFAULT_PORT, null, true, true) @@ -38,14 +37,20 @@ class ClientBuilder private(host: String = "", // securedConnection, verifySSlCertificate) - def withTokenProvider(tokenProvider: () => String): ClientBuilder = new ClientBuilder( + def withTokenProvider(tokenProvider: Option[() => String]): ClientBuilder = new ClientBuilder( host, port, - checkNotNull(tokenProvider), + tokenProvider, securedConnection, verifySSlCertificate) - def withTokenProvider4Java(tokenProvider: Supplier[String]): ClientBuilder = withTokenProvider(() => tokenProvider.get()) + def withTokenProvider4Java(tokenProvider: Supplier[String]): ClientBuilder = withTokenProvider { + if (tokenProvider == null) { + None + } else { + Option(() => tokenProvider.get()) + } + } def withSecuredConnection(securedConnection: Boolean = true): ClientBuilder = new ClientBuilder( host, @@ -63,8 +68,8 @@ class ClientBuilder private(host: String = "", // def build(): Client = new ClientImpl(Connection.newConnection(host, port, tokenProvider, securedConnection, verifySSlCertificate), "UTF-8") - def buildJavaClient():org.zalando.nakadi.client.java.Client = { - val connection =Connection.newConnection(host, port, tokenProvider, securedConnection, verifySSlCertificate) + def buildJavaClient(): org.zalando.nakadi.client.java.Client = { + val connection = Connection.newConnection(host, port, tokenProvider, securedConnection, verifySSlCertificate) new org.zalando.nakadi.client.java.ClientImpl(connection) } diff --git a/client/src/main/scala/org/zalando/nakadi/client/utils/GeneralConversions.scala b/client/src/main/scala/org/zalando/nakadi/client/utils/GeneralConversions.scala index 6c694a5..89179fe 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/utils/GeneralConversions.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/utils/GeneralConversions.scala @@ -2,23 +2,24 @@ package org.zalando.nakadi.client.utils import java.util.Optional import scala.collection.JavaConversions._ +import scala.language.implicitConversions object GeneralConversions { - // def fromOptinalSequence[T](in:Option[Seq[T]]):Optional[List[T]]={ - // - // } - - def fromOptional[T](in: Option[T]) = in match { + def fromOptional[T](in: Option[T]) = in match { case None => Optional.empty() case Some(value) => Optional.of(value) } - def fromOption[T](in: Option[T]): Optional[T] = in match { + def fromOption[T](in: Option[T]): Optional[T] = in match { case None => Optional.empty() case Some(value) => Optional.of(value) } - def fromOptionOfSeq[T](in: Option[Seq[T]]): Optional[java.util.List[T]] = in match { +// def fromOption[IN, OUT](in: Option[IN])(implicit conversion: IN => OUT): Optional[OUT] = in match { +// case None => Optional.empty() +// case Some(value) => Optional.of(conversion(value)) +// } + def fromOptionOfSeq[T](in: Option[Seq[T]]): Optional[java.util.List[T]] = in match { case None => Optional.empty() case Some(seq: Seq[T]) => Optional.of(seqAsJavaList(seq)) } diff --git a/client/src/main/scala/org/zalando/nakadi/client/utils/ModelConverter.scala b/client/src/main/scala/org/zalando/nakadi/client/utils/ModelConverter.scala index 783e3cd..0bdf498 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/utils/ModelConverter.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/utils/ModelConverter.scala @@ -13,7 +13,10 @@ import scala.collection.JavaConversions._ import org.slf4j.LoggerFactory import com.typesafe.scalalogging.Logger import org.zalando.nakadi.client.scala.model.EventStreamBatch + object ModelConverter { + import GeneralConversions._ + def toScalaCursor(in: Optional[JCursor]): Option[Cursor] = { if (in.isPresent()) { toScalaCursor(in.get) @@ -21,11 +24,13 @@ object ModelConverter { None } } + def toScalaCursor(in: JCursor): Option[Cursor] = Option(in) match { case None => None case Some(c) => Some(Cursor(c.getPartition, c.getOffset)) } + def toJavaCursor(in: Cursor): JCursor = in match { case Cursor(partition, offset) => new JCursor(partition, offset) @@ -40,7 +45,7 @@ object ModelConverter { } } - def getScalaCursor[T <: JEvent](in: JEventStreamBatch[T]): Option[Cursor] = { + def toScalaCursor[T <: JEvent](in: JEventStreamBatch[T]): Option[Cursor] = { in.getCursor Option(in) match { case None => None @@ -48,7 +53,7 @@ object ModelConverter { } } - def getJavaEvents[T <: JEvent](in: JEventStreamBatch[T]): Option[java.util.List[T]] = { + def toJavaEvents[T <: JEvent](in: JEventStreamBatch[T]): Option[java.util.List[T]] = { Option(in) match { case None => None case Some(in) => Option(in.getEvents) @@ -56,9 +61,8 @@ object ModelConverter { } def toJavaClientError(error: Option[ClientError]): Optional[JClientError] = error match { - case Some(ClientError(msg, Some(httpStatusCode))) => Optional.of(new JClientError(msg, Optional.of(httpStatusCode))) - case Some(ClientError(msg, None)) => Optional.of(new JClientError(msg, Optional.empty())) - case None => Optional.empty() + case Some(ClientError(msg, httpStatusCodeOpt, exceptionOpt)) => Optional.of(new JClientError(msg, fromOption(httpStatusCodeOpt), fromOption(exceptionOpt))) + case None => Optional.empty() } private def createListenerWrapper[T <: org.zalando.nakadi.client.java.model.Event, B <: Event](in: org.zalando.nakadi.client.java.Listener[T]): Listener[T] = { diff --git a/client/src/test/scala/org/zalando/nakadi/client/utils/ConversionsTest.scala b/client/src/test/scala/org/zalando/nakadi/client/utils/ModelConverterTest.scala similarity index 92% rename from client/src/test/scala/org/zalando/nakadi/client/utils/ConversionsTest.scala rename to client/src/test/scala/org/zalando/nakadi/client/utils/ModelConverterTest.scala index ef7c899..cb5fbf0 100644 --- a/client/src/test/scala/org/zalando/nakadi/client/utils/ConversionsTest.scala +++ b/client/src/test/scala/org/zalando/nakadi/client/utils/ModelConverterTest.scala @@ -24,7 +24,7 @@ import akka.http.scaladsl.model.HttpEntity import akka.http.scaladsl.model.ContentTypes import org.zalando.nakadi.client.scala.model.JacksonJsonMarshaller -class ConversionsTest extends WordSpec with Matchers with MockitoSugar { +class ModelConverterTest extends WordSpec with Matchers with MockitoSugar { "Conversions" should { "be implemented" in { diff --git a/it/src/main/java/org/zalando/nakadi/client/examples/java/ClientExample.java b/it/src/main/java/org/zalando/nakadi/client/examples/java/ClientExample.java index 8b42322..b14d08f 100644 --- a/it/src/main/java/org/zalando/nakadi/client/examples/java/ClientExample.java +++ b/it/src/main/java/org/zalando/nakadi/client/examples/java/ClientExample.java @@ -12,7 +12,7 @@ import org.zalando.nakadi.client.utils.ClientBuilder; public class ClientExample { - private static final String token = ClientFactory.getToken(); + private static final String token = ""; private static Optional> unwrap( Future>> result) diff --git a/it/src/main/java/org/zalando/nakadi/client/examples/java/EventCreationExample.java b/it/src/main/java/org/zalando/nakadi/client/examples/java/EventCreationExample.java index 6586c46..46e28ce 100644 --- a/it/src/main/java/org/zalando/nakadi/client/examples/java/EventCreationExample.java +++ b/it/src/main/java/org/zalando/nakadi/client/examples/java/EventCreationExample.java @@ -89,12 +89,7 @@ public static void main(String[] args) throws InterruptedException, /** * Create client */ - final Client client = new ClientBuilder()// - .withHost(ClientFactory.host())// - .withSecuredConnection(true) // s - .withVerifiedSslCertificate(false) // s - .withTokenProvider4Java(() -> ClientFactory.getToken())// - .buildJavaClient(); + final Client client = ClientFactory.getJavaClient(); /** * nakadi needs to know what kind of Json-schema you are going to send to diff --git a/it/src/main/java/org/zalando/nakadi/client/examples/java/EventListenerExample.java b/it/src/main/java/org/zalando/nakadi/client/examples/java/EventListenerExample.java index 8df2850..f26edb3 100644 --- a/it/src/main/java/org/zalando/nakadi/client/examples/java/EventListenerExample.java +++ b/it/src/main/java/org/zalando/nakadi/client/examples/java/EventListenerExample.java @@ -13,34 +13,32 @@ public class EventListenerExample { - /** - * Implement the Listener interface - */ - - public static void main(String[] args) throws InterruptedException, ExecutionException { - /** - * Create client - */ - final Client client = new ClientBuilder()// - .withHost(ClientFactory.host())// - .withSecuredConnection(true) // s - .withVerifiedSslCertificate(false) // s - .withTokenProvider4Java(() -> ClientFactory.getToken())// - .buildJavaClient(); - -/** - * Initialize our Listener - */ -Listener listener = new EventCounterListener("Java-Test"); -Cursor cursor = new Cursor("0", "BEGIN"); - -String eventTypeName = "MeetingsEvent-example-E"; -TypeReference> typeRef = new TypeReference>() { -}; - -java.util.concurrent.Future result = client.subscribe(eventTypeName, java.util.Optional.of(cursor), listener, typeRef); - -//result.get(); - - } + /** + * Implement the Listener interface + */ + + public static void main(String[] args) throws InterruptedException, + ExecutionException { + /** + * Create client + */ + final Client client = ClientFactory.getJavaClient(); + + /** + * Initialize our Listener + */ + Listener listener = new EventCounterListener("Java-Test"); + Cursor cursor = new Cursor("0", "BEGIN"); + + String eventTypeName = "Event-example-with-0-messages"; + TypeReference> typeRef = new TypeReference>() { + }; + + java.util.concurrent.Future result = client + .subscribe(eventTypeName, java.util.Optional.of(cursor), + listener, typeRef); + + result.get(); + + } } diff --git a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala index 5d7a604..8ada3d4 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala @@ -19,13 +19,7 @@ object EventCreationExample extends App { import org.zalando.nakadi.client.utils.ClientBuilder import org.zalando.nakadi.client.scala.Client - val client: Client = ClientBuilder() - .withHost(ClientFactory.host()) - .withSecuredConnection(true) //s - .withVerifiedSslCertificate(false) //s - .withTokenProvider(ClientFactory.OAuth2Token()) // - // .build(); - .build() + val client: Client = ClientFactory.getScalaClient() // 2. Create a simple Meeting Event class case class MeetingsEvent(date: String, topic: String) extends Event @@ -72,17 +66,17 @@ object EventCreationExample extends App { import JacksonJsonMarshaller._ client.createEventType(eventType) - // Thread.sleep(10000) + Thread.sleep(1000) // 4. Publish the EventType - // val event = new MeetingsEvent("2016-04-28T13:28:15+00:00", "Hackaton") -// var events = for { -// a <- 1 to 49999 -// } yield MeetingsEvent("2016-04-28T13:28:15+00:00", "Hackaton" + a) -// -// // Thread.sleep(1000) -// -// Await.result(client.publishEvents(eventTypeName, events), 120.seconds) + val event = new MeetingsEvent("2016-04-28T13:28:15+00:00", "Hackaton") + var events = for { + a <- 1 to 10000 + } yield MeetingsEvent("2016-04-28T13:28:15+00:00", "Hackaton" + a) + + // Thread.sleep(1000) + + Await.result(client.publishEvents(eventTypeName, events), 120.seconds) client.stop() } \ No newline at end of file diff --git a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala index f452c25..d08f11b 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala @@ -37,13 +37,7 @@ object EventListenerExample extends App { /** * Create our client */ - val client: Client = ClientBuilder() - .withHost(ClientFactory.host()) - .withSecuredConnection(true) //s - .withVerifiedSslCertificate(false) //s - .withTokenProvider(ClientFactory.OAuth2Token()) // - // .build(); - .build() + val client: Client = ClientFactory.getScalaClient() /** * Initialize our Listener diff --git a/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala b/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala index 05bb34f..bec3d9c 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala @@ -1,12 +1,39 @@ package org.zalando.nakadi.client.scala + +import org.zalando.nakadi.client.utils.ClientBuilder +import java.util.function.Supplier + object ClientFactory { import sys.process._ import scala.language.postfixOps - def host():String = "nakadi-sandbox.my-test.fernan.do" - def OAuth2Token(): () => String = () => "" - def getToken():String = OAuth2Token().apply() - def port():Integer = 443 - def connection():Connection = Connection.newConnection(host, port, OAuth2Token(), true, false) - def client():Client = new ClientImpl(connection, "UTF-8") + def host(): String = "nakadi-sandbox.aruha-test.zalan.do" + def localHost(): String = "localhost" + def localPort(): Integer = 8080 + def OAuth2Token(): Option[() => String] = Option(() => "5375a46c-9195-4a01-90bc-5e304eb7fae8") + def port(): Integer = 443 + def connection(): Connection = Connection.newConnection(host, port, OAuth2Token(), true, false) + def client(): Client = new ClientImpl(connection, "UTF-8") + + def getJavaClient() = + defaultBuilder().buildJavaClient(); + + def getScalaClient() = defaultBuilder().build() + private def defaultBuilder() = { + connect2Default() + } + private def connect2Local() = { + new ClientBuilder() // + .withHost(ClientFactory.localHost()) // + .withPort(ClientFactory.localPort()) // + .withSecuredConnection(false) // s + .withVerifiedSslCertificate(false) // s + } + private def connect2Default() = { + ClientBuilder() + .withHost(ClientFactory.host()) + .withSecuredConnection(true) //s + .withVerifiedSslCertificate(false) //s + .withTokenProvider(ClientFactory.OAuth2Token()) + } } \ No newline at end of file From b9c8d8b76866ee61f8c9535f4d00488ec7acc565 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Fri, 20 May 2016 16:43:39 +0200 Subject: [PATCH 053/183] techjira:LAAS-60 Todo: Tests --- .../zalando/nakadi/client/java/Client.java | 7 +- .../zalando/nakadi/client/scala/Client.scala | 2 +- .../nakadi/client/java/ClientImpl.java | 38 ++--- .../nakadi/client/java/ClientHandler.scala | 120 ++++++++++++++ .../model/JavaJacksonJsonMarshaller.scala | 2 - .../nakadi/client/scala/ClientHandler.scala | 72 ++++++++ .../nakadi/client/scala/ClientImpl.scala | 13 +- .../nakadi/client/scala/Connection.scala | 156 ++++-------------- .../nakadi/client/scala/HttpFactory.scala | 7 +- .../nakadi/client/utils/ClientBuilder.scala | 21 +-- .../client/utils/GeneralConversions.scala | 16 +- .../nakadi/client/utils/ModelConverter.scala | 58 ++++--- .../client/scala/ClientHandlerTest.scala | 51 ++++++ .../nakadi/client/scala/ClientTest.scala | 8 +- .../examples/java/EventCreationExample.java | 1 - .../examples/java/EventListenerExample.java | 24 ++- .../examples/scala/EventCreationExample.scala | 18 +- .../nakadi/client/scala/ClientFactory.scala | 18 +- 18 files changed, 391 insertions(+), 241 deletions(-) create mode 100644 client/src/main/scala/org/zalando/nakadi/client/java/ClientHandler.scala create mode 100644 client/src/main/scala/org/zalando/nakadi/client/scala/ClientHandler.scala create mode 100644 client/src/test/scala/org/zalando/nakadi/client/scala/ClientHandlerTest.scala diff --git a/api/src/main/java/org/zalando/nakadi/client/java/Client.java b/api/src/main/java/org/zalando/nakadi/client/java/Client.java index 3a9a9e4..db4116a 100644 --- a/api/src/main/java/org/zalando/nakadi/client/java/Client.java +++ b/api/src/main/java/org/zalando/nakadi/client/java/Client.java @@ -9,7 +9,6 @@ import org.zalando.nakadi.client.java.enumerator.EventEnrichmentStrategy; import org.zalando.nakadi.client.java.enumerator.EventValidationStrategy; import org.zalando.nakadi.client.java.enumerator.PartitionStrategy; -import org.zalando.nakadi.client.java.model.Cursor; import org.zalando.nakadi.client.java.model.Event; import org.zalando.nakadi.client.java.model.EventStreamBatch; import org.zalando.nakadi.client.java.model.EventType; @@ -144,7 +143,7 @@ public interface Client { * @param listener Listener to pass the event to when it is received. * @return Void in case of success */ - Future subscribe(String eventTypeName, Optional cursor, Listener listener, Deserializer> deserializer); + Future subscribe(String eventTypeName, StreamParameters parameters, Listener listener, Deserializer> deserializer); /** * Registers the subscription of a listener to start streaming events from a partition in non-blocking fashion. * @param eventTypeName The unique name (id) of the EventType target @@ -152,12 +151,12 @@ public interface Client { * @param typeRef TypeReference for unmarshalling with the Jackson ObjectMapper. * @return Void in case of success */ - Future subscribe(String eventTypeName, Optional cursor, Listener listener, TypeReference> typeRef); + Future subscribe(String eventTypeName, StreamParameters parameters, Listener listener, TypeReference> typeRef); /** * Removes the subscription of a listener, to stop streaming events from a partition. * @param eventTypeName The unique name (id) of the EventType target * @param listener Listener to pass the event to when it is received. * @return Void in case of success */ - Future unsubscribe(String eventTypeName, Listener listener); + Future> unsubscribe(String eventTypeName, Listener listener); } \ No newline at end of file diff --git a/api/src/main/scala/org/zalando/nakadi/client/scala/Client.scala b/api/src/main/scala/org/zalando/nakadi/client/scala/Client.scala index cc6dcf0..97a10b6 100644 --- a/api/src/main/scala/org/zalando/nakadi/client/scala/Client.scala +++ b/api/src/main/scala/org/zalando/nakadi/client/scala/Client.scala @@ -9,7 +9,7 @@ import com.fasterxml.jackson.core.`type`.TypeReference case class ClientError(msg: String, status: Option[Integer] = None, exception: Option[Throwable] = None) -trait Client { + trait Client { /** * Retrieve monitoring metrics diff --git a/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java b/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java index cb3d906..f98d9f4 100644 --- a/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java +++ b/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java @@ -10,14 +10,12 @@ import org.zalando.nakadi.client.java.enumerator.EventEnrichmentStrategy; import org.zalando.nakadi.client.java.enumerator.EventValidationStrategy; import org.zalando.nakadi.client.java.enumerator.PartitionStrategy; -import org.zalando.nakadi.client.java.model.Cursor; import org.zalando.nakadi.client.java.model.Event; import org.zalando.nakadi.client.java.model.EventStreamBatch; import org.zalando.nakadi.client.java.model.EventType; import org.zalando.nakadi.client.java.model.Metrics; import org.zalando.nakadi.client.java.model.Partition; import org.zalando.nakadi.client.java.utils.SerializationUtils; -import org.zalando.nakadi.client.scala.Connection; import org.zalando.nakadi.client.utils.Uri; import sun.reflect.generics.reflectiveObjects.NotImplementedException; @@ -25,7 +23,7 @@ import com.fasterxml.jackson.core.type.TypeReference; public class ClientImpl implements Client { - private final Connection connection; + private final ClientHandler handler; // Deserializers private final Deserializer metricsDeserializer = SerializationUtils.metricsDeserializer(); @@ -41,30 +39,30 @@ public class ClientImpl implements Client { private final Serializer eventTypeSerializer = SerializationUtils.defaultSerializer(); private final Deserializer eventTypeDeserializer = SerializationUtils.eventTypeDeserializer(); - public ClientImpl(Connection connection) { - this.connection = connection; + public ClientImpl(ClientHandler handler) { + this.handler = handler; } @Override public Future> getMetrics() { - return connection.get4Java(Uri.URI_METRICS(), metricsDeserializer); + return handler.get4Java(Uri.URI_METRICS(), metricsDeserializer); } @Override public Future>> getEventTypes() { - return connection.get4Java(Uri.URI_EVENT_TYPES(), seqOfEventTypeDeserializer); + return handler.get4Java(Uri.URI_EVENT_TYPES(), seqOfEventTypeDeserializer); } @Override public Future createEventType(EventType eventType) { - return connection.post4Java(Uri.URI_EVENT_TYPES(), eventType, eventTypeSerializer); + return handler.post4Java(Uri.URI_EVENT_TYPES(), eventType, eventTypeSerializer); } @Override public Future> getEventType(String eventTypeName) { - return connection.get4Java(Uri.getEventTypeByName(eventTypeName), eventTypeDeserializer); + return handler.get4Java(Uri.getEventTypeByName(eventTypeName), eventTypeDeserializer); } @Override @@ -89,7 +87,7 @@ public Future publishEvent(String eventTypeName, T event @Override public Future publishEvents(String eventTypeName, List events, Serializer> serializer) { - return connection.post4Java(Uri.getEventStreamingUri(eventTypeName), events, serializer); + return handler.post4Java(Uri.getEventStreamingUri(eventTypeName), events, serializer); } @Override @@ -99,23 +97,23 @@ public Future publishEvents(String eventTypeName, List>> getPartitions(String eventTypeName) { - return connection.get4Java(Uri.getPartitions(eventTypeName), seqOfPartitionDeserializer); + return handler.get4Java(Uri.getPartitions(eventTypeName), seqOfPartitionDeserializer); } @Override public Future>> getValidationStrategies() { - return connection.get4Java(Uri.URI_VALIDATION_STRATEGIES(), seqOfEventValidationStrategy); + return handler.get4Java(Uri.URI_VALIDATION_STRATEGIES(), seqOfEventValidationStrategy); } @Override public Future>> getEnrichmentStrategies() { - return connection.get4Java(Uri.URI_ENRICHMENT_STRATEGIES(), seqOfEventEnrichmentStrategy); + return handler.get4Java(Uri.URI_ENRICHMENT_STRATEGIES(), seqOfEventEnrichmentStrategy); } @Override public Future>> getPartitioningStrategies() { - return connection.get4Java(Uri.URI_PARTITIONING_STRATEGIES(), seqOfPartitionStrategy); + return handler.get4Java(Uri.URI_PARTITIONING_STRATEGIES(), seqOfPartitionStrategy); } @Override @@ -124,19 +122,19 @@ public Future stop() { } @Override - public Future subscribe(String eventTypeName, Optional cursor, Listener listener, - Deserializer> deserializer) { - return connection.subscribeJava(Uri.getEventStreamingUri(eventTypeName), cursor, listener, deserializer); + public Future subscribe(String eventTypeName, StreamParameters parameters, Listener listener, Deserializer> deserializer) { + return handler.subscribeJava(Uri.getEventStreamingUri(eventTypeName), parameters, listener, deserializer); + } @Override - public Future subscribe(String eventTypeName, Optional cursor, Listener listener, + public Future subscribe(String eventTypeName, StreamParameters parameters, Listener listener, TypeReference> typeRef) { - return connection.subscribeJava(Uri.getEventStreamingUri(eventTypeName), cursor, listener, SerializationUtils.withCustomDeserializer(typeRef)); + return handler.subscribeJava(Uri.getEventStreamingUri(eventTypeName), parameters, listener, SerializationUtils.withCustomDeserializer(typeRef)); } @Override - public Future unsubscribe(String eventTypeName, Listener listener) { + public Future> unsubscribe(String eventTypeName, Listener listener) { throw new NotImplementedException(); } diff --git a/client/src/main/scala/org/zalando/nakadi/client/java/ClientHandler.scala b/client/src/main/scala/org/zalando/nakadi/client/java/ClientHandler.scala new file mode 100644 index 0000000..25531ae --- /dev/null +++ b/client/src/main/scala/org/zalando/nakadi/client/java/ClientHandler.scala @@ -0,0 +1,120 @@ +package org.zalando.nakadi.client.java + +import java.util.Optional + +import scala.collection.immutable.Seq +import scala.concurrent.ExecutionContext.Implicits.global +import scala.concurrent.Future +import scala.util.Failure +import scala.util.Success +import scala.util.Try + +import org.slf4j.LoggerFactory +import org.zalando.nakadi.client.Deserializer +import org.zalando.nakadi.client.Serializer +import org.zalando.nakadi.client.java.model.{ Event => JEvent } +import org.zalando.nakadi.client.scala.model.{ Cursor => ScalaCursor } +import org.zalando.nakadi.client.java.model.{ EventStreamBatch => JEventStreamBatch } +import org.zalando.nakadi.client.java.{ StreamParameters => JStreamParameters } +import org.zalando.nakadi.client.java.{ Listener => JListener } +import org.zalando.nakadi.client.scala.Connection +import org.zalando.nakadi.client.scala.EmptyScalaEvent +import org.zalando.nakadi.client.scala.EventHandler +import org.zalando.nakadi.client.scala.EventHandlerImpl +import org.zalando.nakadi.client.scala.HttpFactory +import org.zalando.nakadi.client.scala.{ StreamParameters => ScalaStreamParameters } +import org.zalando.nakadi.client.utils.FutureConversions +import org.zalando.nakadi.client.utils.ModelConverter + +import com.typesafe.scalalogging.Logger + +import akka.http.scaladsl.model.HttpHeader +import akka.http.scaladsl.model.HttpMethods +import akka.http.scaladsl.model.HttpResponse +import akka.http.scaladsl.unmarshalling.Unmarshal + +/** + * Handleer for delegating low-level http calls and listener subscriptions for the Java API. + */ +trait ClientHandler { + import HttpFactory._ + implicit val mat = connection.materializer() + + def logger(): Logger + def connection(): Connection + + def deserialize4Java[T](response: HttpResponse, des: Deserializer[T]): Future[Optional[T]] = response match { + case HttpResponse(status, headers, entity, protocol) if (status.isSuccess()) => + + Try(Unmarshal(entity).to[String].map(body => des.from(body))) match { + case Success(result) => result.map(Optional.of(_)) + case Failure(error) => throw new RuntimeException(error.getMessage) + } + + case HttpResponse(status, headers, entity, protocol) if (status.isFailure()) => + throw new RuntimeException(status.reason()) + } + + def get(endpoint: String): Future[HttpResponse] = { + logger.info("Get - URL {}", endpoint) + connection().executeCall(withHttpRequest(endpoint, HttpMethods.GET, Nil, connection().tokenProvider, None)) + } + + def get4Java[T](endpoint: String, des: Deserializer[T]): java.util.concurrent.Future[Optional[T]] = { + FutureConversions.fromFuture2Future(get(endpoint).flatMap(deserialize4Java(_, des))) + } + def get4Java[T](endpoint: String, headers: Seq[HttpHeader], des: Deserializer[T]): java.util.concurrent.Future[Optional[T]] = { + FutureConversions.fromFuture2Future(connection.executeCall(withHttpRequest(endpoint, HttpMethods.GET, headers, connection.tokenProvider, None)).flatMap(deserialize4Java(_, des))) + } + def post4Java[T](endpoint: String, model: T)(implicit serializer: Serializer[T]): java.util.concurrent.Future[Void] = { + val entity = serializer.to(model) + logger.info("Posting to endpoint {}", endpoint) + logger.debug("Data to post {}", entity) + val result = connection.executeCall( + withHttpRequestAndPayload(endpoint, serialize4Java(model), HttpMethods.POST, connection.tokenProvider)) + .flatMap(response4Java(_)) + FutureConversions.fromOption2Void(result) + } + + private def serialize4Java[T](model: T)(implicit serializer: Serializer[T]): String = + Try(serializer.to(model)) match { + case Success(result) => result + case Failure(error) => throw new RuntimeException("Failed to serialize: " + error.getMessage) + } + + private def response4Java[T](response: HttpResponse): Future[Option[String]] = response match { + case HttpResponse(status, headers, entity, protocol) if (status.isSuccess()) => + Try(Unmarshal(entity).to[String]) match { + case Success(result) => result.map(Option(_)) + case Failure(error) => throw new RuntimeException(error.getMessage) + } + + case HttpResponse(status, headers, entity, protocol) if (status.isFailure()) => + Unmarshal(entity).to[String].map { x => + val msg = "http-stats(%s) - %s - problem: %s ".format(status.intValue(), x, status.defaultMessage()) + logger.warn(msg) + throw new RuntimeException(msg) + } + } + + def subscribeJava[T <: JEvent](url: String, parameters: JStreamParameters, listener: JListener[T])(implicit des: Deserializer[JEventStreamBatch[T]]) = + FutureConversions.fromFuture2FutureVoid { + (Future { + import ModelConverter._ + val params: Option[ScalaStreamParameters] = toScalaStreamParameters(parameters) + val scalaListener = toScalaListener(listener) + val handler: EventHandler = new EventHandlerImpl[T, EmptyScalaEvent](Option((des, listener)), None) + val finalUrl = withUrl(url, params) + connection().handleSubscription(url, getCursor(params), handler) + }) + } + private def getCursor(params: Option[ScalaStreamParameters]): Option[ScalaCursor] = params match { + case Some(ScalaStreamParameters(cursor, batchLimit, streamLimit, batchFlushTimeout, streamTimeout, streamKeepAliveLimit, flowId)) => cursor + case None => None + } +} + +class ClientHandlerImpl(val connection: Connection) extends ClientHandler { + def logger(): Logger = Logger(LoggerFactory.getLogger(this.getClass)) +} + diff --git a/client/src/main/scala/org/zalando/nakadi/client/java/model/JavaJacksonJsonMarshaller.scala b/client/src/main/scala/org/zalando/nakadi/client/java/model/JavaJacksonJsonMarshaller.scala index d7f55ed..0c2c76b 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/java/model/JavaJacksonJsonMarshaller.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/java/model/JavaJacksonJsonMarshaller.scala @@ -51,7 +51,6 @@ object JavaJacksonJsonMarshaller { def optionalDeserializer[T]( expectedType: TypeReference[T]): Deserializer[Option[T]] = new Deserializer[Option[T]] { def from(from: String): Option[T] = { - logger.debug("json: {}",from) defaultObjectMapper.readValue[Option[T]](from, expectedType) } } @@ -62,7 +61,6 @@ object JavaJacksonJsonMarshaller { def deserializer[T]( expectedType: TypeReference[T]): Deserializer[T] = new Deserializer[T] { def from(from: String): T = { - logger.debug("json: {}",from) defaultObjectMapper.readValue[T](from, expectedType) } } diff --git a/client/src/main/scala/org/zalando/nakadi/client/scala/ClientHandler.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/ClientHandler.scala new file mode 100644 index 0000000..cc58801 --- /dev/null +++ b/client/src/main/scala/org/zalando/nakadi/client/scala/ClientHandler.scala @@ -0,0 +1,72 @@ +package org.zalando.nakadi.client.scala + +import scala.collection.immutable.Seq +import scala.concurrent.Future + +import org.slf4j.LoggerFactory +import org.zalando.nakadi.client.Deserializer +import org.zalando.nakadi.client.Serializer +import org.zalando.nakadi.client.scala.model.Cursor +import org.zalando.nakadi.client.scala.model.Event +import org.zalando.nakadi.client.scala.model.EventStreamBatch + +import com.typesafe.scalalogging.Logger + +import akka.actor.Terminated +import akka.http.scaladsl.model.HttpHeader +import akka.http.scaladsl.model.HttpMethods +import akka.http.scaladsl.model.HttpResponse +import akka.stream.ActorMaterializer + +/** + * Handleer for delegating low-level http calls and listener subscriptions. + */ +trait ClientHandler { + import HttpFactory._ + def logger(): Logger + + def connection(): Connection + + def shutdown(): Future[Terminated] = connection().shutdown() + + def materializer(): ActorMaterializer = connection().materializer() + + def get(endpoint: String): Future[HttpResponse] = { + logger.info("Get - URL {}", endpoint) + connection().executeCall(withHttpRequest(endpoint, HttpMethods.GET, Nil, connection().tokenProvider, None)) + } + def get(endpoint: String, headers: Seq[HttpHeader]): Future[HttpResponse] = { + logger.info("Get - URL {} - Headers {}", endpoint, headers) + connection().executeCall(withHttpRequest(endpoint, HttpMethods.GET, headers, connection().tokenProvider, None)) + } + def stream(endpoint: String, headers: Seq[HttpHeader]): Future[HttpResponse] = { + logger.info("Streaming on Get: {}", endpoint) + connection().executeCall(withHttpRequest(endpoint, HttpMethods.GET, headers, connection().tokenProvider, None)) //TODO: Change to stream single event + } + + def delete(endpoint: String): Future[HttpResponse] = { + logger.info("Delete: {}", endpoint) + connection().executeCall(withHttpRequest(endpoint, HttpMethods.DELETE, Nil, connection().tokenProvider, None)) + } + + def put[T](endpoint: String, model: T)(implicit serializer: Serializer[T]): Future[HttpResponse] = { + logger.info("Get: {}", endpoint) + connection().executeCall(withHttpRequest(endpoint, HttpMethods.GET, Nil, connection().tokenProvider, None)) + } + + def post[T](endpoint: String, model: T)(implicit serializer: Serializer[T]): Future[HttpResponse] = { + val entity = serializer.to(model) + logger.info("Posting to endpoint {}", endpoint) + logger.debug("Data to post {}", entity) + connection().executeCall(withHttpRequestAndPayload(endpoint, entity, HttpMethods.POST, connection().tokenProvider)) + } + + def subscribe[T <: Event](endpoint: String, cursor: Option[Cursor], listener: Listener[T])(implicit des: Deserializer[EventStreamBatch[T]]) = { + val eventHandler: EventHandler = new EventHandlerImpl[EmptyJavaEvent, T](None, Option((des, listener))) + connection().handleSubscription(endpoint, cursor, eventHandler) + } +} + +class ClientHandlerImpl(val connection: Connection) extends ClientHandler { + def logger(): Logger = Logger(LoggerFactory.getLogger(this.getClass)) +} \ No newline at end of file diff --git a/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala index 7055c44..d6c80e0 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala @@ -21,10 +21,11 @@ import org.zalando.nakadi.client.scala.model._ import org.zalando.nakadi.client.utils.Uri import com.fasterxml.jackson.core.`type`.TypeReference -private[client] class ClientImpl(connection: Connection, charSet: String = "UTF-8") extends Client with HttpFactory { - implicit val materializer = connection.materializer +private[scala] class ClientImpl(connection: ClientHandler, charSet: String = "UTF-8") extends Client { import Uri._ import JacksonJsonMarshaller._ + import HttpFactory._ + implicit val materializer = connection.materializer // implicit val val logger = Logger(LoggerFactory.getLogger(this.getClass)) def getMetrics(): Future[Either[ClientError, Option[Metrics]]] = { @@ -83,7 +84,7 @@ private[client] class ClientImpl(connection: Connection, charSet: String = "UTF- logFutureEither(connection.get(URI_PARTITIONING_STRATEGIES).flatMap(mapToEither(_)(deserializer(listOfPartitionStrategyTR)))) def stop(): Option[ClientError] = { - val result = Await.ready(connection.stop(), Duration.Inf) + val result = Await.ready(connection.shutdown(), Duration.Inf) None } @@ -91,7 +92,7 @@ private[client] class ClientImpl(connection: Connection, charSet: String = "UTF- subscribe(eventTypeName, parameters, listener)(deserializer(typeRef)) } - def subscribe[T <: Event](eventType: String, params: StreamParameters, listener: Listener[T])(implicit des: Deserializer[EventStreamBatch[T]]): Future[Option[ClientError]] = + def subscribe[T <: Event](eventType: String, params: StreamParameters, listener: Listener[T])(implicit des: Deserializer[EventStreamBatch[T]]): Future[Option[ClientError]] = (eventType, params, listener) match { case (_, _, listener) if listener == null => @@ -104,8 +105,8 @@ private[client] class ClientImpl(connection: Connection, charSet: String = "UTF- case (eventType, StreamParameters(cursor, _, _, _, _, _, _), listener) if Option(eventType).isDefined => val url = URI_EVENTS_OF_EVENT_TYPE.format(eventType) - logger.debug("Subscribing listener {} - cursor {} - parameters {} - eventType {} - url {}", listener.id, cursor, params, eventType,url) - val finalUrl =withUrl(url, Some(params)) + logger.debug("Subscribing listener {} - cursor {} - parameters {} - eventType {} - url {}", listener.id, cursor, params, eventType, url) + val finalUrl = withUrl(url, Some(params)) connection.subscribe(finalUrl, cursor, listener)(des) Future.successful(None) } diff --git a/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala index 02839a7..5ec33c1 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala @@ -19,6 +19,12 @@ import org.zalando.nakadi.client.scala.model.Cursor import org.zalando.nakadi.client.scala.model.Event import org.zalando.nakadi.client.scala.model.EventStreamBatch import org.zalando.nakadi.client.java.model.{ Event => JEvent } +import org.zalando.nakadi.client.java.model.{ EventStreamBatch => JEventStreamBatch } +import org.zalando.nakadi.client.java.model.{ Cursor => JCursor } +import org.zalando.nakadi.client.java.{ StreamParameters => JStreamParameters } +import org.zalando.nakadi.client.java.{ ClientHandler => JClientHandler } +import org.zalando.nakadi.client.java.{ ClientHandlerImpl => JClientHandlerImpl } +import org.zalando.nakadi.client.java.{ Listener => JListener } import org.zalando.nakadi.client.utils.FutureConversions import com.typesafe.scalalogging.Logger import akka.NotUsed @@ -55,33 +61,19 @@ import org.reactivestreams.Subscriber import org.zalando.nakadi.client.actor.EventReceivingActor import java.util.concurrent.TimeoutException -trait EmptyJavaEvent extends JEvent -trait EmptyScalaEvent extends Event - -trait Connection extends HttpFactory { +sealed trait EmptyJavaEvent extends JEvent +sealed trait EmptyScalaEvent extends Event +trait Connection { //Connection details - def host: String - def port: Int + import HttpFactory.TokenProvider def tokenProvider(): Option[TokenProvider] - def connection(): Flow[HttpRequest, HttpResponse, Future[Http.OutgoingConnection]] - - def get(endpoint: String): Future[HttpResponse] - def get(endpoint: String, headers: Seq[HttpHeader]): Future[HttpResponse] - def stream(endpoint: String, headers: Seq[HttpHeader]): Future[HttpResponse] - def delete(endpoint: String): Future[HttpResponse] - def post[T](endpoint: String, model: T)(implicit serializer: Serializer[T]): Future[HttpResponse] - def get4Java[T](endpoint: String, des: Deserializer[T]): java.util.concurrent.Future[Optional[T]] - def get4Java[T](endpoint: String, headers: Seq[HttpHeader], des: Deserializer[T]): java.util.concurrent.Future[Optional[T]] - def post4Java[T](endpoint: String, model: T)(implicit serializer: Serializer[T]): java.util.concurrent.Future[Void] - def put[T](endpoint: String, model: T)(implicit serializer: Serializer[T]): Future[HttpResponse] - def subscribe[T <: Event](url: String, cursor: Option[Cursor], listener: Listener[T])(implicit des: Deserializer[EventStreamBatch[T]]) - def subscribeJava[T <: org.zalando.nakadi.client.java.model.Event]( - url: String, cursor: Optional[org.zalando.nakadi.client.java.model.Cursor], - listener: org.zalando.nakadi.client.java.Listener[T])(implicit des: Deserializer[org.zalando.nakadi.client.java.model.EventStreamBatch[T]]): java.util.concurrent.Future[Void] + def executeCall(request: HttpRequest): Future[HttpResponse] + def handleSubscription[J <: JEvent, S <: Event](url: String, cursor: Option[Cursor], eventHandler: EventHandler) - def stop(): Future[Terminated] + def connection(): Flow[HttpRequest, HttpResponse, Future[Http.OutgoingConnection]] def materializer(): ActorMaterializer + def shutdown(): Future[Terminated] } /** @@ -108,10 +100,17 @@ object Connection { } /** - * Creates a new Connection + * Creates a new Client Handler */ - def newConnection(host: String, port: Int, tokenProvider: Option[() => String], securedConnection: Boolean, verifySSlCertificate: Boolean): Connection = - new ConnectionImpl(host, port, tokenProvider, securedConnection, verifySSlCertificate) + def newClient(host: String, port: Int, tokenProvider: Option[() => String], securedConnection: Boolean, verifySSlCertificate: Boolean): Client = { + val connection = new ConnectionImpl(host, port, tokenProvider, securedConnection, verifySSlCertificate) + val handler = new ClientHandlerImpl(connection) + new ClientImpl(handler) + } + + def newClientHandler4Java(host: String, port: Int, tokenProvider: Option[() => String], securedConnection: Boolean, verifySSlCertificate: Boolean): JClientHandler = { + new JClientHandlerImpl(new ConnectionImpl(host, port, tokenProvider, securedConnection, verifySSlCertificate)) + } } /** @@ -120,6 +119,7 @@ object Connection { sealed class ConnectionImpl(val host: String, val port: Int, val tokenProvider: Option[() => String], securedConnection: Boolean, verifySSlCertificate: Boolean) extends Connection { import Connection._ + import HttpFactory._ type HttpFlow[T] = Flow[(HttpRequest, T), (Try[HttpResponse], T), HostConnectionPool] type StepResult[T] = (T, Option[String]) @@ -141,84 +141,7 @@ sealed class ConnectionImpl(val host: String, val port: Int, val tokenProvider: http.outgoingConnection(host, port) } - def get(endpoint: String): Future[HttpResponse] = { - logger.info("Get - URL {}", endpoint) - executeCall(withHttpRequest(endpoint, HttpMethods.GET, Nil, tokenProvider, None)) - } - def get(endpoint: String, headers: Seq[HttpHeader]): Future[HttpResponse] = { - logger.info("Get - URL {} - Headers {}", endpoint, headers) - executeCall(withHttpRequest(endpoint, HttpMethods.GET, headers, tokenProvider, None)) - } - - private def deserialize4Java[T](response: HttpResponse, des: Deserializer[T]): Future[Optional[T]] = response match { - case HttpResponse(status, headers, entity, protocol) if (status.isSuccess()) => - - Try(Unmarshal(entity).to[String].map(body => des.from(body))) match { - case Success(result) => result.map(Optional.of(_)) - case Failure(error) => throw new RuntimeException(error.getMessage) - } - - case HttpResponse(status, headers, entity, protocol) if (status.isFailure()) => - throw new RuntimeException(status.reason()) - } - def stream(endpoint: String, headers: Seq[HttpHeader]): Future[HttpResponse] = { - logger.info("Streaming on Get: {}", endpoint) - executeCall(withHttpRequest(endpoint, HttpMethods.GET, headers, tokenProvider, None)) //TODO: Change to stream single event - } - - def put[T](endpoint: String, model: T)(implicit serializer: Serializer[T]): Future[HttpResponse] = { - logger.info("Get: {}", endpoint) - executeCall(withHttpRequest(endpoint, HttpMethods.GET, Nil, tokenProvider, None)) - } - - def post[T](endpoint: String, model: T)(implicit serializer: Serializer[T]): Future[HttpResponse] = { - val entity = serializer.to(model) - logger.info("Posting to endpoint {}", endpoint) - logger.debug("Data to post {}", entity) - executeCall(withHttpRequestAndPayload(endpoint, entity, HttpMethods.POST, tokenProvider)) - } - def get4Java[T](endpoint: String, des: Deserializer[T]): java.util.concurrent.Future[Optional[T]] = { - FutureConversions.fromFuture2Future(get(endpoint).flatMap(deserialize4Java(_, des))) - } - def get4Java[T](endpoint: String, headers: Seq[HttpHeader], des: Deserializer[T]): java.util.concurrent.Future[Optional[T]] = { - FutureConversions.fromFuture2Future(executeCall(withHttpRequest(endpoint, HttpMethods.GET, headers, tokenProvider, None)).flatMap(deserialize4Java(_, des))) - } - def post4Java[T](endpoint: String, model: T)(implicit serializer: Serializer[T]): java.util.concurrent.Future[Void] = { - val entity = serializer.to(model) - logger.info("Posting to endpoint {}", endpoint) - logger.debug("Data to post {}", entity) - val result = executeCall( - withHttpRequestAndPayload(endpoint, serialize4Java(model), HttpMethods.POST, tokenProvider)) - .flatMap(response4Java(_)) - FutureConversions.fromOption2Void(result) - } - - private def serialize4Java[T](model: T)(implicit serializer: Serializer[T]): String = - Try(serializer.to(model)) match { - case Success(result) => result - case Failure(error) => throw new RuntimeException("Failed to serialize: " + error.getMessage) - } - - private def response4Java[T](response: HttpResponse): Future[Option[String]] = response match { - case HttpResponse(status, headers, entity, protocol) if (status.isSuccess()) => - Try(Unmarshal(entity).to[String]) match { - case Success(result) => result.map(Option(_)) - case Failure(error) => throw new RuntimeException(error.getMessage) - } - - case HttpResponse(status, headers, entity, protocol) if (status.isFailure()) => - Unmarshal(entity).to[String].map { x => - val msg = "http-stats(%s) - %s - problem: %s ".format(status.intValue(), x, status.defaultMessage()) - logger.warn(msg) - throw new RuntimeException(msg) - } - } - def delete(endpoint: String): Future[HttpResponse] = { - logger.info("Delete: {}", endpoint) - executeCall(withHttpRequest(endpoint, HttpMethods.DELETE, Nil, tokenProvider, None)) - } - - private def executeCall(request: HttpRequest): Future[HttpResponse] = { + def executeCall(request: HttpRequest): Future[HttpResponse] = { val response: Future[HttpResponse] = Source.single(request) .via(connection). @@ -233,13 +156,9 @@ sealed class ConnectionImpl(val host: String, val port: Int, val tokenProvider: } } - def stop(): Future[Terminated] = actorSystem.terminate() + def shutdown(): Future[Terminated] = actorSystem.terminate() - def subscribe[T <: Event](url: String, cursor: Option[Cursor], listener: Listener[T])(implicit des: Deserializer[EventStreamBatch[T]]) = { - val eventHandler: EventHandler = new EventHandlerImpl[EmptyJavaEvent, T](None, Option((des, listener))) - handleSubscription(url, cursor, eventHandler) - } - private def handleSubscription[J <: JEvent, S <: Event](url: String, cursor: Option[Cursor], eventHandler: EventHandler) = { + def handleSubscription[J <: JEvent, S <: Event](url: String, cursor: Option[Cursor], eventHandler: EventHandler) = { logger.info("Handler [{}] cursor {} uri [{}]", eventHandler.id, cursor, url) import EventPublishingActor._ @@ -270,8 +189,8 @@ sealed class ConnectionImpl(val host: String, val port: Int, val tokenProvider: .runForeach(_.runWith(Sink.fromSubscriber(subscriber))) result.onFailure { - case exception:TimeoutException => - logger.error("Received an Exception timeout, restarting the client {}",exception.getMessage) + case exception: TimeoutException => + logger.error("Received an Exception timeout, restarting the client {}", exception.getMessage) eventHandler.handleError(url, None, exception) } @@ -292,19 +211,4 @@ sealed class ConnectionImpl(val host: String, val port: Int, val tokenProvider: private def delimiterFlow = Flow[ByteString] .via(Framing.delimiter(ByteString(EVENT_DELIMITER), maximumFrameLength = RECEIVE_BUFFER_SIZE, allowTruncation = true)) - def subscribeJava[T <: org.zalando.nakadi.client.java.model.Event]( - url: String, cursor: Optional[org.zalando.nakadi.client.java.model.Cursor], - listener: org.zalando.nakadi.client.java.Listener[T])(implicit des: Deserializer[org.zalando.nakadi.client.java.model.EventStreamBatch[T]]) = { - FutureConversions.fromFuture2FutureVoid { - Future { - import ModelConverter._ - val scalaCursor = toScalaCursor(cursor) - val scalaListener = toScalaListener(listener) - val handler: EventHandler = new EventHandlerImpl[T, EmptyScalaEvent](Option((des, listener)), None) - handleSubscription(url, scalaCursor, handler) - - } - } - - } } diff --git a/client/src/main/scala/org/zalando/nakadi/client/scala/HttpFactory.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/HttpFactory.scala index 20838b7..90c9a81 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/scala/HttpFactory.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/scala/HttpFactory.scala @@ -14,7 +14,10 @@ import org.slf4j.LoggerFactory import com.typesafe.scalalogging.Logger // -trait HttpFactory { +object HttpFactory { + /** + * Alias for a function that returns a string. + */ type TokenProvider = () => String def withHeaders(params: Option[StreamParameters]): Seq[HttpHeader] = { params match { @@ -76,7 +79,6 @@ trait HttpFactory { def withHttpRequest(url: String, cursor: Option[Cursor], flowId: Option[String], tokenProvider: Option[TokenProvider]): HttpRequest = { val customHeaders = withDefaultHeaders(cursor, flowId) :+ RawHeader("Accept", "application/x-json-stream") - val allHeaders = tokenProvider match { case None => customHeaders case Some(token) => customHeaders :+ headers.Authorization(OAuth2BearerToken(token())) @@ -84,6 +86,5 @@ trait HttpFactory { HttpRequest(uri = url, method = HttpMethods.GET).withHeaders(allHeaders) } - } diff --git a/client/src/main/scala/org/zalando/nakadi/client/utils/ClientBuilder.scala b/client/src/main/scala/org/zalando/nakadi/client/utils/ClientBuilder.scala index 8b27daf..f44fc96 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/utils/ClientBuilder.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/utils/ClientBuilder.scala @@ -1,9 +1,12 @@ package org.zalando.nakadi.client.utils import org.zalando.nakadi.client.scala.Client +import org.zalando.nakadi.client.java.{ Client => JClient } +import org.zalando.nakadi.client.java.{ ClientImpl => JClientImpl } import org.zalando.nakadi.client.scala.ClientImpl import org.zalando.nakadi.client.scala.Connection import java.util.function.Supplier +import com.google.common.base.Preconditions._ object ClientBuilder { @@ -17,12 +20,12 @@ object ClientBuilder { private val DEFAULT_PORT = 443 } -class ClientBuilder private (host: String = "", // +class ClientBuilder private (host: String = null, // port: Int, // tokenProvider: Option[() => String] = None, // securedConnection: Boolean = true, // verifySSlCertificate: Boolean = true) { - def this() = this(null, ClientBuilder.DEFAULT_PORT, null, true, true) + def this() = this(null, ClientBuilder.DEFAULT_PORT, None, true, true) def withHost(host: String): ClientBuilder = new ClientBuilder( checkNotNull(host), port, @@ -66,17 +69,11 @@ class ClientBuilder private (host: String = "", // securedConnection, checkNotNull(verifySSlCertificate)) - def build(): Client = new ClientImpl(Connection.newConnection(host, port, tokenProvider, securedConnection, verifySSlCertificate), "UTF-8") + def build(): Client = Connection.newClient(host, port, tokenProvider, securedConnection, verifySSlCertificate) - def buildJavaClient(): org.zalando.nakadi.client.java.Client = { - val connection = Connection.newConnection(host, port, tokenProvider, securedConnection, verifySSlCertificate) - new org.zalando.nakadi.client.java.ClientImpl(connection) + def buildJavaClient(): JClient = { + val connection = Connection.newClientHandler4Java(host, port, tokenProvider, securedConnection, verifySSlCertificate) + new JClientImpl(connection) } - private def checkNotNull[T](subject: T): T = - if (Option(subject).isEmpty) throw new NullPointerException else subject - - private def checkState[T](subject: T, predicate: (T) => Boolean, msg: String): T = - if (predicate(subject)) subject else throw new IllegalStateException() - } \ No newline at end of file diff --git a/client/src/main/scala/org/zalando/nakadi/client/utils/GeneralConversions.scala b/client/src/main/scala/org/zalando/nakadi/client/utils/GeneralConversions.scala index 89179fe..e86b2f8 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/utils/GeneralConversions.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/utils/GeneralConversions.scala @@ -6,20 +6,18 @@ import scala.language.implicitConversions object GeneralConversions { - def fromOptional[T](in: Option[T]) = in match { + def toOptional[T](in: Option[T]): Optional[T] = in match { case None => Optional.empty() case Some(value) => Optional.of(value) } - def fromOption[T](in: Option[T]): Optional[T] = in match { - case None => Optional.empty() - case Some(value) => Optional.of(value) + def toOption[T](in: Optional[T]): Option[T] = if (in.isPresent()) { + Option(in.get) + } else { + None } -// def fromOption[IN, OUT](in: Option[IN])(implicit conversion: IN => OUT): Optional[OUT] = in match { -// case None => Optional.empty() -// case Some(value) => Optional.of(conversion(value)) -// } - def fromOptionOfSeq[T](in: Option[Seq[T]]): Optional[java.util.List[T]] = in match { + + def toOptionOfSeq[T](in: Option[Seq[T]]): Optional[java.util.List[T]] = in match { case None => Optional.empty() case Some(seq: Seq[T]) => Optional.of(seqAsJavaList(seq)) } diff --git a/client/src/main/scala/org/zalando/nakadi/client/utils/ModelConverter.scala b/client/src/main/scala/org/zalando/nakadi/client/utils/ModelConverter.scala index 0bdf498..0c86b76 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/utils/ModelConverter.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/utils/ModelConverter.scala @@ -4,7 +4,9 @@ import org.zalando.nakadi.client.scala.model.Cursor import org.zalando.nakadi.client.scala.model.Event import org.zalando.nakadi.client.scala.ClientError import org.zalando.nakadi.client.scala.Listener +import org.zalando.nakadi.client.scala.StreamParameters import org.zalando.nakadi.client.java.{ ClientError => JClientError } +import org.zalando.nakadi.client.java.{ StreamParameters => JStreamParameters } import org.zalando.nakadi.client.java.model.{ Event => JEvent } import org.zalando.nakadi.client.java.model.{ EventStreamBatch => JEventStreamBatch } import org.zalando.nakadi.client.java.model.{ Cursor => JCursor } @@ -17,18 +19,33 @@ import org.zalando.nakadi.client.scala.model.EventStreamBatch object ModelConverter { import GeneralConversions._ - def toScalaCursor(in: Optional[JCursor]): Option[Cursor] = { - if (in.isPresent()) { - toScalaCursor(in.get) - } else { - None - } + def toScalaCursor(in: Optional[JCursor]): Option[Cursor] = if (in.isPresent()) { + toScalaCursor(in.get) + } else { + None } def toScalaCursor(in: JCursor): Option[Cursor] = Option(in) match { case None => None case Some(c) => Some(Cursor(c.getPartition, c.getOffset)) + } + + def toScalaStreamParameters(in: Optional[JStreamParameters]): Option[StreamParameters] = if (in.isPresent()) { + toScalaStreamParameters(in.get) + } else { + None + } + def toScalaStreamParameters(in: JStreamParameters): Option[StreamParameters] = Option(in) match { + case None => None + case Some(c) => Some(StreamParameters( + cursor = toScalaCursor(c.getCursor), + batchLimit = toOption(c.getBatchLimit), + streamLimit = toOption(c.getStreamLimit), + batchFlushTimeout = toOption(c.getBatchFlushTimeout), + streamTimeout = toOption(c.getStreamTimeout), + streamKeepAliveLimit = toOption(c.getStreamKeepAliveLimit), + flowId = toOption(c.getFlowId))) } def toJavaCursor(in: Cursor): JCursor = in match { @@ -37,31 +54,24 @@ object ModelConverter { case null => null } - def toScalaListener[T <: JEvent](in: org.zalando.nakadi.client.java.Listener[T]): Listener[T] = { - if (in == null) - null - else { - createListenerWrapper(in) - } + def toScalaListener[T <: JEvent](in: org.zalando.nakadi.client.java.Listener[T]): Listener[T] = if (in == null) { + null + } else { + createListenerWrapper(in) } - def toScalaCursor[T <: JEvent](in: JEventStreamBatch[T]): Option[Cursor] = { - in.getCursor - Option(in) match { - case None => None - case Some(in) => toScalaCursor(in.getCursor) - } + def toScalaCursor[T <: JEvent](in: JEventStreamBatch[T]): Option[Cursor] = Option(in) match { + case None => None + case Some(in) => toScalaCursor(in.getCursor) } - def toJavaEvents[T <: JEvent](in: JEventStreamBatch[T]): Option[java.util.List[T]] = { - Option(in) match { - case None => None - case Some(in) => Option(in.getEvents) - } + def toJavaEvents[T <: JEvent](in: JEventStreamBatch[T]): Option[java.util.List[T]] = Option(in) match { + case None => None + case Some(in) => Option(in.getEvents) } def toJavaClientError(error: Option[ClientError]): Optional[JClientError] = error match { - case Some(ClientError(msg, httpStatusCodeOpt, exceptionOpt)) => Optional.of(new JClientError(msg, fromOption(httpStatusCodeOpt), fromOption(exceptionOpt))) + case Some(ClientError(msg, httpStatusCodeOpt, exceptionOpt)) => Optional.of(new JClientError(msg, toOptional(httpStatusCodeOpt), toOptional(exceptionOpt))) case None => Optional.empty() } diff --git a/client/src/test/scala/org/zalando/nakadi/client/scala/ClientHandlerTest.scala b/client/src/test/scala/org/zalando/nakadi/client/scala/ClientHandlerTest.scala new file mode 100644 index 0000000..f2bc63c --- /dev/null +++ b/client/src/test/scala/org/zalando/nakadi/client/scala/ClientHandlerTest.scala @@ -0,0 +1,51 @@ +package org.zalando.nakadi.client.scala +import org.mockito.Matchers.any +import org.mockito.Matchers.anyString +import org.mockito.Mockito.reset +import org.mockito.Mockito.times +import org.mockito.Mockito.verify +import org.mockito.Mockito.when +import org.scalatest.BeforeAndAfter +import org.scalatest.FlatSpec +import org.scalatest.Matchers +import org.scalatest.WordSpec +import org.scalatest.mock.MockitoSugar + +class ClientHandlerTest extends WordSpec with Matchers with MockitoSugar with BeforeAndAfter { + + private val conn = mock[Connection] + private val handler = new ClientHandlerImpl(conn) + private val endpoint = "event-types" + + before { + reset(conn) + } + def replaySuccessfullResponse() { + conn.executeCall(null) + } + + "ClientHandler" should { + "call [get] method with [endpoint] and [token]" in { +// handler.get(endpoint) + } + "call [get] method with [endpoint], [header-params] and [token]" in { + + } + "call [stream] method with [endpoint], [header-params] and [token]" in { + + } + "call [delete] method with [endpoint] and [token]" in { + + } + "call [put] method with [endpoint, [model] and [token]" in { + + } + "call [post] method with [endpoint, [model] and [token]" in { + + } + "call [subscribe] method with [endpoint, [cursor], [listener], and [token]" in { + + } + } + +} \ No newline at end of file diff --git a/client/src/test/scala/org/zalando/nakadi/client/scala/ClientTest.scala b/client/src/test/scala/org/zalando/nakadi/client/scala/ClientTest.scala index cfc14d5..86d4a5f 100644 --- a/client/src/test/scala/org/zalando/nakadi/client/scala/ClientTest.scala +++ b/client/src/test/scala/org/zalando/nakadi/client/scala/ClientTest.scala @@ -28,10 +28,10 @@ import org.zalando.nakadi.client.utils.Uri class ClientTest extends WordSpec with Matchers with MockitoSugar with BeforeAndAfter { import JacksonJsonMarshaller._ import Uri._ - private var connection: Connection = mock[Connection] - private val client: Client = new ClientImpl(connection) + private var handler: ClientHandler = mock[ClientHandler] + private val client: Client = new ClientImpl(handler) before { - reset(connection) + reset(handler) } "ClientImpl " should { @@ -40,7 +40,7 @@ class ClientTest extends WordSpec with Matchers with MockitoSugar with BeforeAn val entity = HttpEntity(ContentTypes.`application/json`, "abc") val response = new HttpResponse(StatusCodes.NotFound, headers, entity, HttpProtocols.`HTTP/1.1`) val futureResponse = Future.successful(response) - when(connection.get(URI_PARTITIONING_STRATEGIES)).thenReturn(futureResponse) + when(handler.get(URI_PARTITIONING_STRATEGIES)).thenReturn(futureResponse) // val result=Await.result(client.partitionStrategies(),500.seconds) // result.isLeft shouldBe true // val Left(clientError) = result diff --git a/it/src/main/java/org/zalando/nakadi/client/examples/java/EventCreationExample.java b/it/src/main/java/org/zalando/nakadi/client/examples/java/EventCreationExample.java index 46e28ce..0772053 100644 --- a/it/src/main/java/org/zalando/nakadi/client/examples/java/EventCreationExample.java +++ b/it/src/main/java/org/zalando/nakadi/client/examples/java/EventCreationExample.java @@ -17,7 +17,6 @@ import org.zalando.nakadi.client.java.model.EventTypeStatistics; import org.zalando.nakadi.client.java.utils.SerializationUtils; import org.zalando.nakadi.client.scala.ClientFactory; -import org.zalando.nakadi.client.utils.ClientBuilder; import com.google.common.collect.Lists; diff --git a/it/src/main/java/org/zalando/nakadi/client/examples/java/EventListenerExample.java b/it/src/main/java/org/zalando/nakadi/client/examples/java/EventListenerExample.java index f26edb3..1f16feb 100644 --- a/it/src/main/java/org/zalando/nakadi/client/examples/java/EventListenerExample.java +++ b/it/src/main/java/org/zalando/nakadi/client/examples/java/EventListenerExample.java @@ -1,13 +1,14 @@ package org.zalando.nakadi.client.examples.java; +import java.util.Optional; import java.util.concurrent.ExecutionException; import org.zalando.nakadi.client.java.Client; import org.zalando.nakadi.client.java.Listener; +import org.zalando.nakadi.client.java.StreamParameters; import org.zalando.nakadi.client.java.model.Cursor; import org.zalando.nakadi.client.java.model.EventStreamBatch; import org.zalando.nakadi.client.scala.ClientFactory; -import org.zalando.nakadi.client.utils.ClientBuilder; import com.fasterxml.jackson.core.type.TypeReference; @@ -28,17 +29,22 @@ public static void main(String[] args) throws InterruptedException, * Initialize our Listener */ Listener listener = new EventCounterListener("Java-Test"); - Cursor cursor = new Cursor("0", "BEGIN"); - String eventTypeName = "Event-example-with-0-messages"; - TypeReference> typeRef = new TypeReference>() { - }; + StreamParameters params = new StreamParameters( + Optional.of(new Cursor("0", "BEGIN")), + Optional.empty(),//batchLimit, + Optional.empty(),//streamLimit, + Optional.empty(),//batchFlushTimeout, + Optional.empty(),//streamTimeout, + Optional.empty(),//streamKeepAliveLimit, + Optional.empty()//flowId + ); - java.util.concurrent.Future result = client - .subscribe(eventTypeName, java.util.Optional.of(cursor), - listener, typeRef); + String eventTypeName = "Event-example-with-0-messages"; + TypeReference> typeRef = new TypeReference>() {}; - result.get(); + java.util.concurrent.Future result = client.subscribe(eventTypeName, params, listener, typeRef); + result.get(); } } diff --git a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala index 8ada3d4..aaad459 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala @@ -19,7 +19,7 @@ object EventCreationExample extends App { import org.zalando.nakadi.client.utils.ClientBuilder import org.zalando.nakadi.client.scala.Client - val client: Client = ClientFactory.getScalaClient() + val client: Client = ClientFactory.getScalaClient() // 2. Create a simple Meeting Event class case class MeetingsEvent(date: String, topic: String) extends Event @@ -65,18 +65,14 @@ object EventCreationExample extends App { //You need to import the default Serializer if you don't sepecify your own! import JacksonJsonMarshaller._ - client.createEventType(eventType) - Thread.sleep(1000) + client.createEventType(eventType) // 4. Publish the EventType - val event = new MeetingsEvent("2016-04-28T13:28:15+00:00", "Hackaton") - var events = for { - a <- 1 to 10000 - } yield MeetingsEvent("2016-04-28T13:28:15+00:00", "Hackaton" + a) - - // Thread.sleep(1000) - - Await.result(client.publishEvents(eventTypeName, events), 120.seconds) + val event = new MeetingsEvent("2016-04-28T13:28:15+00:00", "Hackaton") + var events = for { + a <- 1 to 1 + } yield MeetingsEvent("2016-04-28T13:28:15+00:00", "Hackaton" + a) + Await.result(client.publishEvents(eventTypeName, events), 120.seconds) client.stop() } \ No newline at end of file diff --git a/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala b/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala index bec3d9c..cdd0ad8 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala @@ -9,27 +9,27 @@ object ClientFactory { def host(): String = "nakadi-sandbox.aruha-test.zalan.do" def localHost(): String = "localhost" def localPort(): Integer = 8080 - def OAuth2Token(): Option[() => String] = Option(() => "5375a46c-9195-4a01-90bc-5e304eb7fae8") + def OAuth2Token(): Option[() => String] = Option(() => "63e78acc-3fae-46d6-b8c5-1c213d99ba7d") def port(): Integer = 443 - def connection(): Connection = Connection.newConnection(host, port, OAuth2Token(), true, false) - def client(): Client = new ClientImpl(connection, "UTF-8") + def client(): Client = Connection.newClient(host, port, OAuth2Token(), true, false) def getJavaClient() = - defaultBuilder().buildJavaClient(); + builder().buildJavaClient(); - def getScalaClient() = defaultBuilder().build() + def getScalaClient() = builder().build() - private def defaultBuilder() = { - connect2Default() + private def builder() = { +// useRemote() + useLocal() } - private def connect2Local() = { + private def useLocal() = { new ClientBuilder() // .withHost(ClientFactory.localHost()) // .withPort(ClientFactory.localPort()) // .withSecuredConnection(false) // s .withVerifiedSslCertificate(false) // s } - private def connect2Default() = { + private def useRemote() = { ClientBuilder() .withHost(ClientFactory.host()) .withSecuredConnection(true) //s From 8c1baefc2814f7c9333c201a116b7628c1af10aa Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Tue, 24 May 2016 15:19:31 +0200 Subject: [PATCH 054/183] techjira:LAAS-60 First pre-release --- project/Build.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/Build.scala b/project/Build.scala index 3b4cdaf..f8337dc 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -65,7 +65,7 @@ lazy val client = withDefaults( project.settings( name := projectName, organization := "org.zalando.laas", - version := "2.0-SNAPSHOT", + version := "2.0.0-pre-alpha", crossPaths := false, scalaVersion := "2.11.7", publishTo := whereToPublishTo(isSnapshot.value), From 6d6b565eaa13ad0167ab67a29867d71450456116 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Wed, 25 May 2016 17:24:28 +0200 Subject: [PATCH 055/183] techjira:LAAS-60 Refactored Actors, Removed Publishing Actor. Added SupervisingActor. Removed unnecessary layers of abstraction. --- .../zalando/nakadi/client/java/Client.java | 6 +- .../zalando/nakadi/client/java/Listener.java | 9 +- .../nakadi/client/java/model/Partition.java | 17 ++- .../nakadi/client/scala/Listener.scala | 5 +- .../nakadi/client/scala/model/Model.scala | 6 +- .../nakadi/client/java/ClientImpl.java | 11 +- .../client/actor/EventConsumingActor.scala | 51 +++---- .../client/actor/EventReceivingActor.scala | 62 --------- .../client/actor/SupervisingActor.scala | 123 +++++++++++++++++ .../client/handler/SubscriptionHandler.scala | 89 +++++++++++++ .../nakadi/client/java/ClientHandler.scala | 34 +++-- .../nakadi/client/scala/ClientHandler.scala | 72 ---------- .../nakadi/client/scala/ClientImpl.scala | 82 ++++++++---- .../nakadi/client/scala/Connection.scala | 118 +++------------- .../nakadi/client/scala/HttpFactory.scala | 50 +++---- .../nakadi/client/scala/MessageHandler.scala | 126 ++++++++++-------- .../scala/model/SprayJsonMarshaller.scala | 75 ----------- .../nakadi/client/utils/ModelConverter.scala | 26 +--- .../nakadi/client/utils/ParseHelper.scala | 86 ------------ .../client/scala/ClientHandlerTest.scala | 1 - .../nakadi/client/scala/ClientTest.scala | 8 +- .../nakadi/client/utils/TestScalaEntity.scala | 2 +- .../project/Settings.scala | 3 +- .../examples/java/EventCounterListener.java | 99 +++++++++----- .../examples/java/EventListenerExample.java | 32 ++--- .../client/examples/java/MeetingsEvent.java | 2 + .../examples/scala/EventCreationExample.scala | 20 ++- .../examples/scala/EventListenerExample.scala | 27 ++-- .../client/ClientSubscriptionTest.scala | 2 +- .../nakadi/client/logic/ReceiverGraph.scala | 2 +- 30 files changed, 589 insertions(+), 657 deletions(-) delete mode 100644 client/src/main/scala/org/zalando/nakadi/client/actor/EventReceivingActor.scala create mode 100644 client/src/main/scala/org/zalando/nakadi/client/actor/SupervisingActor.scala create mode 100644 client/src/main/scala/org/zalando/nakadi/client/handler/SubscriptionHandler.scala delete mode 100644 client/src/main/scala/org/zalando/nakadi/client/scala/ClientHandler.scala delete mode 100644 client/src/main/scala/org/zalando/nakadi/client/scala/model/SprayJsonMarshaller.scala delete mode 100644 client/src/main/scala/org/zalando/nakadi/client/utils/ParseHelper.scala diff --git a/api/src/main/java/org/zalando/nakadi/client/java/Client.java b/api/src/main/java/org/zalando/nakadi/client/java/Client.java index db4116a..69f0c6a 100644 --- a/api/src/main/java/org/zalando/nakadi/client/java/Client.java +++ b/api/src/main/java/org/zalando/nakadi/client/java/Client.java @@ -143,7 +143,7 @@ public interface Client { * @param listener Listener to pass the event to when it is received. * @return Void in case of success */ - Future subscribe(String eventTypeName, StreamParameters parameters, Listener listener, Deserializer> deserializer); + void subscribe(String eventTypeName, StreamParameters parameters, Listener listener, Deserializer> deserializer); /** * Registers the subscription of a listener to start streaming events from a partition in non-blocking fashion. * @param eventTypeName The unique name (id) of the EventType target @@ -151,12 +151,12 @@ public interface Client { * @param typeRef TypeReference for unmarshalling with the Jackson ObjectMapper. * @return Void in case of success */ - Future subscribe(String eventTypeName, StreamParameters parameters, Listener listener, TypeReference> typeRef); + void subscribe(String eventTypeName, StreamParameters parameters, Listener listener, TypeReference> typeRef); /** * Removes the subscription of a listener, to stop streaming events from a partition. * @param eventTypeName The unique name (id) of the EventType target * @param listener Listener to pass the event to when it is received. * @return Void in case of success */ - Future> unsubscribe(String eventTypeName, Listener listener); + void unsubscribe(String eventTypeName, Listener listener); } \ No newline at end of file diff --git a/api/src/main/java/org/zalando/nakadi/client/java/Listener.java b/api/src/main/java/org/zalando/nakadi/client/java/Listener.java index 873d58b..1ea2caf 100644 --- a/api/src/main/java/org/zalando/nakadi/client/java/Listener.java +++ b/api/src/main/java/org/zalando/nakadi/client/java/Listener.java @@ -7,9 +7,12 @@ import org.zalando.nakadi.client.java.model.Event; public interface Listener { - String getId(); + + String getId(); - void onReceive(String eventUrl, Cursor cursor, List events); + void onReceive(String endpoint, Cursor cursor, List events); - void onError(String eventUrl, Optional error); + void onSubscribed(String endpoint, Optional cursor); + + void onError(String endpoint, Optional error); } diff --git a/api/src/main/java/org/zalando/nakadi/client/java/model/Partition.java b/api/src/main/java/org/zalando/nakadi/client/java/model/Partition.java index b99abd2..1593d8e 100644 --- a/api/src/main/java/org/zalando/nakadi/client/java/model/Partition.java +++ b/api/src/main/java/org/zalando/nakadi/client/java/model/Partition.java @@ -1,14 +1,16 @@ package org.zalando.nakadi.client.java.model; +import com.fasterxml.jackson.annotation.JsonCreator; + /** * Partition information. Can be helpful when trying to start a stream using an * unmanaged API. This information is not related to the state of the consumer * clients. */ public class Partition { - private final Integer partition; - private final Integer oldestAvailableOffset; - private final Integer newestAvailableOffset; + private final String partition; + private final String oldestAvailableOffset; + private final String newestAvailableOffset; /** * Partition information. Can be helpful when trying to start a stream using @@ -30,21 +32,22 @@ public class Partition { * special name BEGIN, meaning a pointer to the offset of the * oldest available event in the partition. */ - public Partition(Integer partition, Integer oldestAvailableOffset, Integer newestAvailableOffset) { + @JsonCreator + public Partition(String partition, String oldestAvailableOffset, String newestAvailableOffset) { this.partition = partition; this.oldestAvailableOffset = oldestAvailableOffset; this.newestAvailableOffset = newestAvailableOffset; } - public Integer getPartition() { + public String getPartition() { return partition; } - public Integer getOldestAvailableOffset() { + public String getOldestAvailableOffset() { return oldestAvailableOffset; } - public Integer getNewestAvailableOffset() { + public String getNewestAvailableOffset() { return newestAvailableOffset; } diff --git a/api/src/main/scala/org/zalando/nakadi/client/scala/Listener.scala b/api/src/main/scala/org/zalando/nakadi/client/scala/Listener.scala index 2e19902..637b934 100644 --- a/api/src/main/scala/org/zalando/nakadi/client/scala/Listener.scala +++ b/api/src/main/scala/org/zalando/nakadi/client/scala/Listener.scala @@ -8,6 +8,7 @@ import org.zalando.nakadi.client.scala.model.Event trait Listener[T] { def id: String - def onReceive(eventUrl: String, cursor: Cursor, events: Seq[T]): Unit - def onError(eventUrl: String, error: Option[ClientError]): Unit + def onReceive(endpoint: String, cursor: Cursor, events: Seq[T]): Unit + def onSubscribed(endpoint: String,cursor: Option[Cursor]):Unit + def onError(endpoint: String, error: Option[ClientError]): Unit } \ No newline at end of file diff --git a/api/src/main/scala/org/zalando/nakadi/client/scala/model/Model.scala b/api/src/main/scala/org/zalando/nakadi/client/scala/model/Model.scala index a052595..117460d 100644 --- a/api/src/main/scala/org/zalando/nakadi/client/scala/model/Model.scala +++ b/api/src/main/scala/org/zalando/nakadi/client/scala/model/Model.scala @@ -111,9 +111,9 @@ case class Metrics(metrics: Map[String, Any]) * @param newestAvailableOffset An offset of the newest available Event in that partition. This value will be changing upon reception of new events for this partition by Nakadi. This value can be used to construct a cursor when opening streams (see `GET /event-type/{name}/events` for details). Might assume the special name BEGIN, meaning a pointer to the offset of the oldest available event in the partition. */ case class Partition( - partition: Integer, - oldestAvailableOffset: Integer, - newestAvailableOffset: Integer) + partition: String, + oldestAvailableOffset: String, + newestAvailableOffset: String) /** * One chunk of events in a stream. A batch consists of an array of `Event`s plus a `Cursor` pointing to the offset of the last Event in the stream. The size of the array of Event is limited by the parameters used to initialize a Stream. If acting as a keep alive message (see `GET /event-type/{name}/events`) the events array will be omitted. Sequential batches might repeat the cursor if no new events arrive. diff --git a/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java b/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java index f98d9f4..4796b11 100644 --- a/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java +++ b/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java @@ -122,19 +122,18 @@ public Future stop() { } @Override - public Future subscribe(String eventTypeName, StreamParameters parameters, Listener listener, Deserializer> deserializer) { - return handler.subscribeJava(Uri.getEventStreamingUri(eventTypeName), parameters, listener, deserializer); - + public void subscribe(String eventTypeName, StreamParameters parameters, Listener listener, Deserializer> deserializer) { + handler.subscribeJava(Uri.getEventStreamingUri(eventTypeName), parameters, listener, deserializer); } @Override - public Future subscribe(String eventTypeName, StreamParameters parameters, Listener listener, + public void subscribe(String eventTypeName, StreamParameters parameters, Listener listener, TypeReference> typeRef) { - return handler.subscribeJava(Uri.getEventStreamingUri(eventTypeName), parameters, listener, SerializationUtils.withCustomDeserializer(typeRef)); + handler.subscribeJava(Uri.getEventStreamingUri(eventTypeName), parameters, listener, SerializationUtils.withCustomDeserializer(typeRef)); } @Override - public Future> unsubscribe(String eventTypeName, Listener listener) { + public void unsubscribe(String eventTypeName, Listener listener) { throw new NotImplementedException(); } diff --git a/client/src/main/scala/org/zalando/nakadi/client/actor/EventConsumingActor.scala b/client/src/main/scala/org/zalando/nakadi/client/actor/EventConsumingActor.scala index bc711a3..9c54769 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/actor/EventConsumingActor.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/actor/EventConsumingActor.scala @@ -9,7 +9,6 @@ import org.zalando.nakadi.client.scala.Listener import org.zalando.nakadi.client.scala.model.Cursor import org.zalando.nakadi.client.scala.model.Event import org.zalando.nakadi.client.scala.model.EventStreamBatch -import EventPublishingActor.NextEvent import akka.actor.Actor import akka.actor.ActorLogging import akka.actor.ActorRef @@ -26,6 +25,7 @@ import org.zalando.nakadi.client.scala.ScalaResult import org.zalando.nakadi.client.scala.JavaResult import org.zalando.nakadi.client.scala.ErrorResult import org.zalando.nakadi.client.java.model.{ Event => JEvent } + /** * This actor serves as Sink for the pipeline.
      * 1. It receives the message and the cursor from the payload. @@ -35,13 +35,18 @@ import org.zalando.nakadi.client.java.model.{ Event => JEvent } * */ +object EventConsumingActor { + case class Init(cursor:Option[Cursor]) +} + class EventConsumingActor(url: String, - receivingActor: ActorRef, // - handler: EventHandler) + receivingActor: ActorRef, // + handler: EventHandler) extends Actor with ActorLogging with ActorSubscriber { import ModelConverter._ - var currCursor: Cursor = null - var prevCursor: Cursor = null + import EventConsumingActor._ + + var initialCursor: Option[Cursor] = null override protected def requestStrategy: RequestStrategy = new RequestStrategy { override def requestDemand(remainingRequested: Int): Int = { @@ -50,35 +55,31 @@ class EventConsumingActor(url: String, } override def receive: Receive = { + case Init(cursor) => + log.debug("Initializing - handler {} - cursor - {}", cursor) + initialCursor = cursor case OnNext(msg: ByteString) => + import util.Random + if (Random.nextFloat() > 0.9 && Random.nextBoolean() && Random.nextBoolean()) + throw new IllegalStateException("OMG, not again!") + val message = msg.utf8String - log.debug("[EventConsumer] Event - url {} - msg {}", url, message) - handleMsg(message) + log.debug("Event - cursor {} - url {} - msg {}", initialCursor, url, message) + handler.handleOnReceive(url, message) case OnError(err: Throwable) => - log.error(err, "[EventConsumer] onError [preCursor {} currCursor {} url {}]", prevCursor, currCursor, url) + log.error("onError - cursor {} - url {} - error {}", initialCursor, url, err.getMessage) context.stop(self) case OnComplete => - log.info("[EventConsumer] onComplete [preCursor {} currCursor {} url {}]", prevCursor, currCursor, url) + log.info("onComplete - cursor {} - url {}", initialCursor, url) context.stop(self) } - def request4NextEvent(cursor: Cursor) = { - prevCursor = currCursor - currCursor = cursor - log.debug("[EventConsumer] NextEvent [preCursor {} currCursor {} url {}]", prevCursor, currCursor, url) - receivingActor ! NextEvent(Option(cursor)) - } - - def handleMsg(message: String) = { + override def postRestart(reason: Throwable) { + super.postRestart(reason) + log.info(s">>>>>>>>>>>>> <<<<<<<<<<<<<<<") + log.info(s">>>>>>>>>>>>> Restarted because of ${reason.getMessage}") + log.info(">>>>>>>>>>>>> Current cursor {} <<<<<<<<<<<<<<<", initialCursor) - handler.handle(url, message) match { - case Right(Some(cursor)) => - request4NextEvent(cursor) - case Right(None) => - log.error("Message lacks of a cursor [{}]", message) - case Left(ErrorResult(error)) => - log.error("Handler could not handle message {}", error.getMessage) - } } } diff --git a/client/src/main/scala/org/zalando/nakadi/client/actor/EventReceivingActor.scala b/client/src/main/scala/org/zalando/nakadi/client/actor/EventReceivingActor.scala deleted file mode 100644 index 5611ed2..0000000 --- a/client/src/main/scala/org/zalando/nakadi/client/actor/EventReceivingActor.scala +++ /dev/null @@ -1,62 +0,0 @@ -package org.zalando.nakadi.client.actor - -import scala.collection.mutable.Queue - -import org.zalando.nakadi.client.scala.model.Cursor - -import akka.actor.Actor -import akka.actor.ActorLogging -import akka.stream.actor.ActorPublisher -import akka.stream.actor.ActorPublisherMessage.Cancel -import akka.stream.actor.ActorPublisherMessage.Request -import org.zalando.nakadi.client.scala.EventHandler - -object EventPublishingActor { - case class Subscribe(handler:EventHandler) - case class Unsubscribe(handler:EventHandler) - case class NextEvent(lastReceivedCursor: Option[Cursor]) - case class Restart() - case class HttpError( - url: String, - cursor: Option[Cursor], - status: Int, - msg: Option[String]) -} - -/** - * This actor is a Publisher and serves as source for the pipeline.
      - * 1. It receives cursors from the EventConsumingActor. - * 2. It queues the received cursors. - * 3. It sends the next cursor(s) from the queue(FIFO) to the pipeline depending on the demand. - */ -class EventReceivingActor(url: String) extends Actor with ActorLogging with ActorPublisher[Option[Cursor]] { - - import EventPublishingActor._ - - val queue: Queue[Option[Cursor]] = Queue() - - override def receive: Receive = { - case NextEvent(cursor) => - log.debug("[EventReceivingActor] Request next event chunk {} ", cursor) - queue.enqueue(cursor) - sendEvents() - case Request(cnt) => - log.debug("[EventReceivingActor] Requested {} event chunks", cnt) - sendEvents() - case Cancel => - log.debug("[EventReceivingActor] Stopping the stream of events!") - context.stop(self) - case HttpError(url, cursor, status, _) => - log.error("[EventReceivingActor] Cursor {} on URL {} caused error {}", cursor, url, status) - case e => - log.error("[EventReceivingActor] an Error occurred {}",e.toString()) - } - - def sendEvents() = while (isActive && totalDemand > 0 && !queue.isEmpty) { - val cursor = queue.dequeue() - log.debug("[EventReceivingActor] Requesting events [cursor {} at url {}] ", cursor, url) - onNext(cursor) - } - -} - diff --git a/client/src/main/scala/org/zalando/nakadi/client/actor/SupervisingActor.scala b/client/src/main/scala/org/zalando/nakadi/client/actor/SupervisingActor.scala new file mode 100644 index 0000000..7c9b546 --- /dev/null +++ b/client/src/main/scala/org/zalando/nakadi/client/actor/SupervisingActor.scala @@ -0,0 +1,123 @@ +package org.zalando.nakadi.client.actor + +import akka.actor.ActorLogging +import akka.actor.Actor +import scala.concurrent.duration._ +import akka.actor.OneForOneStrategy +import akka.actor.SupervisorStrategy +import akka.actor.ActorKilledException +import akka.actor.ActorInitializationException +import akka.actor.SupervisorStrategy._ +import akka.actor.AllForOneStrategy +import akka.actor.ActorRef +import org.zalando.nakadi.client.scala.EventHandler +import org.zalando.nakadi.client.handler.SubscriptionHandler +import org.zalando.nakadi.client.scala.Connection +import org.zalando.nakadi.client.handler.SubscriptionHandlerImpl +import akka.actor.ActorLogging +import akka.actor.Actor +import scala.concurrent.duration._ +import akka.actor.OneForOneStrategy +import akka.actor.SupervisorStrategy +import akka.actor.ActorKilledException +import akka.actor.ActorInitializationException +import akka.actor.SupervisorStrategy._ +import akka.actor.AllForOneStrategy +import akka.actor.ActorRef +import org.zalando.nakadi.client.scala.EventHandler +import java.security.SecureRandom +import java.security.cert.X509Certificate +import java.util.concurrent.TimeoutException + +import scala.concurrent.ExecutionContext.Implicits.global +import scala.concurrent.Future +import scala.util.Try + +import org.reactivestreams.Publisher +import org.reactivestreams.Subscriber +import org.slf4j.LoggerFactory +import org.zalando.nakadi.client.Deserializer +import org.zalando.nakadi.client.Serializer +import org.zalando.nakadi.client.java.{ ClientHandler => JClientHandler } +import org.zalando.nakadi.client.java.{ ClientHandlerImpl => JClientHandlerImpl } +import org.zalando.nakadi.client.java.model.{ Cursor => JCursor } +import org.zalando.nakadi.client.java.model.{ Event => JEvent } +import org.zalando.nakadi.client.scala.model.Cursor +import org.zalando.nakadi.client.scala.model.Event + +import com.typesafe.scalalogging.Logger + +import akka.NotUsed +import akka.actor.Actor +import akka.actor.ActorRef +import akka.actor.ActorSystem +import akka.actor.Props +import akka.actor.Terminated +import akka.actor.actorRef2Scala +import akka.http.scaladsl.Http +import akka.http.scaladsl.Http.HostConnectionPool +import akka.http.scaladsl.HttpsConnectionContext +import akka.http.scaladsl.model.HttpRequest +import akka.http.scaladsl.model.HttpResponse +import akka.stream.ActorMaterializer +import akka.stream.ActorMaterializerSettings +import akka.stream.OverflowStrategy +import akka.stream.actor.ActorPublisher +import akka.stream.actor.ActorSubscriber +import akka.stream.scaladsl.Flow +import akka.stream.scaladsl.Framing +import akka.stream.scaladsl.Sink +import akka.stream.scaladsl.Source +import akka.util.ByteString +import javax.net.ssl.SSLContext +import javax.net.ssl.TrustManager +import javax.net.ssl.X509TrustManager +import akka.http.scaladsl.model.EntityStreamException +import java.util.concurrent.atomic.AtomicLong +import org.zalando.nakadi.client.scala.Connection +import org.zalando.nakadi.client.scala.HttpFactory + +object SupervisingActor { + sealed trait Subscription + case class Subscribe(endpoint: String, cursor: Option[Cursor], handler: EventHandler) extends Subscription + case class Unsubscribe(handler: EventHandler) extends Subscription + +} + +class SupervisingActor(val connection: Connection, val subscriptionHandler: SubscriptionHandler) extends Actor with ActorLogging { + import SupervisingActor._ + import EventConsumingActor._ + type SubscriptionEntry = (EventHandler, ActorRef) + //Listener ID + Susbscription + private var handlerMap: Map[String, SubscriptionEntry] = Map() + private var registrationCounter: AtomicLong = new AtomicLong(0); + + def receive: Receive = { + case subscrition: Subscribe => + log.info("New subscription {}", subscrition) + subscribe(subscrition) + case Unsubscribe(handler) => + } + + def subscribe(subscribe: Subscribe) = { + registrationCounter.incrementAndGet() + val Subscribe(endpoint, cursor, eventHandler) = subscribe + //Create the Consumer + val consumingActor = connection.actorSystem.actorOf(Props(classOf[EventConsumingActor], endpoint, null, eventHandler), "EventConsumingActor-" + registrationCounter.get) + val consumer = ActorSubscriber[ByteString](consumingActor) + + //Create the pipeline + subscriptionHandler.createPipeline(cursor, consumer, endpoint, eventHandler) + val subscription = (eventHandler, consumingActor) + val entry: SubscriptionEntry = (eventHandler, consumingActor) + handlerMap = handlerMap + ((eventHandler.id(), entry)) + + // Notify listener it is subscribed + eventHandler.handleOnSubscribed(endpoint, cursor) + } + + def unsubscribe() = { + + } + +} \ No newline at end of file diff --git a/client/src/main/scala/org/zalando/nakadi/client/handler/SubscriptionHandler.scala b/client/src/main/scala/org/zalando/nakadi/client/handler/SubscriptionHandler.scala new file mode 100644 index 0000000..e6d4920 --- /dev/null +++ b/client/src/main/scala/org/zalando/nakadi/client/handler/SubscriptionHandler.scala @@ -0,0 +1,89 @@ +package org.zalando.nakadi.client.handler + +import java.util.concurrent.TimeoutException + +import scala.concurrent.ExecutionContext.Implicits.global + +import org.reactivestreams.Subscriber +import org.slf4j.LoggerFactory +import org.zalando.nakadi.client.actor.SupervisingActor +import org.zalando.nakadi.client.actor.SupervisingActor.Subscribe +import org.zalando.nakadi.client.scala.Connection +import org.zalando.nakadi.client.scala.EventHandler +import org.zalando.nakadi.client.scala.HttpFactory +import org.zalando.nakadi.client.scala.model.Cursor + +import com.typesafe.scalalogging.Logger + +import akka.NotUsed +import akka.actor.Props +import akka.actor.actorRef2Scala +import akka.http.scaladsl.model.EntityStreamException +import akka.http.scaladsl.model.HttpRequest +import akka.http.scaladsl.model.HttpResponse +import akka.stream.OverflowStrategy +import akka.stream.scaladsl.Flow +import akka.stream.scaladsl.Framing +import akka.stream.scaladsl.Sink +import akka.stream.scaladsl.Source +import akka.util.ByteString + +trait SubscriptionHandler { + /** + * Handles the subscription for an eventHandler. + */ + def subscribe(endpoint: String, cursor: Option[Cursor], eventHandler: EventHandler) + def createPipeline(cursor:Option[Cursor], subscriber: Subscriber[ByteString], url: String, eventHandler: EventHandler) +} + +class SubscriptionHandlerImpl(val connection: Connection) extends SubscriptionHandler { + import HttpFactory._ + import SupervisingActor._ + //Local variables + private implicit val materializer = connection.materializer() + + val supervisingActor = connection.actorSystem.actorOf(Props(classOf[SupervisingActor], connection, this), "EventReceivingActor-" + System.currentTimeMillis()) + + private val RECEIVE_BUFFER_SIZE = 40960 + private val EVENT_DELIMITER = "\n" + + val logger = Logger(LoggerFactory.getLogger(this.getClass)) + + def subscribe(endpoint: String, cursor: Option[Cursor], eventHandler: EventHandler) = { + supervisingActor ! Subscribe(endpoint, cursor, eventHandler) + } + + def createPipeline(cursor:Option[Cursor], subscriber: Subscriber[ByteString], url: String, eventHandler: EventHandler) = { + //Setup a flow for the request + val requestFlow = Flow[Option[Cursor]].via(requestCreator(url)) + .via(connection.requestFlow()) + .buffer(RECEIVE_BUFFER_SIZE, OverflowStrategy.backpressure) + .via(requestRenderer) + + //create the pipeline + val result = Source(List(cursor)) + .via(requestFlow) + .runForeach(_.runWith(Sink.fromSubscriber(subscriber))) + + result.onFailure { + case exception: TimeoutException => //When connection does not react within the timeout range + logger.error("Received an Exception timeout, restarting the client {}", exception.getMessage) + eventHandler.handleOnError(url, None, exception) + case exception: EntityStreamException => //When connection is broken without + logger.error("Received an Exception timeout, restarting the client {}", exception.getMessage) + eventHandler.handleOnError(url, None, exception) + case e => + logger.error("################# " + e) + } + } + + private def requestCreator(url: String): Flow[Option[Cursor], HttpRequest, NotUsed] = + Flow[Option[Cursor]].map(withHttpRequest(url, _, None, connection.tokenProvider)) + + private def requestRenderer: Flow[HttpResponse, Source[ByteString, Any], NotUsed] = + Flow[HttpResponse].filter(x => x.status.isSuccess()) + .map(_.entity.withSizeLimit(Long.MaxValue).dataBytes.via(delimiterFlow)) + + private def delimiterFlow = Flow[ByteString] + .via(Framing.delimiter(ByteString(EVENT_DELIMITER), maximumFrameLength = RECEIVE_BUFFER_SIZE, allowTruncation = true)) +} \ No newline at end of file diff --git a/client/src/main/scala/org/zalando/nakadi/client/java/ClientHandler.scala b/client/src/main/scala/org/zalando/nakadi/client/java/ClientHandler.scala index 25531ae..4f6aefe 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/java/ClientHandler.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/java/ClientHandler.scala @@ -32,17 +32,35 @@ import akka.http.scaladsl.model.HttpHeader import akka.http.scaladsl.model.HttpMethods import akka.http.scaladsl.model.HttpResponse import akka.http.scaladsl.unmarshalling.Unmarshal +import org.zalando.nakadi.client.handler.SubscriptionHandlerImpl /** * Handleer for delegating low-level http calls and listener subscriptions for the Java API. */ trait ClientHandler { - import HttpFactory._ - implicit val mat = connection.materializer() def logger(): Logger - def connection(): Connection + def connection: Connection + + def deserialize4Java[T](response: HttpResponse, des: Deserializer[T]): Future[Optional[T]] + + def get(endpoint: String): Future[HttpResponse] + + def get4Java[T](endpoint: String, des: Deserializer[T]): java.util.concurrent.Future[Optional[T]] + def get4Java[T](endpoint: String, headers: Seq[HttpHeader], des: Deserializer[T]): java.util.concurrent.Future[Optional[T]] + def post4Java[T](endpoint: String, model: T)(implicit serializer: Serializer[T]): java.util.concurrent.Future[Void] + + def subscribeJava[T <: JEvent](url: String, parameters: JStreamParameters, listener: JListener[T])(implicit des: Deserializer[JEventStreamBatch[T]]) +} +class ClientHandlerImpl(val connection: Connection) extends ClientHandler { + val logger: Logger = Logger(LoggerFactory.getLogger(this.getClass)) + import HttpFactory._ + private implicit val mat = connection.materializer() + + //TODO: Use constructor later make the tests simpler + private val subscriber = new SubscriptionHandlerImpl(connection) + def deserialize4Java[T](response: HttpResponse, des: Deserializer[T]): Future[Optional[T]] = response match { case HttpResponse(status, headers, entity, protocol) if (status.isSuccess()) => @@ -57,7 +75,7 @@ trait ClientHandler { def get(endpoint: String): Future[HttpResponse] = { logger.info("Get - URL {}", endpoint) - connection().executeCall(withHttpRequest(endpoint, HttpMethods.GET, Nil, connection().tokenProvider, None)) + connection.executeCall(withHttpRequest(endpoint, HttpMethods.GET, Nil, connection.tokenProvider, None)) } def get4Java[T](endpoint: String, des: Deserializer[T]): java.util.concurrent.Future[Optional[T]] = { @@ -102,19 +120,15 @@ trait ClientHandler { (Future { import ModelConverter._ val params: Option[ScalaStreamParameters] = toScalaStreamParameters(parameters) - val scalaListener = toScalaListener(listener) - val handler: EventHandler = new EventHandlerImpl[T, EmptyScalaEvent](Option((des, listener)), None) + val eventHandler: EventHandler = new EventHandlerImpl[T, EmptyScalaEvent](Left((des, listener))) val finalUrl = withUrl(url, params) - connection().handleSubscription(url, getCursor(params), handler) + subscriber.subscribe(url, getCursor(params), eventHandler) }) } private def getCursor(params: Option[ScalaStreamParameters]): Option[ScalaCursor] = params match { case Some(ScalaStreamParameters(cursor, batchLimit, streamLimit, batchFlushTimeout, streamTimeout, streamKeepAliveLimit, flowId)) => cursor case None => None } -} -class ClientHandlerImpl(val connection: Connection) extends ClientHandler { - def logger(): Logger = Logger(LoggerFactory.getLogger(this.getClass)) } diff --git a/client/src/main/scala/org/zalando/nakadi/client/scala/ClientHandler.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/ClientHandler.scala deleted file mode 100644 index cc58801..0000000 --- a/client/src/main/scala/org/zalando/nakadi/client/scala/ClientHandler.scala +++ /dev/null @@ -1,72 +0,0 @@ -package org.zalando.nakadi.client.scala - -import scala.collection.immutable.Seq -import scala.concurrent.Future - -import org.slf4j.LoggerFactory -import org.zalando.nakadi.client.Deserializer -import org.zalando.nakadi.client.Serializer -import org.zalando.nakadi.client.scala.model.Cursor -import org.zalando.nakadi.client.scala.model.Event -import org.zalando.nakadi.client.scala.model.EventStreamBatch - -import com.typesafe.scalalogging.Logger - -import akka.actor.Terminated -import akka.http.scaladsl.model.HttpHeader -import akka.http.scaladsl.model.HttpMethods -import akka.http.scaladsl.model.HttpResponse -import akka.stream.ActorMaterializer - -/** - * Handleer for delegating low-level http calls and listener subscriptions. - */ -trait ClientHandler { - import HttpFactory._ - def logger(): Logger - - def connection(): Connection - - def shutdown(): Future[Terminated] = connection().shutdown() - - def materializer(): ActorMaterializer = connection().materializer() - - def get(endpoint: String): Future[HttpResponse] = { - logger.info("Get - URL {}", endpoint) - connection().executeCall(withHttpRequest(endpoint, HttpMethods.GET, Nil, connection().tokenProvider, None)) - } - def get(endpoint: String, headers: Seq[HttpHeader]): Future[HttpResponse] = { - logger.info("Get - URL {} - Headers {}", endpoint, headers) - connection().executeCall(withHttpRequest(endpoint, HttpMethods.GET, headers, connection().tokenProvider, None)) - } - def stream(endpoint: String, headers: Seq[HttpHeader]): Future[HttpResponse] = { - logger.info("Streaming on Get: {}", endpoint) - connection().executeCall(withHttpRequest(endpoint, HttpMethods.GET, headers, connection().tokenProvider, None)) //TODO: Change to stream single event - } - - def delete(endpoint: String): Future[HttpResponse] = { - logger.info("Delete: {}", endpoint) - connection().executeCall(withHttpRequest(endpoint, HttpMethods.DELETE, Nil, connection().tokenProvider, None)) - } - - def put[T](endpoint: String, model: T)(implicit serializer: Serializer[T]): Future[HttpResponse] = { - logger.info("Get: {}", endpoint) - connection().executeCall(withHttpRequest(endpoint, HttpMethods.GET, Nil, connection().tokenProvider, None)) - } - - def post[T](endpoint: String, model: T)(implicit serializer: Serializer[T]): Future[HttpResponse] = { - val entity = serializer.to(model) - logger.info("Posting to endpoint {}", endpoint) - logger.debug("Data to post {}", entity) - connection().executeCall(withHttpRequestAndPayload(endpoint, entity, HttpMethods.POST, connection().tokenProvider)) - } - - def subscribe[T <: Event](endpoint: String, cursor: Option[Cursor], listener: Listener[T])(implicit des: Deserializer[EventStreamBatch[T]]) = { - val eventHandler: EventHandler = new EventHandlerImpl[EmptyJavaEvent, T](None, Option((des, listener))) - connection().handleSubscription(endpoint, cursor, eventHandler) - } -} - -class ClientHandlerImpl(val connection: Connection) extends ClientHandler { - def logger(): Logger = Logger(LoggerFactory.getLogger(this.getClass)) -} \ No newline at end of file diff --git a/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala index d6c80e0..dd616a4 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala @@ -20,71 +20,74 @@ import org.zalando.nakadi.client.Serializer import org.zalando.nakadi.client.scala.model._ import org.zalando.nakadi.client.utils.Uri import com.fasterxml.jackson.core.`type`.TypeReference +import org.zalando.nakadi.client.handler.SubscriptionHandlerImpl -private[scala] class ClientImpl(connection: ClientHandler, charSet: String = "UTF-8") extends Client { +private[scala] class ClientImpl(connection: Connection, charSet: String = "UTF-8") extends Client { import Uri._ import JacksonJsonMarshaller._ import HttpFactory._ - implicit val materializer = connection.materializer - // implicit val - val logger = Logger(LoggerFactory.getLogger(this.getClass)) + implicit val materializer = connection.materializer() + + private val logger = Logger(LoggerFactory.getLogger(this.getClass)) + private val subscriber = new SubscriptionHandlerImpl(connection) + def getMetrics(): Future[Either[ClientError, Option[Metrics]]] = { - logFutureEither(connection.get(URI_METRICS).flatMap(mapToEither(_)(deserializer(metricsTR)))) + logFutureEither(get(URI_METRICS).flatMap(mapToEither(_)(deserializer(metricsTR)))) } def getEventTypes(): Future[Either[ClientError, Option[Seq[EventType]]]] = { - logFutureEither(connection.get(URI_EVENT_TYPES).flatMap(mapToEither(_)(deserializer(listOfEventTypeTR)))) + logFutureEither(get(URI_EVENT_TYPES).flatMap(mapToEither(_)(deserializer(listOfEventTypeTR)))) } def createEventType(eventType: EventType): Future[Option[ClientError]] = { - logFutureOption(connection.post(URI_EVENT_TYPES, eventType).flatMap(mapToOption(_))) + logFutureOption(post(URI_EVENT_TYPES, eventType).flatMap(mapToOption(_))) } def getEventType(name: String): Future[Either[ClientError, Option[EventType]]] = { - logFutureEither(connection.get(URI_EVENT_TYPE_BY_NAME.format(name)).flatMap(in => mapToEither(in)(deserializer(eventTypeTR)))) + logFutureEither(get(URI_EVENT_TYPE_BY_NAME.format(name)).flatMap(in => mapToEither(in)(deserializer(eventTypeTR)))) } def updateEventType(name: String, eventType: EventType): Future[Option[ClientError]] = { - val result = connection.put(URI_EVENT_TYPE_BY_NAME.format(name), eventType) + val result = put(URI_EVENT_TYPE_BY_NAME.format(name), eventType) logFutureOption(result.flatMap(in => mapToOption(in))) } def deleteEventType(name: String): Future[Option[ClientError]] = { - logFutureOption(connection.delete(URI_EVENT_TYPE_BY_NAME.format(name)).flatMap(in => mapToOption(in))) + logFutureOption(delete(URI_EVENT_TYPE_BY_NAME.format(name)).flatMap(in => mapToOption(in))) } def publishEvents[T <: Event](eventTypeName: String, events: Seq[T], ser: Serializer[Seq[T]]): Future[Option[ClientError]] = { - logFutureOption(connection.post(URI_EVENTS_OF_EVENT_TYPE.format(eventTypeName), events).flatMap(in => mapToOption(in))) + logFutureOption(post(URI_EVENTS_OF_EVENT_TYPE.format(eventTypeName), events).flatMap(in => mapToOption(in))) } def publishEvents[T <: Event](eventTypeName: String, events: Seq[T]): Future[Option[ClientError]] = { - logFutureOption(connection.post(URI_EVENTS_OF_EVENT_TYPE.format(eventTypeName), events).flatMap(in => mapToOption(in))) + logFutureOption(post(URI_EVENTS_OF_EVENT_TYPE.format(eventTypeName), events).flatMap(in => mapToOption(in))) } def publishEvent[T <: Event](name: String, event: T, ser: Serializer[T]): Future[Option[ClientError]] = { - logFutureOption(connection.post(URI_EVENTS_OF_EVENT_TYPE.format(name), event).flatMap(in => mapToOption(in))) + logFutureOption(post(URI_EVENTS_OF_EVENT_TYPE.format(name), event).flatMap(in => mapToOption(in))) } def publishEvent[T <: Event](name: String, event: T): Future[Option[ClientError]] = { - logFutureOption(connection.post(URI_EVENTS_OF_EVENT_TYPE.format(name), event).flatMap(in => mapToOption(in))) + logFutureOption(post(URI_EVENTS_OF_EVENT_TYPE.format(name), event).flatMap(in => mapToOption(in))) } def getPartitions(name: String): Future[Either[ClientError, Option[Seq[Partition]]]] = { - logFutureEither(connection.get(URI_PARTITIONS_BY_EVENT_TYPE.format(name)).flatMap(in => mapToEither(in)(deserializer(listOfPartitionTR)))) + logFutureEither(get(URI_PARTITIONS_BY_EVENT_TYPE.format(name)).flatMap(in => mapToEither(in)(deserializer(listOfPartitionTR)))) } def getValidationStrategies(): Future[Either[ClientError, Option[Seq[EventValidationStrategy.Value]]]] = { - logFutureEither(connection.get(URI_VALIDATION_STRATEGIES).flatMap(mapToEither(_)(deserializer(listOfEventValidationStrategyTR)))) + logFutureEither(get(URI_VALIDATION_STRATEGIES).flatMap(mapToEither(_)(deserializer(listOfEventValidationStrategyTR)))) } def getEnrichmentStrategies(): Future[Either[ClientError, Option[Seq[EventEnrichmentStrategy.Value]]]] = { - logFutureEither(connection.get(URI_ENRICHMENT_STRATEGIES).flatMap(mapToEither(_)(deserializer(listOfEventEnrichmentStrategyTR)))) + logFutureEither(get(URI_ENRICHMENT_STRATEGIES).flatMap(mapToEither(_)(deserializer(listOfEventEnrichmentStrategyTR)))) } def getPartitioningStrategies(): Future[Either[ClientError, Option[Seq[PartitionStrategy.Value]]]] = - logFutureEither(connection.get(URI_PARTITIONING_STRATEGIES).flatMap(mapToEither(_)(deserializer(listOfPartitionStrategyTR)))) + logFutureEither(get(URI_PARTITIONING_STRATEGIES).flatMap(mapToEither(_)(deserializer(listOfPartitionStrategyTR)))) def stop(): Option[ClientError] = { - val result = Await.ready(connection.shutdown(), Duration.Inf) + val result = Await.ready(connection.actorSystem().terminate(), Duration.Inf) None } @@ -107,24 +110,57 @@ private[scala] class ClientImpl(connection: ClientHandler, charSet: String = "UT val url = URI_EVENTS_OF_EVENT_TYPE.format(eventType) logger.debug("Subscribing listener {} - cursor {} - parameters {} - eventType {} - url {}", listener.id, cursor, params, eventType, url) val finalUrl = withUrl(url, Some(params)) - connection.subscribe(finalUrl, cursor, listener)(des) + subscribe(finalUrl, cursor, listener)(des) + val eventHandler: EventHandler = new EventHandlerImpl[EmptyJavaEvent, T](Right((des, listener))) + subscriber.subscribe(finalUrl, cursor, eventHandler) Future.successful(None) } def unsubscribe[T <: Event](eventType: String, listener: Listener[T]): Future[Option[ClientError]] = ??? + def unsubscribe[T <: Event](endpoint: String, cursor: Option[Cursor], listener: Listener[T]): Unit = { + val eventHandler: EventHandler = new EventHandlerImpl[EmptyJavaEvent, T](Right((null, listener))) + subscriber.subscribe(endpoint, cursor, eventHandler) + } + //#################### //# HELPER METHODS # //#################### - private[client] def logFutureEither[A, T](future: Future[Either[ClientError, T]]): Future[Either[ClientError, T]] = { + private def get(endpoint: String): Future[HttpResponse] = { + logger.info("Get - URL {}", endpoint) + connection.executeCall(withHttpRequest(endpoint, HttpMethods.GET, Nil, connection.tokenProvider, None)) + } + private def delete(endpoint: String): Future[HttpResponse] = { + logger.info("Delete: {}", endpoint) + connection.executeCall(withHttpRequest(endpoint, HttpMethods.DELETE, Nil, connection.tokenProvider, None)) + } + + def put[T](endpoint: String, model: T)(implicit serializer: Serializer[T]): Future[HttpResponse] = { + logger.info("Get: {}", endpoint) + connection.executeCall(withHttpRequest(endpoint, HttpMethods.GET, Nil, connection.tokenProvider, None)) + } + + def post[T](endpoint: String, model: T)(implicit serializer: Serializer[T]): Future[HttpResponse] = { + val entity = serializer.to(model) + logger.info("Posting to endpoint {}", endpoint) + logger.debug("Data to post {}", entity) + connection.executeCall(withHttpRequestAndPayload(endpoint, entity, HttpMethods.POST, connection.tokenProvider)) + } + + def subscribe[T <: Event](endpoint: String, cursor: Option[Cursor], listener: Listener[T])(implicit des: Deserializer[EventStreamBatch[T]]) = { + val eventHandler: EventHandler = new EventHandlerImpl[EmptyJavaEvent, T](Right((des, listener))) + subscriber.subscribe(endpoint, cursor, eventHandler) + } + + private def logFutureEither[A, T](future: Future[Either[ClientError, T]]): Future[Either[ClientError, T]] = { future recover { case e: Throwable => logger.error("A unexpected error occured:", e.getMessage) Left(ClientError("Error: " + e.getMessage, None)) } } - private[client] def logFutureOption(future: Future[Option[ClientError]]): Future[Option[ClientError]] = { + private def logFutureOption(future: Future[Option[ClientError]]): Future[Option[ClientError]] = { future recover { case e: Throwable => logger.error("A unexpected error occured", e) @@ -132,7 +168,7 @@ private[scala] class ClientImpl(connection: ClientHandler, charSet: String = "UT } } - private[client] def mapToEither[T](response: HttpResponse)(implicit deserializer: Deserializer[T]): Future[Either[ClientError, Option[T]]] = { + private def mapToEither[T](response: HttpResponse)(implicit deserializer: Deserializer[T]): Future[Either[ClientError, Option[T]]] = { logger.debug("received [response={}]", response) response match { case HttpResponse(status, headers, entity, protocol) if (status.isSuccess()) => diff --git a/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala index 5ec33c1..fa6f695 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala @@ -2,64 +2,36 @@ package org.zalando.nakadi.client.scala import java.security.SecureRandom import java.security.cert.X509Certificate -import java.util.Optional -import scala.collection.immutable.Seq +import java.util.concurrent.atomic.AtomicLong + import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.Future -import scala.util.Failure -import scala.util.Success import scala.util.Try + import org.slf4j.LoggerFactory -import org.zalando.nakadi.client.Deserializer -import org.zalando.nakadi.client.Serializer -import org.zalando.nakadi.client.actor.EventConsumingActor -import org.zalando.nakadi.client.actor.EventPublishingActor -import org.zalando.nakadi.client.actor.EventPublishingActor.NextEvent -import org.zalando.nakadi.client.scala.model.Cursor -import org.zalando.nakadi.client.scala.model.Event -import org.zalando.nakadi.client.scala.model.EventStreamBatch -import org.zalando.nakadi.client.java.model.{ Event => JEvent } -import org.zalando.nakadi.client.java.model.{ EventStreamBatch => JEventStreamBatch } -import org.zalando.nakadi.client.java.model.{ Cursor => JCursor } -import org.zalando.nakadi.client.java.{ StreamParameters => JStreamParameters } import org.zalando.nakadi.client.java.{ ClientHandler => JClientHandler } import org.zalando.nakadi.client.java.{ ClientHandlerImpl => JClientHandlerImpl } -import org.zalando.nakadi.client.java.{ Listener => JListener } -import org.zalando.nakadi.client.utils.FutureConversions +import org.zalando.nakadi.client.java.model.{ Event => JEvent } +import org.zalando.nakadi.client.scala.model.Event + import com.typesafe.scalalogging.Logger -import akka.NotUsed + +import HttpFactory.TokenProvider import akka.actor.Actor import akka.actor.ActorSystem -import akka.actor.Props -import akka.actor.Terminated -import akka.actor.actorRef2Scala import akka.http.scaladsl.Http import akka.http.scaladsl.Http.HostConnectionPool import akka.http.scaladsl.HttpsConnectionContext -import akka.http.scaladsl.model.HttpHeader -import akka.http.scaladsl.model.HttpMethods import akka.http.scaladsl.model.HttpRequest import akka.http.scaladsl.model.HttpResponse -import akka.http.scaladsl.unmarshalling.Unmarshal import akka.stream.ActorMaterializer import akka.stream.ActorMaterializerSettings -import akka.stream.OverflowStrategy -import akka.stream.actor.ActorPublisher -import akka.stream.actor.ActorSubscriber import akka.stream.scaladsl.Flow -import akka.stream.scaladsl.Framing import akka.stream.scaladsl.Sink import akka.stream.scaladsl.Source -import akka.util.ByteString import javax.net.ssl.SSLContext import javax.net.ssl.TrustManager import javax.net.ssl.X509TrustManager -import org.zalando.nakadi.client.utils.ModelConverter -import akka.actor.ActorRef -import org.reactivestreams.Publisher -import org.reactivestreams.Subscriber -import org.zalando.nakadi.client.actor.EventReceivingActor -import java.util.concurrent.TimeoutException sealed trait EmptyJavaEvent extends JEvent sealed trait EmptyScalaEvent extends Event @@ -69,11 +41,10 @@ trait Connection { import HttpFactory.TokenProvider def tokenProvider(): Option[TokenProvider] def executeCall(request: HttpRequest): Future[HttpResponse] - def handleSubscription[J <: JEvent, S <: Event](url: String, cursor: Option[Cursor], eventHandler: EventHandler) - def connection(): Flow[HttpRequest, HttpResponse, Future[Http.OutgoingConnection]] + def requestFlow(): Flow[HttpRequest, HttpResponse, Future[Http.OutgoingConnection]] def materializer(): ActorMaterializer - def shutdown(): Future[Terminated] + def actorSystem(): ActorSystem } /** @@ -104,8 +75,7 @@ object Connection { */ def newClient(host: String, port: Int, tokenProvider: Option[() => String], securedConnection: Boolean, verifySSlCertificate: Boolean): Client = { val connection = new ConnectionImpl(host, port, tokenProvider, securedConnection, verifySSlCertificate) - val handler = new ClientHandlerImpl(connection) - new ClientImpl(handler) + new ClientImpl(connection) } def newClientHandler4Java(host: String, port: Int, tokenProvider: Option[() => String], securedConnection: Boolean, verifySSlCertificate: Boolean): JClientHandler = { @@ -120,21 +90,22 @@ object Connection { sealed class ConnectionImpl(val host: String, val port: Int, val tokenProvider: Option[() => String], securedConnection: Boolean, verifySSlCertificate: Boolean) extends Connection { import Connection._ import HttpFactory._ - + var registrationCounter: AtomicLong = new AtomicLong(0); type HttpFlow[T] = Flow[(HttpRequest, T), (Try[HttpResponse], T), HostConnectionPool] type StepResult[T] = (T, Option[String]) - private implicit val actorSystem = ActorSystem("Nakadi-Client-Connections") - private implicit val http = Http(actorSystem) + implicit val actorSystem = ActorSystem("Nakadi-Client-Connections") implicit val materializer = ActorMaterializer( ActorMaterializerSettings(actorSystem) .withInputBuffer( initialSize = 8, maxSize = 16)) + private implicit val http = Http(actorSystem) private val actors: Map[String, Actor] = Map() val logger = Logger(LoggerFactory.getLogger(this.getClass)) - val connection: Flow[HttpRequest, HttpResponse, Future[Http.OutgoingConnection]] = newSslContext(securedConnection, verifySSlCertificate) match { + + def requestFlow(): Flow[HttpRequest, HttpResponse, Future[Http.OutgoingConnection]] = newSslContext(securedConnection, verifySSlCertificate) match { case Some(result) => http.outgoingConnectionHttps(host, port, result) case None => logger.warn("Disabled HTTPS, switching to HTTP only!") @@ -144,7 +115,7 @@ sealed class ConnectionImpl(val host: String, val port: Int, val tokenProvider: def executeCall(request: HttpRequest): Future[HttpResponse] = { val response: Future[HttpResponse] = Source.single(request) - .via(connection). + .via(requestFlow). runWith(Sink.head) logError(response) response @@ -156,59 +127,4 @@ sealed class ConnectionImpl(val host: String, val port: Int, val tokenProvider: } } - def shutdown(): Future[Terminated] = actorSystem.terminate() - - def handleSubscription[J <: JEvent, S <: Event](url: String, cursor: Option[Cursor], eventHandler: EventHandler) = { - logger.info("Handler [{}] cursor {} uri [{}]", eventHandler.id, cursor, url) - - import EventPublishingActor._ - - //Create the Actors - val publishingActor = actorSystem.actorOf(Props(classOf[EventReceivingActor], url)) - val publisher = ActorPublisher[Option[Cursor]](publishingActor) - - val subscribingActor = actorSystem.actorOf(Props(classOf[EventConsumingActor], url, publishingActor, eventHandler)) - val subscriber = ActorSubscriber[ByteString](subscribingActor) - - val pipeline = createPipeline(publisher, subscriber, url, eventHandler) - - //Start - publishingActor ! NextEvent(cursor) - } - - private def createPipeline(publisher: Publisher[Option[Cursor]], subscriber: Subscriber[ByteString], url: String, eventHandler: EventHandler) = { - //Setup a flow for the request - val requestFlow = Flow[Option[Cursor]].via(requestCreator(url)) - .via(connection) - .buffer(RECEIVE_BUFFER_SIZE, OverflowStrategy.backpressure) - .via(requestRenderer) - - //create the pipeline - val result = Source.fromPublisher(publisher) - .via(requestFlow) - .runForeach(_.runWith(Sink.fromSubscriber(subscriber))) - - result.onFailure { - case exception: TimeoutException => - logger.error("Received an Exception timeout, restarting the client {}", exception.getMessage) - eventHandler.handleError(url, None, exception) - } - - result - } - - private def start() = { - - } - - private def requestCreator(url: String): Flow[Option[Cursor], HttpRequest, NotUsed] = - Flow[Option[Cursor]].map(withHttpRequest(url, _, None, tokenProvider)) - - private def requestRenderer: Flow[HttpResponse, Source[ByteString, Any], NotUsed] = - Flow[HttpResponse].filter(x => x.status.isSuccess()) - .map(_.entity.withSizeLimit(Long.MaxValue).dataBytes.via(delimiterFlow)) - - private def delimiterFlow = Flow[ByteString] - .via(Framing.delimiter(ByteString(EVENT_DELIMITER), maximumFrameLength = RECEIVE_BUFFER_SIZE, allowTruncation = true)) - } diff --git a/client/src/main/scala/org/zalando/nakadi/client/scala/HttpFactory.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/HttpFactory.scala index 90c9a81..ba42521 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/scala/HttpFactory.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/scala/HttpFactory.scala @@ -19,23 +19,6 @@ object HttpFactory { * Alias for a function that returns a string. */ type TokenProvider = () => String - def withHeaders(params: Option[StreamParameters]): Seq[HttpHeader] = { - params match { - case Some(StreamParameters(cursor, _, _, _, _, _, flowId)) => - withDefaultHeaders(cursor, flowId) - case None => Nil - } - } - def withQueryParams(params: Option[StreamParameters]): Seq[String] = { - params match { - case Some(StreamParameters(_, batchLimit, streamLimit, batchFlushTimeout, streamTimeout, streamKeepAliveLimit, _)) => - val parameters = List(("batch_limit", batchLimit), ("stream_limit", streamLimit), // - ("batch_flush_timeout", batchFlushTimeout), ("stream_timeout", streamTimeout), // - ("stream_keep_alive_limit", streamKeepAliveLimit)) - for { (key, optional) <- parameters; value <- optional } yield (key + "=" + value) - case None => Nil - } - } def withUrl(url: String, params: Option[StreamParameters]) = { val paramsList = withQueryParams(params) @@ -48,9 +31,7 @@ object HttpFactory { case None => additionalHeaders case Some(token) => additionalHeaders :+ headers.Authorization(OAuth2BearerToken(token())) } - val paramsList = withQueryParams(params) - HttpRequest(uri = url, method = httpMethod).withHeaders(allHeaders) } @@ -63,10 +44,29 @@ object HttpFactory { headers.Accept(MediaRange(`application/json`))) .withEntity(ContentType(`application/json`), entity) } + } + def withHttpRequest(url: String, cursor: Option[Cursor], flowId: Option[String], tokenProvider: Option[TokenProvider]): HttpRequest = { + val customHeaders = withDefaultHeaders(cursor, flowId) :+ RawHeader("Accept", "application/x-json-stream") + val allHeaders = tokenProvider match { + case None => customHeaders + case Some(token) => customHeaders :+ headers.Authorization(OAuth2BearerToken(token())) + } + + HttpRequest(uri = url, method = HttpMethods.GET).withHeaders(allHeaders) + } + private def withQueryParams(params: Option[StreamParameters]): Seq[String] = { + params match { + case Some(StreamParameters(_, batchLimit, streamLimit, batchFlushTimeout, streamTimeout, streamKeepAliveLimit, _)) => + val parameters = List(("batch_limit", batchLimit), ("stream_limit", streamLimit), // + ("batch_flush_timeout", batchFlushTimeout), ("stream_timeout", streamTimeout), // + ("stream_keep_alive_limit", streamKeepAliveLimit)) + for { (key, optional) <- parameters; value <- optional } yield (key + "=" + value) + case None => Nil + } } - def withDefaultHeaders(cursor: Option[Cursor], flowId: Option[String]): Seq[HttpHeader] = { + private def withDefaultHeaders(cursor: Option[Cursor], flowId: Option[String]): Seq[HttpHeader] = { val nakadiCursor = cursor match { case Some(value) if Option(value.partition).isDefined && Option(value.offset).isDefined => ("X-Nakadi-Cursors", Some("[{\"partition\":\"" + value.partition + "\", \"offset\": \"" + value.offset + "\"}]")) @@ -77,14 +77,4 @@ object HttpFactory { for { (key, optional) <- parameters; value <- optional } yield RawHeader(key, value) } - def withHttpRequest(url: String, cursor: Option[Cursor], flowId: Option[String], tokenProvider: Option[TokenProvider]): HttpRequest = { - val customHeaders = withDefaultHeaders(cursor, flowId) :+ RawHeader("Accept", "application/x-json-stream") - val allHeaders = tokenProvider match { - case None => customHeaders - case Some(token) => customHeaders :+ headers.Authorization(OAuth2BearerToken(token())) - } - - HttpRequest(uri = url, method = HttpMethods.GET).withHeaders(allHeaders) - } - } diff --git a/client/src/main/scala/org/zalando/nakadi/client/scala/MessageHandler.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/MessageHandler.scala index 135922a..5f16d8b 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/scala/MessageHandler.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/scala/MessageHandler.scala @@ -18,104 +18,116 @@ import java.util.Optional import org.slf4j.LoggerFactory import com.typesafe.scalalogging.Logger -private object EventHandler { - def transformScala[S <: Event](message: String, des: Deserializer[EventStreamBatch[S]]): Either[ErrorResult, ScalaResult[S]] = Try(des.from(message)) match { - case Success(eventBatch) => - eventBatch match { - case EventStreamBatch(cursor, events) => Right(ScalaResult(events, Some(cursor))) - } - case Failure(err) => Left(ErrorResult(err)) - } - def transformJava[J <: JEvent](message: String, des: Deserializer[JEventStreamBatch[J]]): Either[ErrorResult, JavaResult[J]] = { - Try(des.from(message)) match { - case Success(eventBatch) => - val events = if (eventBatch.getEvents != null && !eventBatch.getEvents.isEmpty()) Some(eventBatch.getEvents) else None - val sCursor = toScalaCursor(eventBatch.getCursor) - val jcursor = Option(eventBatch.getCursor) - Right(JavaResult(events, sCursor, jcursor)) - case Failure(err) => - Left(ErrorResult(err)) - } - } -} +/** + * Internal Models for handling logic between Java and Scala + */ +/** + * Base result for Deserialization Attempt. + */ trait Result -case class JavaResult[J <: JEvent]( +private case class JavaResult[J <: JEvent]( val javaEvents: Option[java.util.List[J]], - val scalaCursor: Option[Cursor], - val javaCursor: Option[JCursor]) extends Result + val scalaCursor: Cursor, + val javaCursor: JCursor) extends Result -case class ScalaResult[S <: Event]( +private case class ScalaResult[S <: Event]( scalaEvents: Option[Seq[S]] = None, - scalaCursor: Option[Cursor]) extends Result + scalaCursor: Cursor) extends Result + case class ErrorResult(error: Throwable) extends Result trait EventHandler { def id(): String - def handle(url: String, msg: String): Either[ErrorResult, Option[Cursor]] - def handleError(url: String, msg: Option[String], exception: Throwable) + def handleOnReceive(url: String, msg: String): Either[ErrorResult, Cursor] + def handleOnSubscribed(endpoint: String, cursor: Option[Cursor]): Unit + def handleOnError(url: String, msg: Option[String], exception: Throwable) } -class EventHandlerImpl[J <: JEvent, S <: Event](java: Option[(Deserializer[JEventStreamBatch[J]], JListener[J])], scala: Option[(Deserializer[EventStreamBatch[S]], Listener[S])]) extends EventHandler { +class EventHandlerImpl[J <: JEvent, S <: Event]( + eitherOfListeners: Either[(Deserializer[JEventStreamBatch[J]], JListener[J]), (Deserializer[EventStreamBatch[S]], Listener[S])]) extends EventHandler { import EventHandler._ - val log = Logger(LoggerFactory.getLogger(this.getClass)) - + private val log = Logger(LoggerFactory.getLogger(this.getClass)) private def createException(msg: String) = new IllegalStateException(msg) + lazy val id: String = { - (java, scala) match { - case (Some((_, listener)), _) => listener.getId - case (_, Some((_, listener))) => listener.id - case _ => "COULD NOT BE DEFINED" + + eitherOfListeners match { + //Validations + case Left((des, listener)) if (listener == null) => throw new IllegalStateException("EventHandler created without a Listener/Deserializer") + case Right((des, listener)) if (listener == null) => throw new IllegalStateException("EventHandler created without a Listener/Deserializer") + //Real handling + case Left((_, listener)) => listener.getId + case Right((_, listener)) => listener.id } } - def handle(url: String, msg: String): Either[ErrorResult, Option[Cursor]] = { - (java, scala) match { - case (Some((des, listener)), _) => // Java + + def handleOnReceive(url: String, msg: String): Either[ErrorResult, Cursor] = { + eitherOfListeners match { + case Left((des, listener)) => // Java transformJava(msg, des).right.flatMap { - case JavaResult(Some(events), Some(sCursor), Some(jCursor)) => + case JavaResult(Some(events), sCursor, jCursor) => listener.onReceive(url, jCursor, events) - Right(Option(sCursor)) - case JavaResult(None, Some(sCursor), Some(jCursor)) => - Right(Option(sCursor)) + Right(sCursor) + case JavaResult(None, sCursor, jCursor) => + Right(sCursor) case _ => val errorMsg = s"Could not handle JAVA Transformation url [$url] listener [${listener.getId}] msg [$msg]" log.error(errorMsg) listener.onError(errorMsg, Optional.empty()) Left(ErrorResult(createException(errorMsg))) } - case (_, Some((des, listener))) => //Scala + case Right((des, listener)) => //Scala transformScala(msg, des).right.flatMap { - case ScalaResult(Some(events), Some(cursor)) => + case ScalaResult(Some(events), cursor) => listener.onReceive(url, cursor, events) - Right(Option(cursor)) - case ScalaResult(None, Some(cursor)) => - Right(Option(cursor)) + Right(cursor) + case ScalaResult(None, cursor) => + Right(cursor) case _ => val errorMsg = s"Could not handle SCALA Transformation url [$url] listener [${listener.id}] msg [$msg]" listener.onError(errorMsg, None) log.error(errorMsg) Left(ErrorResult(createException(errorMsg))) } - case _ => - val errorMsg = s"Could not find a listener and serialiation url [$url] msg [$msg]" - log.error(errorMsg) - Left(ErrorResult(createException(errorMsg))) } } - def handleError(url: String, msg: Option[String], exception: Throwable) = { + def handleOnError(url: String, msg: Option[String], exception: Throwable) = { val errorMsg = if (msg.isDefined) msg.get else exception.getMessage val clientError = Some(ClientError(errorMsg, exception = Some(exception))) - (java, scala) match { - case (Some((des, listener)), _) => // Java + eitherOfListeners match { + case Left((des, listener)) => // Java listener.onError(errorMsg, toJavaClientError(clientError)) - case (_, Some((des, listener))) => //Scala + case Right((des, listener)) => //Scala listener.onError(errorMsg, clientError) - case _ => - val errorMsg = s"Could not find a listener to pass the error, url [$url] msg [$msg]" - log.error(errorMsg) } } + def handleOnSubscribed(endpoint: String, cursor: Option[Cursor]): Unit = eitherOfListeners match { + case Left((des, listener)) => + val res: Optional[JCursor] = toJavaCursor(cursor) + listener.onSubscribed(endpoint, res) // Java + case Right((des, listener)) => listener.onSubscribed(endpoint, cursor) + } } + +private object EventHandler { + def transformScala[S <: Event](message: String, des: Deserializer[EventStreamBatch[S]]): Either[ErrorResult, ScalaResult[S]] = Try(des.from(message)) match { + case Success(EventStreamBatch(cursor, events)) => Right(ScalaResult(events, cursor)) + case Failure(err) => Left(ErrorResult(err)) + } + + def transformJava[J <: JEvent](message: String, des: Deserializer[JEventStreamBatch[J]]): Either[ErrorResult, JavaResult[J]] = { + Try(des.from(message)) match { + case Success(eventBatch) => + val events = if (eventBatch.getEvents != null && !eventBatch.getEvents.isEmpty()) Some(eventBatch.getEvents) else None + val Some(sCursor: Cursor) = toScalaCursor(eventBatch.getCursor) + val jcursor: JCursor = eventBatch.getCursor + Right(JavaResult(events, sCursor, jcursor)) + case Failure(err) => + Left(ErrorResult(err)) + } + } +} \ No newline at end of file diff --git a/client/src/main/scala/org/zalando/nakadi/client/scala/model/SprayJsonMarshaller.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/model/SprayJsonMarshaller.scala deleted file mode 100644 index 40aa351..0000000 --- a/client/src/main/scala/org/zalando/nakadi/client/scala/model/SprayJsonMarshaller.scala +++ /dev/null @@ -1,75 +0,0 @@ -package org.zalando.nakadi.client.model - - - -//trait SprayJsonMarshaller extends SprayJsonSupport with DefaultJsonProtocol { -// import ParseHelper._ -// case class Test(subjectDescription: String) -// implicit val testFormatter = jsonFormat1(Test) -// //Enums -// implicit val dataOperationEnumFormatter = jsonEnumFormat(DataOperation) -// implicit val eventTypeCategoryEnumFormatter = jsonEnumFormat(EventTypeCategory) -// implicit val batchItemStepjsonEnumFormat = jsonEnumFormat(BatchItemStep) -// implicit val BatchItemPublishingStatusEnumFormat = jsonEnumFormat(BatchItemPublishingStatus) -// implicit val SchemaTypeEnumFormat = jsonEnumFormat(SchemaType) -// -// implicit val problemFormatter = jsonFormat(Problem, "problem_type", "title", "status", "detail", "instance") -// implicit val metricsFormatter = jsonFormat1(Metrics) -// implicit val partitionFormatter = jsonFormat(Partition, "partition", "oldest_available_offset", "newest_available_offset") -// implicit val cursorFormatter = jsonFormat2(Cursor) -// implicit val partitionResolutionStrategyFormatter = jsonFormat2(PartitionResolutionStrategy) -// implicit val eventTypeSchemaFormatter = jsonFormat(EventTypeSchema, "type", "schema") -// implicit val eventTypeStatisticsFormatter = jsonFormat(EventTypeStatistics, "expected_write_rate", "message_size", "read_parallelism", "write_parallelism") -// implicit val eventValidationStrategyFormatter = jsonFormat2(EventValidationStrategy) -// implicit val eventTypeFormatter = jsonFormat(EventType, "name", "owning_application", "category", "validation_strategies", "enrichment_strategies", "partition_strategy", "schema", "partition_key_fields", "partitioning_key_fields", "statistics") -// implicit val eventMetadataFormatter = jsonFormat(EventMetadata, "eid", "event_type", "occurred_at", "received_at", "parent_eids", "flow_id", "partition") -// implicit val eventEnrichmentStrategyFormatter = jsonFormat2(EventEnrichmentStrategy) -// implicit def dataChangeEventFormatter[A: JsonFormat] = jsonFormat(DataChangeEvent.apply[A],"data","data_type","data_operation","metadata") -// -// implicit object BusinessJsonFormat extends RootJsonFormat[BusinessEvent] { -// def write(a: BusinessEvent) = a match { -// case p: BusinessEvent => p.toJson -// } -// def read(json: JsValue) = json.asJsObject.fields("label") match { -// case JsString("business") => json.convertTo[BusinessEvent] -// case JsString("change-event") => json.convertTo[DataChangeEvent] -// case _ => -// throw new IllegalStateException("Couldn't parse what seems to be an Event: %s".format(json)) -// } -// } -// -// -// implicit object EventJsonFormat extends RootJsonFormat[Event] { -// def write(a: Event) = a match { -// case p: BusinessEvent => p.toJson -// } -// def read(json: JsValue) = json.asJsObject.fields("label") match { -// case JsString("business") => json.convertTo[BusinessEvent] -// case JsString("change-event") => json.convertTo[DataChangeEvent] -// case _ => -// throw new IllegalStateException("Couldn't parse what seems to be an Event: %s".format(json)) -// } -// } -// -// implicit val dataChangeEventFormatter = jsonFormat4(DataChangeEvent.apply) -// -// -// implicit def eventFormatter[T <: Event](implicit writer: JsonWriter[T])= writer.write(obj) -// -// implicit val eventStreamBatchFormatter = jsonFormat2(EventStreamBatch) -// implicit val batchItemResponseFormatter = jsonFormat(BatchItemResponse, "eid", "publishing_status", "step", "detail") -// -// -// -// implicit def serializer[T](implicit writer: JsonWriter[T]): Serializer[T] = new Serializer[T] { -// def to(from: T): String = { -// from.toJson(writer).toString() -// } -// } -// implicit def deserializer[T](implicit reader: JsonReader[T]): Derializer[T] = new Derializer[T] { -// def to(from: String): T = { -// from.parseJson.convertTo[T] -// } -// } -// -//} \ No newline at end of file diff --git a/client/src/main/scala/org/zalando/nakadi/client/utils/ModelConverter.scala b/client/src/main/scala/org/zalando/nakadi/client/utils/ModelConverter.scala index 0c86b76..d7954c8 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/utils/ModelConverter.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/utils/ModelConverter.scala @@ -53,13 +53,13 @@ object ModelConverter { new JCursor(partition, offset) case null => null } - - def toScalaListener[T <: JEvent](in: org.zalando.nakadi.client.java.Listener[T]): Listener[T] = if (in == null) { - null - } else { - createListenerWrapper(in) + def toJavaCursor(in: Option[Cursor]): Optional[JCursor] = in match { + case Some(Cursor(partition, offset)) => Optional.of(new JCursor(partition, offset)) + case None => null } + + def toScalaCursor[T <: JEvent](in: JEventStreamBatch[T]): Option[Cursor] = Option(in) match { case None => None case Some(in) => toScalaCursor(in.getCursor) @@ -75,19 +75,5 @@ object ModelConverter { case None => Optional.empty() } - private def createListenerWrapper[T <: org.zalando.nakadi.client.java.model.Event, B <: Event](in: org.zalando.nakadi.client.java.Listener[T]): Listener[T] = { - new Listener[T] { - val logger = Logger(LoggerFactory.getLogger(this.getClass)) - def id: String = in.getId - def onReceive(eventUrl: String, cursor: Cursor, events: Seq[T]): Unit = { - logger.debug("[ListenerWrapper] cursor {} url {} events {}", cursor, eventUrl, events) - in.onReceive(eventUrl, toJavaCursor(cursor), seqAsJavaList(events)) - } - def onError(eventUrl: String, error: Option[ClientError]) = { - logger.debug("[ListenerWrapper] cursor {} url {} error {}", eventUrl, error) - in.onError(eventUrl, toJavaClientError(error)) - } - } - - } + } \ No newline at end of file diff --git a/client/src/main/scala/org/zalando/nakadi/client/utils/ParseHelper.scala b/client/src/main/scala/org/zalando/nakadi/client/utils/ParseHelper.scala deleted file mode 100644 index 146e9ca..0000000 --- a/client/src/main/scala/org/zalando/nakadi/client/utils/ParseHelper.scala +++ /dev/null @@ -1,86 +0,0 @@ -package org.zalando.nakadi.client.utils - -import spray.json._ -import DefaultJsonProtocol._ - -object ParseHelper { - /* - * From (link expired!) -> https://tech.mendix.com/scala/2014/09/28/scala-nested-maps-to-json/ - */ - implicit object MapJsonFormat extends JsonFormat[Map[String, Any]] { - def write(m: Map[String, Any]) = { - JsObject(m.mapValues { - case s: String => JsString(s) - case v: Int => JsNumber(v) // will get output without decimal point - case v: Long => JsNumber(v) - case v: Double => JsNumber(v) - case b: Boolean => JsBoolean(b) - case mm: Map[String @unchecked, Any @unchecked] => write(mm) - case x => serializationError(s"Unexpected value within Map[String,Any] values (cannot convert to JSON): $x") - }) - } - - def read(jsv: JsValue) = jsv match { - case jso: JsObject => readMap(jso) - case v => deserializationError("Expected JsObject, but got: " + v) - } - - // Note: Makes sense having this as a separate function (not embedded in 'read') because our recursion can now - // use 'JsObject's directly. - // - private def readMap(jso: JsObject): Map[String, Any] = jso.fields.mapValues { - case JsString(s) => s - case JsNumber(d) if d.intValue == d => d.intValue - case JsNumber(d) if d.longValue == d => d.longValue - case JsNumber(d) => d - case JsBoolean(b) => b - case v: JsObject => readMap(v) - case v => deserializationError("Unexpected value within JsObject: " + v) - } - } - - - def jsonEnumFormat[T <: Enumeration](enum: T) = new JsonFormat[T#Value] { - def write(in: T#Value) = JsString(in.toString()) - def read(json: JsValue) = json match { - case JsString(s) => enum.withName(s) - case somethingElse => deserializationError("Unexpected value four our Enumerator: " + somethingElse) - } - } - - implicit def listFormat[T: JsonFormat] = new JsonFormat[T] { - def write(in: T) = write(in) - def read(value: JsValue) = value match { - case element: JsObject => element.convertTo[T] - case _ => deserializationError("A JsObject was expected!!") - } - } - - - -/** - * A custom version of the Spray DefaultJsonProtocol with a modified field naming strategy - */ -trait SnakifiedSprayJsonSupport extends DefaultJsonProtocol { - import reflect._ - - /** - * This is the most important piece of code in this object! - * It overrides the default naming scheme used by spray-json and replaces it with a scheme that turns camelcased - * names into snakified names (i.e. using underscores as word separators). - */ - override protected def extractFieldNames(classTag: ClassTag[_]) = { - import java.util.Locale - - def snakify(name: String) = PASS2.replaceAllIn(PASS1.replaceAllIn(name, REPLACEMENT), REPLACEMENT).toLowerCase(Locale.US) - - super.extractFieldNames(classTag).map { snakify(_) } - } - - private val PASS1 = """([A-Z]+)([A-Z][a-z])""".r - private val PASS2 = """([a-z\d])([A-Z])""".r - private val REPLACEMENT = "$1_$2" -} - -object SnakifiedSprayJsonSupport extends SnakifiedSprayJsonSupport -} \ No newline at end of file diff --git a/client/src/test/scala/org/zalando/nakadi/client/scala/ClientHandlerTest.scala b/client/src/test/scala/org/zalando/nakadi/client/scala/ClientHandlerTest.scala index f2bc63c..8af615b 100644 --- a/client/src/test/scala/org/zalando/nakadi/client/scala/ClientHandlerTest.scala +++ b/client/src/test/scala/org/zalando/nakadi/client/scala/ClientHandlerTest.scala @@ -14,7 +14,6 @@ import org.scalatest.mock.MockitoSugar class ClientHandlerTest extends WordSpec with Matchers with MockitoSugar with BeforeAndAfter { private val conn = mock[Connection] - private val handler = new ClientHandlerImpl(conn) private val endpoint = "event-types" before { diff --git a/client/src/test/scala/org/zalando/nakadi/client/scala/ClientTest.scala b/client/src/test/scala/org/zalando/nakadi/client/scala/ClientTest.scala index 86d4a5f..d46a977 100644 --- a/client/src/test/scala/org/zalando/nakadi/client/scala/ClientTest.scala +++ b/client/src/test/scala/org/zalando/nakadi/client/scala/ClientTest.scala @@ -28,10 +28,10 @@ import org.zalando.nakadi.client.utils.Uri class ClientTest extends WordSpec with Matchers with MockitoSugar with BeforeAndAfter { import JacksonJsonMarshaller._ import Uri._ - private var handler: ClientHandler = mock[ClientHandler] - private val client: Client = new ClientImpl(handler) + private var connection: Connection = mock[Connection] + private val client: Client = new ClientImpl(connection) before { - reset(handler) + reset(connection) } "ClientImpl " should { @@ -40,7 +40,7 @@ class ClientTest extends WordSpec with Matchers with MockitoSugar with BeforeAn val entity = HttpEntity(ContentTypes.`application/json`, "abc") val response = new HttpResponse(StatusCodes.NotFound, headers, entity, HttpProtocols.`HTTP/1.1`) val futureResponse = Future.successful(response) - when(handler.get(URI_PARTITIONING_STRATEGIES)).thenReturn(futureResponse) +// when(connection.get(URI_PARTITIONING_STRATEGIES)).thenReturn(futureResponse) // val result=Await.result(client.partitionStrategies(),500.seconds) // result.isLeft shouldBe true // val Left(clientError) = result diff --git a/client/src/test/scala/org/zalando/nakadi/client/utils/TestScalaEntity.scala b/client/src/test/scala/org/zalando/nakadi/client/utils/TestScalaEntity.scala index 770a0fb..3f6d0c0 100644 --- a/client/src/test/scala/org/zalando/nakadi/client/utils/TestScalaEntity.scala +++ b/client/src/test/scala/org/zalando/nakadi/client/utils/TestScalaEntity.scala @@ -7,7 +7,7 @@ object TestScalaEntity { // Simple bjects composed out of scalar typers only (without dependency to other Object-models) val problem = new Problem("problemType", "title", 312, Option("detail"), Option("instance")) val metrics = new Metrics(Map("metrics" -> "test")) - val partition = new Partition(0, 132, 4423) + val partition = new Partition("0", "132", "4423") val cursor = new Cursor("0", "120") val eventTypeSchema = new EventTypeSchema(SchemaType.JSON, "schema") val eventValidationStrategy = EventValidationStrategy.NONE diff --git a/examples/scalajs-play-core-react/project/Settings.scala b/examples/scalajs-play-core-react/project/Settings.scala index bea9248..d669e40 100644 --- a/examples/scalajs-play-core-react/project/Settings.scala +++ b/examples/scalajs-play-core-react/project/Settings.scala @@ -86,7 +86,8 @@ object dependencies { "com.github.japgolly.scalajs-react" %%% "ext-scalaz71" % versions.client.scalajsReact, "com.github.japgolly.scalajs-react" %%% "ext-monocle" % versions.client.scalajsReact, "com.github.japgolly.scalacss" %%% "ext-react" % versions.client.scalaCSS, - "org.scala-js" %%% "scalajs-dom" % versions.client.scalaDom + "org.scala-js" %%% "scalajs-dom" % versions.client.scalaDom, + "org.zalando.laas" % "nakadi-clients" % "2.0-SNAPSHOT" withSources() withJavadoc() )) val jsDependencies = Def.setting(Seq( diff --git a/it/src/main/java/org/zalando/nakadi/client/examples/java/EventCounterListener.java b/it/src/main/java/org/zalando/nakadi/client/examples/java/EventCounterListener.java index f74256d..6e6d9f2 100644 --- a/it/src/main/java/org/zalando/nakadi/client/examples/java/EventCounterListener.java +++ b/it/src/main/java/org/zalando/nakadi/client/examples/java/EventCounterListener.java @@ -1,6 +1,9 @@ package org.zalando.nakadi.client.examples.java; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.Optional; import java.util.concurrent.atomic.AtomicLong; import org.slf4j.Logger; @@ -8,36 +11,68 @@ import org.zalando.nakadi.client.java.ClientError; import org.zalando.nakadi.client.java.model.Cursor; -public class EventCounterListener implements org.zalando.nakadi.client.java.Listener { - private final String id; - private AtomicLong eventCount = new AtomicLong(0); - private Logger log = LoggerFactory.getLogger(this.getClass()); - - public EventCounterListener(String id) { - this.id = id; - } - - @Override - public String getId() { - return id; - } - - @Override - public void onReceive(String eventUrl, Cursor cursor, List events) { - eventCount.addAndGet(events.size()); - log.info("#####################################"); - log.info("Received " + events.size()); - log.info(String.format("Has a total of %d events", eventCount.get())); - log.info("#####################################"); - - } - - @Override - public void onError(String eventUrl, java.util.Optional error) { - if (error.isPresent()) { - ClientError clientError = error.get(); - log.error("An error occurred" + clientError.getMsg()); - } - - } +public class EventCounterListener implements + org.zalando.nakadi.client.java.Listener { + private final String id; + private AtomicLong eventCounter = new AtomicLong(0); + private AtomicLong calledCounter = new AtomicLong(0); + private Map maps = new HashMap(); + private Logger log = LoggerFactory.getLogger(this.getClass()); + + public EventCounterListener(String id) { + this.id = id; + } + + @Override + public String getId() { + return id; + } + + @Override + public void onReceive(String eventUrl, Cursor cursor, + List events) { + calledCounter.addAndGet(1); + for (MeetingsEvent event : events) { + Integer startIndex = event.getTopic().indexOf('n'); + Long number = Long.valueOf(event.getTopic().substring( + startIndex + 1)); + + if (maps.get(number) != null) { + log.error("Event {} [ALREADY EXISTS] {}", event, + maps.get(number)); + System.exit(0); + } + + maps.put(number, event); + if (eventCounter.get() == (number - 1)) { + eventCounter.addAndGet(1); + } else { + log.info("Current:" + eventCounter.get() + " Received " + + number); + } + } + + log.info("#####################################"); + log.info("Received " + events.size()); + log.info(String.format("Has a total of %d events", eventCounter.get())); + log.info(String.format("Was called %d time", calledCounter.get())); + log.info("#####################################"); + + } + + @Override + public void onError(String eventUrl, java.util.Optional error) { + if (error.isPresent()) { + ClientError clientError = error.get(); + log.error("An error occurred" + clientError.getMsg()); + } + + } + + public void onSubscribed(String endpoint, Optional cursor) { + log.info("########## onSubscribed ############"); + log.info("Endpoint " + endpoint ); + log.info("Cursor " + cursor ); + log.info("#####################################"); + } } \ No newline at end of file diff --git a/it/src/main/java/org/zalando/nakadi/client/examples/java/EventListenerExample.java b/it/src/main/java/org/zalando/nakadi/client/examples/java/EventListenerExample.java index 1f16feb..2baf43f 100644 --- a/it/src/main/java/org/zalando/nakadi/client/examples/java/EventListenerExample.java +++ b/it/src/main/java/org/zalando/nakadi/client/examples/java/EventListenerExample.java @@ -30,21 +30,21 @@ public static void main(String[] args) throws InterruptedException, */ Listener listener = new EventCounterListener("Java-Test"); - StreamParameters params = new StreamParameters( - Optional.of(new Cursor("0", "BEGIN")), - Optional.empty(),//batchLimit, - Optional.empty(),//streamLimit, - Optional.empty(),//batchFlushTimeout, - Optional.empty(),//streamTimeout, - Optional.empty(),//streamKeepAliveLimit, - Optional.empty()//flowId - ); - - String eventTypeName = "Event-example-with-0-messages"; - TypeReference> typeRef = new TypeReference>() {}; - - java.util.concurrent.Future result = client.subscribe(eventTypeName, params, listener, typeRef); - - result.get(); + StreamParameters params = new StreamParameters(Optional.of(new Cursor( + "0", "BEGIN")), Optional.empty(),// batchLimit, + Optional.empty(),// streamLimit, + Optional.empty(),// batchFlushTimeout, + Optional.empty(),// streamTimeout, + Optional.empty(),// streamKeepAliveLimit, + Optional.empty()// flowId + ); + + + // String eventTypeName = "Example-unique-hundred-messages-3"; + String eventTypeName = "Example-unique-million-messages"; + TypeReference> typeRef = new TypeReference>() { + }; + + client.subscribe(eventTypeName, params, listener, typeRef); } } diff --git a/it/src/main/java/org/zalando/nakadi/client/examples/java/MeetingsEvent.java b/it/src/main/java/org/zalando/nakadi/client/examples/java/MeetingsEvent.java index a266b83..37b3d9f 100644 --- a/it/src/main/java/org/zalando/nakadi/client/examples/java/MeetingsEvent.java +++ b/it/src/main/java/org/zalando/nakadi/client/examples/java/MeetingsEvent.java @@ -22,5 +22,7 @@ public String getDate() { public String getTopic() { return topic; } + + } \ No newline at end of file diff --git a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala index aaad459..12980e0 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala @@ -12,6 +12,7 @@ import org.zalando.nakadi.client.scala.model.SchemaType import org.zalando.nakadi.client.utils.ClientBuilder import scala.concurrent.Await import scala.concurrent.duration.DurationInt +import scala.collection.mutable.ListBuffer object EventCreationExample extends App { @@ -42,7 +43,9 @@ object EventCreationExample extends App { //First the eventType name, wich will be part of the URL: https://nakadi.test.io/event-types/{eventTypeName} //See the API for more information on the EventType model //https://github.com/zalando/nakadi/blob/nakadi-jvm/api/nakadi-event-bus-api.yaml#L1240 - val eventTypeName = "Event-example-with-0-messages" + // val eventTypeName = "Example-unique-million-messages" + val eventTypeName = "Example-unique-million-messages" +// val eventTypeName = "Example-unique-hundred-messages-3" val owner = "team-laas" val category = EventTypeCategory.UNDEFINED // We want just to pass data without through Nakadi, simple schema-validation is enough! @@ -68,11 +71,16 @@ object EventCreationExample extends App { client.createEventType(eventType) // 4. Publish the EventType - val event = new MeetingsEvent("2016-04-28T13:28:15+00:00", "Hackaton") - var events = for { - a <- 1 to 1 - } yield MeetingsEvent("2016-04-28T13:28:15+00:00", "Hackaton" + a) - Await.result(client.publishEvents(eventTypeName, events), 120.seconds) + var counter = 0 + for (n <- 1 to 1000) { + val event = new MeetingsEvent("2016-04-28T13:28:15+00:00", "Hackaton") + var events = ListBuffer[MeetingsEvent]() + for (a <- 1 to 1000) { + counter += 1 + events += MeetingsEvent("2016-04-28T13:28:15+00:00", "Hackaton" + counter) + } + Await.result(client.publishEvents(eventTypeName, events), 120.seconds) + } client.stop() } \ No newline at end of file diff --git a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala index d08f11b..e1acb34 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala @@ -15,11 +15,12 @@ import com.typesafe.scalalogging.Logger * Your listener will have to implement the necessary */ class EventCounterListener(val id: String) extends Listener[MeetingsEvent] { - val log = Logger(LoggerFactory.getLogger(this.getClass)) + val log = Logger(LoggerFactory.getLogger(this.getClass)) private var eventCount: AtomicLong = new AtomicLong(0); + private var callerCount: AtomicLong = new AtomicLong(0); def onError(sourceUrl: String, error: Option[ClientError]): Unit = { - println("Error %s %s %s".format(sourceUrl,error)) + println("Error %s %s %s".format(sourceUrl, error)) } def onReceive(sourceUrl: String, cursor: Cursor, events: Seq[MeetingsEvent]): Unit = { @@ -30,6 +31,13 @@ class EventCounterListener(val id: String) extends Listener[MeetingsEvent] { log.info("#####################################") } + def onSubscribed(endpoint: String, cursor: Option[Cursor]): Unit = { + log.info("########## onSubscribed ############") + log.info("Endpoint " + endpoint ) + log.info("Cursor " + cursor ) + log.info("#####################################") + + } } object EventListenerExample extends App { @@ -48,16 +56,16 @@ object EventListenerExample extends App { * Create the Parameters with the cursor. */ - val cursor = Cursor("153000", "BEGIN") + val cursor = Cursor("0", "BEGIN") val parameters = new StreamParameters( - cursor = Some(cursor) // - , batchLimit = Some(200) // Maximum number of `Event`s in each chunk (and therefore per partition) of the stream. -// , streamLimit = Some(2) // Maximum number of `Event`s to stream (over all partitions being streamed in this + cursor = Some(cursor) // + , batchLimit = Some(1) // Maximum number of `Event`s in each chunk (and therefore per partition) of the stream. + // , streamLimit = Some(2) // Maximum number of `Event`s to stream (over all partitions being streamed in this //connection). -// , batchFlushTimeout = Some(5) // Maximum time in seconds to wait for the flushing of each chunk (per partition). + // , batchFlushTimeout = Some(5) // Maximum time in seconds to wait for the flushing of each chunk (per partition). // ,streamKeepAliveLimit=Some(4) -// , streamTimeout = Some(30) + // , streamTimeout = Some(30) ) /** @@ -67,7 +75,8 @@ object EventListenerExample extends App { implicit def typeRef: TypeReference[EventStreamBatch[MeetingsEvent]] = new TypeReference[EventStreamBatch[MeetingsEvent]] {} import org.zalando.nakadi.client.scala.model.JacksonJsonMarshaller._ - val eventTypeName = "MeetingsEvent-example-E" + // val eventTypeName = "Event-example-with-0-messages" + val eventTypeName = "Example-unique-million-messages" val result = client.subscribe(eventTypeName, parameters, listener) } \ No newline at end of file diff --git a/it/src/test/scala/org/zalando/nakadi/client/ClientSubscriptionTest.scala b/it/src/test/scala/org/zalando/nakadi/client/ClientSubscriptionTest.scala index 336379e..27a1325 100644 --- a/it/src/test/scala/org/zalando/nakadi/client/ClientSubscriptionTest.scala +++ b/it/src/test/scala/org/zalando/nakadi/client/ClientSubscriptionTest.scala @@ -29,7 +29,7 @@ class ClientSubscriptionTest extends WordSpec with Matchers with ModelFactory { def onError(sourceUrl: String, error: Option[ClientError]): Unit = { println("YOOOOOOOOOOOOOO ") } - def onSubscribed(): Unit = ??? + def onSubscribed(endpoint: String,cursor: Option[Cursor]):Unit = ??? def onUnsubscribed(): Unit = ??? def onReceive(sourceUrl: String, cursor: Cursor, event: Seq[MyEventExample]): Unit = ??? } diff --git a/it/src/test/scala/org/zalando/nakadi/client/logic/ReceiverGraph.scala b/it/src/test/scala/org/zalando/nakadi/client/logic/ReceiverGraph.scala index 9b048d2..f47c30d 100644 --- a/it/src/test/scala/org/zalando/nakadi/client/logic/ReceiverGraph.scala +++ b/it/src/test/scala/org/zalando/nakadi/client/logic/ReceiverGraph.scala @@ -29,7 +29,7 @@ class ReceiverGraph[T](eventName: String, connection: Connection, request: HttpR val g: RunnableGraph[_] = RunnableGraph.fromGraph(GraphDSL.create() { implicit builder => - val flow: Flow[HttpRequest, HttpResponse, Future[Http.OutgoingConnection]] = connection.connection() + val flow: Flow[HttpRequest, HttpResponse, Future[Http.OutgoingConnection]] = connection.requestFlow() // Source val out: Outlet[HttpResponse] = builder.add(Source.single(request).via(flow)).out From 25deaf5438c60a3374a7b0fc8c90eac4b9b6c8bd Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Fri, 27 May 2016 10:52:28 +0200 Subject: [PATCH 056/183] techjira:LAAS-60 Subscription and Unsubscription implemented --- .../zalando/nakadi/client/java/Client.java | 3 +- .../zalando/nakadi/client/java/Listener.java | 6 +- .../zalando/nakadi/client/scala/Client.scala | 5 +- .../nakadi/client/java/ClientImpl.java | 30 ++++---- .../client/actor/EventConsumingActor.scala | 26 +++---- .../client/actor/SupervisingActor.scala | 48 ++++++++---- .../client/handler/SubscriptionHandler.scala | 20 +++-- ...tHandler.scala => JavaClientHandler.scala} | 63 ++++++++-------- .../nakadi/client/scala/ClientImpl.scala | 73 ++++++------------- .../nakadi/client/scala/Connection.scala | 44 ++++++++--- .../nakadi/client/scala/MessageHandler.scala | 16 ++-- .../nakadi/client/scala/ClientTest.scala | 9 ++- .../examples/scala/EventCreationExample.scala | 8 +- .../examples/scala/EventListenerExample.scala | 10 ++- project/Build.scala | 21 +++--- project/Dependencies.scala | 42 ++++------- project/plugins.sbt | 2 + 17 files changed, 220 insertions(+), 206 deletions(-) rename client/src/main/scala/org/zalando/nakadi/client/java/{ClientHandler.scala => JavaClientHandler.scala} (61%) diff --git a/api/src/main/java/org/zalando/nakadi/client/java/Client.java b/api/src/main/java/org/zalando/nakadi/client/java/Client.java index 69f0c6a..fc6d2d1 100644 --- a/api/src/main/java/org/zalando/nakadi/client/java/Client.java +++ b/api/src/main/java/org/zalando/nakadi/client/java/Client.java @@ -155,8 +155,9 @@ public interface Client { /** * Removes the subscription of a listener, to stop streaming events from a partition. * @param eventTypeName The unique name (id) of the EventType target + * @param partition The partition assigned to this listener. * @param listener Listener to pass the event to when it is received. * @return Void in case of success */ - void unsubscribe(String eventTypeName, Listener listener); + void unsubscribe(String eventTypeName,String partition, Listener listener); } \ No newline at end of file diff --git a/api/src/main/java/org/zalando/nakadi/client/java/Listener.java b/api/src/main/java/org/zalando/nakadi/client/java/Listener.java index 1ea2caf..f99104e 100644 --- a/api/src/main/java/org/zalando/nakadi/client/java/Listener.java +++ b/api/src/main/java/org/zalando/nakadi/client/java/Listener.java @@ -10,9 +10,9 @@ public interface Listener { String getId(); - void onReceive(String endpoint, Cursor cursor, List events); + void onReceive(String eventType, Cursor cursor, List events); - void onSubscribed(String endpoint, Optional cursor); + void onSubscribed(String eventType, Optional cursor); - void onError(String endpoint, Optional error); + void onError(String eventType, Optional error); } diff --git a/api/src/main/scala/org/zalando/nakadi/client/scala/Client.scala b/api/src/main/scala/org/zalando/nakadi/client/scala/Client.scala index 97a10b6..04b52b1 100644 --- a/api/src/main/scala/org/zalando/nakadi/client/scala/Client.scala +++ b/api/src/main/scala/org/zalando/nakadi/client/scala/Client.scala @@ -9,7 +9,7 @@ import com.fasterxml.jackson.core.`type`.TypeReference case class ClientError(msg: String, status: Option[Integer] = None, exception: Option[Throwable] = None) - trait Client { +trait Client { /** * Retrieve monitoring metrics @@ -174,9 +174,10 @@ case class ClientError(msg: String, status: Option[Integer] = None, exception: O * Removes the subscription of a listener, to stop streaming events from a partition. * * @param eventTypeName - Name of the EventType. + * @param partition The partition assigned to this listener. * @param listener - Listener to unsubscribe from the streaming events. */ - def unsubscribe[T <: Event](eventTypeName: String, listener: Listener[T]): Future[Option[ClientError]] + def unsubscribe[T <: Event](eventTypeName: String, partition:String, listener: Listener[T]): Future[Option[ClientError]] } diff --git a/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java b/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java index 4796b11..1061ac6 100644 --- a/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java +++ b/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java @@ -23,7 +23,7 @@ import com.fasterxml.jackson.core.type.TypeReference; public class ClientImpl implements Client { - private final ClientHandler handler; + private final JavaClientHandler handler; // Deserializers private final Deserializer metricsDeserializer = SerializationUtils.metricsDeserializer(); @@ -39,30 +39,30 @@ public class ClientImpl implements Client { private final Serializer eventTypeSerializer = SerializationUtils.defaultSerializer(); private final Deserializer eventTypeDeserializer = SerializationUtils.eventTypeDeserializer(); - public ClientImpl(ClientHandler handler) { + public ClientImpl(JavaClientHandler handler) { this.handler = handler; } @Override public Future> getMetrics() { - return handler.get4Java(Uri.URI_METRICS(), metricsDeserializer); + return handler.get(Uri.URI_METRICS(), metricsDeserializer); } @Override public Future>> getEventTypes() { - return handler.get4Java(Uri.URI_EVENT_TYPES(), seqOfEventTypeDeserializer); + return handler.get(Uri.URI_EVENT_TYPES(), seqOfEventTypeDeserializer); } @Override public Future createEventType(EventType eventType) { - return handler.post4Java(Uri.URI_EVENT_TYPES(), eventType, eventTypeSerializer); + return handler.post(Uri.URI_EVENT_TYPES(), eventType, eventTypeSerializer); } @Override public Future> getEventType(String eventTypeName) { - return handler.get4Java(Uri.getEventTypeByName(eventTypeName), eventTypeDeserializer); + return handler.get(Uri.getEventTypeByName(eventTypeName), eventTypeDeserializer); } @Override @@ -87,7 +87,7 @@ public Future publishEvent(String eventTypeName, T event @Override public Future publishEvents(String eventTypeName, List events, Serializer> serializer) { - return handler.post4Java(Uri.getEventStreamingUri(eventTypeName), events, serializer); + return handler.post(Uri.getEventStreamingUri(eventTypeName), events, serializer); } @Override @@ -97,23 +97,23 @@ public Future publishEvents(String eventTypeName, List>> getPartitions(String eventTypeName) { - return handler.get4Java(Uri.getPartitions(eventTypeName), seqOfPartitionDeserializer); + return handler.get(Uri.getPartitions(eventTypeName), seqOfPartitionDeserializer); } @Override public Future>> getValidationStrategies() { - return handler.get4Java(Uri.URI_VALIDATION_STRATEGIES(), seqOfEventValidationStrategy); + return handler.get(Uri.URI_VALIDATION_STRATEGIES(), seqOfEventValidationStrategy); } @Override public Future>> getEnrichmentStrategies() { - return handler.get4Java(Uri.URI_ENRICHMENT_STRATEGIES(), seqOfEventEnrichmentStrategy); + return handler.get(Uri.URI_ENRICHMENT_STRATEGIES(), seqOfEventEnrichmentStrategy); } @Override public Future>> getPartitioningStrategies() { - return handler.get4Java(Uri.URI_PARTITIONING_STRATEGIES(), seqOfPartitionStrategy); + return handler.get(Uri.URI_PARTITIONING_STRATEGIES(), seqOfPartitionStrategy); } @Override @@ -123,18 +123,18 @@ public Future stop() { @Override public void subscribe(String eventTypeName, StreamParameters parameters, Listener listener, Deserializer> deserializer) { - handler.subscribeJava(Uri.getEventStreamingUri(eventTypeName), parameters, listener, deserializer); + handler.subscribe(eventTypeName,Uri.getEventStreamingUri(eventTypeName), parameters, listener, deserializer); } @Override public void subscribe(String eventTypeName, StreamParameters parameters, Listener listener, TypeReference> typeRef) { - handler.subscribeJava(Uri.getEventStreamingUri(eventTypeName), parameters, listener, SerializationUtils.withCustomDeserializer(typeRef)); + handler.subscribe(eventTypeName,Uri.getEventStreamingUri(eventTypeName), parameters, listener, SerializationUtils.withCustomDeserializer(typeRef)); } @Override - public void unsubscribe(String eventTypeName, Listener listener) { - throw new NotImplementedException(); + public void unsubscribe(String eventTypeName, String partition, Listener listener) { + handler.unsubscribe(eventTypeName,partition, listener); } } \ No newline at end of file diff --git a/client/src/main/scala/org/zalando/nakadi/client/actor/EventConsumingActor.scala b/client/src/main/scala/org/zalando/nakadi/client/actor/EventConsumingActor.scala index 9c54769..fb4efb5 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/actor/EventConsumingActor.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/actor/EventConsumingActor.scala @@ -36,11 +36,10 @@ import org.zalando.nakadi.client.java.model.{ Event => JEvent } */ object EventConsumingActor { - case class Init(cursor:Option[Cursor]) + case class Shutdown(handler: EventHandler) } -class EventConsumingActor(url: String, - receivingActor: ActorRef, // +class EventConsumingActor(eventType: String, handler: EventHandler) extends Actor with ActorLogging with ActorSubscriber { import ModelConverter._ @@ -55,23 +54,24 @@ class EventConsumingActor(url: String, } override def receive: Receive = { - case Init(cursor) => - log.debug("Initializing - handler {} - cursor - {}", cursor) - initialCursor = cursor case OnNext(msg: ByteString) => import util.Random - if (Random.nextFloat() > 0.9 && Random.nextBoolean() && Random.nextBoolean()) - throw new IllegalStateException("OMG, not again!") - +// if (Random.nextFloat() > 0.9 && Random.nextBoolean() && Random.nextBoolean()) +// throw new IllegalStateException("OMG, not again!") val message = msg.utf8String - log.debug("Event - cursor {} - url {} - msg {}", initialCursor, url, message) - handler.handleOnReceive(url, message) + log.debug("Event - cursor {} - eventType {} - msg {}", initialCursor, eventType, message) + handler.handleOnReceive(eventType, message) case OnError(err: Throwable) => - log.error("onError - cursor {} - url {} - error {}", initialCursor, url, err.getMessage) + log.error("onError - cursor {} - eventType {} - error {}", initialCursor, eventType, err.getMessage) context.stop(self) case OnComplete => - log.info("onComplete - cursor {} - url {}", initialCursor, url) + log.info("onComplete - cursor {} - eventType {}", initialCursor, eventType) + context.stop(self) + case Shutdown(shutdownHandler: EventHandler) => + log.info("Shutting down eventType listener-id {} -> {}", handler.id(), shutdownHandler.id()) context.stop(self) + case a => + log.error("Could not handle message: {}",a) } override def postRestart(reason: Throwable) { diff --git a/client/src/main/scala/org/zalando/nakadi/client/actor/SupervisingActor.scala b/client/src/main/scala/org/zalando/nakadi/client/actor/SupervisingActor.scala index 7c9b546..b874496 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/actor/SupervisingActor.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/actor/SupervisingActor.scala @@ -38,8 +38,8 @@ import org.reactivestreams.Subscriber import org.slf4j.LoggerFactory import org.zalando.nakadi.client.Deserializer import org.zalando.nakadi.client.Serializer -import org.zalando.nakadi.client.java.{ ClientHandler => JClientHandler } -import org.zalando.nakadi.client.java.{ ClientHandlerImpl => JClientHandlerImpl } +import org.zalando.nakadi.client.java.{ JavaClientHandler => JClientHandler } +import org.zalando.nakadi.client.java.{ JavaClientHandlerImpl => JClientHandlerImpl } import org.zalando.nakadi.client.java.model.{ Cursor => JCursor } import org.zalando.nakadi.client.java.model.{ Event => JEvent } import org.zalando.nakadi.client.scala.model.Cursor @@ -79,44 +79,60 @@ import org.zalando.nakadi.client.scala.HttpFactory object SupervisingActor { sealed trait Subscription - case class Subscribe(endpoint: String, cursor: Option[Cursor], handler: EventHandler) extends Subscription - case class Unsubscribe(handler: EventHandler) extends Subscription + case class Subscribe(eventyTypeName: String, endpoint: String, cursor: Option[Cursor], handler: EventHandler) extends Subscription + case class Unsubscribe(eventTypeName: String, partition:String, eventHandlerId: String) extends Subscription } class SupervisingActor(val connection: Connection, val subscriptionHandler: SubscriptionHandler) extends Actor with ActorLogging { import SupervisingActor._ import EventConsumingActor._ + type SubscriptionKey = (String,String) //EventTypeName,PartitionId type SubscriptionEntry = (EventHandler, ActorRef) - //Listener ID + Susbscription - private var handlerMap: Map[String, SubscriptionEntry] = Map() - private var registrationCounter: AtomicLong = new AtomicLong(0); + private var subscriptions: Map[SubscriptionKey, SubscriptionEntry] = Map() //SubscriptionKey , SubscriptionEntry def receive: Receive = { case subscrition: Subscribe => log.info("New subscription {}", subscrition) subscribe(subscrition) - case Unsubscribe(handler) => + case unsubscription: Unsubscribe => + log.info("Number of subscriptions {}", subscriptions.size) + unsubscribe(unsubscription) } def subscribe(subscribe: Subscribe) = { - registrationCounter.incrementAndGet() - val Subscribe(endpoint, cursor, eventHandler) = subscribe + val Subscribe(eventTypeName, endpoint, cursor, eventHandler) = subscribe + log.info("Subscription nr {} for eventType {} and listener {}", (subscriptions.size + 1), eventTypeName, eventHandler.id()) + //Create the Consumer - val consumingActor = connection.actorSystem.actorOf(Props(classOf[EventConsumingActor], endpoint, null, eventHandler), "EventConsumingActor-" + registrationCounter.get) + val consumingActor = context.actorOf(Props(classOf[EventConsumingActor], endpoint, eventHandler), "EventConsumingActor-" + subscriptions.size) val consumer = ActorSubscriber[ByteString](consumingActor) + // Notify listener it is subscribed + eventHandler.handleOnSubscribed(endpoint, cursor) + //Create the pipeline subscriptionHandler.createPipeline(cursor, consumer, endpoint, eventHandler) val subscription = (eventHandler, consumingActor) + val Some(Cursor(partition,_))=cursor + val key :SubscriptionKey = (eventTypeName,partition) val entry: SubscriptionEntry = (eventHandler, consumingActor) - handlerMap = handlerMap + ((eventHandler.id(), entry)) - - // Notify listener it is subscribed - eventHandler.handleOnSubscribed(endpoint, cursor) + subscriptions = subscriptions + ((key, entry)) } - def unsubscribe() = { + def unsubscribe(unsubscription: Unsubscribe) = { + val Unsubscribe(eventTypeName, partition, eventHandlerId) = unsubscription + val key :SubscriptionKey = (eventTypeName,partition) + log.info("Unsubscribe({}) for eventType {} and listener {}",subscriptions, eventTypeName, eventHandlerId) + subscriptions.get(key) match { + case Some(subscription) => + val (handler,actor)= subscription + log.info("Sending shutdown message to actor: {}", actor) + actor ! Shutdown(handler) + + case None => + log.warning("Listener not found for {}", unsubscription) + } } diff --git a/client/src/main/scala/org/zalando/nakadi/client/handler/SubscriptionHandler.scala b/client/src/main/scala/org/zalando/nakadi/client/handler/SubscriptionHandler.scala index e6d4920..43c1881 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/handler/SubscriptionHandler.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/handler/SubscriptionHandler.scala @@ -32,8 +32,9 @@ trait SubscriptionHandler { /** * Handles the subscription for an eventHandler. */ - def subscribe(endpoint: String, cursor: Option[Cursor], eventHandler: EventHandler) - def createPipeline(cursor:Option[Cursor], subscriber: Subscriber[ByteString], url: String, eventHandler: EventHandler) + def subscribe(eventTypeName: String, endpoint: String, cursor: Option[Cursor], eventHandler: EventHandler) + def unsubscribe(eventTypeName: String,partition:String, listenerId: String) + def createPipeline(cursor: Option[Cursor], subscriber: Subscriber[ByteString], url: String, eventHandler: EventHandler) } class SubscriptionHandlerImpl(val connection: Connection) extends SubscriptionHandler { @@ -48,12 +49,15 @@ class SubscriptionHandlerImpl(val connection: Connection) extends SubscriptionHa private val EVENT_DELIMITER = "\n" val logger = Logger(LoggerFactory.getLogger(this.getClass)) - - def subscribe(endpoint: String, cursor: Option[Cursor], eventHandler: EventHandler) = { - supervisingActor ! Subscribe(endpoint, cursor, eventHandler) + + def subscribe(eventTypeName: String, endpoint: String, cursor: Option[Cursor], eventHandler: EventHandler) = { + supervisingActor ! Subscribe(eventTypeName,endpoint, cursor, eventHandler) + } + def unsubscribe(eventTypeName: String,partition:String, listenerId: String)= { + supervisingActor ! Unsubscribe(eventTypeName,partition, listenerId) } - def createPipeline(cursor:Option[Cursor], subscriber: Subscriber[ByteString], url: String, eventHandler: EventHandler) = { + def createPipeline(cursor: Option[Cursor], subscriber: Subscriber[ByteString], url: String, eventHandler: EventHandler) = { //Setup a flow for the request val requestFlow = Flow[Option[Cursor]].via(requestCreator(url)) .via(connection.requestFlow()) @@ -61,7 +65,7 @@ class SubscriptionHandlerImpl(val connection: Connection) extends SubscriptionHa .via(requestRenderer) //create the pipeline - val result = Source(List(cursor)) + val result = Source(List(cursor)) .via(requestFlow) .runForeach(_.runWith(Sink.fromSubscriber(subscriber))) @@ -73,7 +77,7 @@ class SubscriptionHandlerImpl(val connection: Connection) extends SubscriptionHa logger.error("Received an Exception timeout, restarting the client {}", exception.getMessage) eventHandler.handleOnError(url, None, exception) case e => - logger.error("################# " + e) + logger.error("Exception not handled" + e) } } diff --git a/client/src/main/scala/org/zalando/nakadi/client/java/ClientHandler.scala b/client/src/main/scala/org/zalando/nakadi/client/java/JavaClientHandler.scala similarity index 61% rename from client/src/main/scala/org/zalando/nakadi/client/java/ClientHandler.scala rename to client/src/main/scala/org/zalando/nakadi/client/java/JavaClientHandler.scala index 4f6aefe..e10c9a8 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/java/ClientHandler.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/java/JavaClientHandler.scala @@ -14,6 +14,7 @@ import org.zalando.nakadi.client.Deserializer import org.zalando.nakadi.client.Serializer import org.zalando.nakadi.client.java.model.{ Event => JEvent } import org.zalando.nakadi.client.scala.model.{ Cursor => ScalaCursor } +import org.zalando.nakadi.client.scala.{ ClientImpl => SClientImpl } import org.zalando.nakadi.client.java.model.{ EventStreamBatch => JEventStreamBatch } import org.zalando.nakadi.client.java.{ StreamParameters => JStreamParameters } import org.zalando.nakadi.client.java.{ Listener => JListener } @@ -33,35 +34,29 @@ import akka.http.scaladsl.model.HttpMethods import akka.http.scaladsl.model.HttpResponse import akka.http.scaladsl.unmarshalling.Unmarshal import org.zalando.nakadi.client.handler.SubscriptionHandlerImpl +import org.zalando.nakadi.client.handler.SubscriptionHandler /** - * Handleer for delegating low-level http calls and listener subscriptions for the Java API. + * Handler for mapping(Java<->Scala) and handling http calls and listener subscriptions for the Java API. */ -trait ClientHandler { +trait JavaClientHandler { + def deserialize[T](response: HttpResponse, des: Deserializer[T]): Future[Optional[T]] + def get[T](endpoint: String, des: Deserializer[T]): java.util.concurrent.Future[Optional[T]] + def get[T](endpoint: String, headers: Seq[HttpHeader], des: Deserializer[T]): java.util.concurrent.Future[Optional[T]] + def post[T](endpoint: String, model: T)(implicit serializer: Serializer[T]): java.util.concurrent.Future[Void] + def subscribe[T <: JEvent](eventTypeName: String, endpoint: String, parameters: JStreamParameters, listener: JListener[T])(implicit des: Deserializer[JEventStreamBatch[T]]) + def unsubscribe[T <: JEvent](eventTypeName: String,partition:String, listener: JListener[T]) - def logger(): Logger - def connection: Connection - - def deserialize4Java[T](response: HttpResponse, des: Deserializer[T]): Future[Optional[T]] - - def get(endpoint: String): Future[HttpResponse] - - def get4Java[T](endpoint: String, des: Deserializer[T]): java.util.concurrent.Future[Optional[T]] - def get4Java[T](endpoint: String, headers: Seq[HttpHeader], des: Deserializer[T]): java.util.concurrent.Future[Optional[T]] - def post4Java[T](endpoint: String, model: T)(implicit serializer: Serializer[T]): java.util.concurrent.Future[Void] - - def subscribeJava[T <: JEvent](url: String, parameters: JStreamParameters, listener: JListener[T])(implicit des: Deserializer[JEventStreamBatch[T]]) } -class ClientHandlerImpl(val connection: Connection) extends ClientHandler { +class JavaClientHandlerImpl(val connection: Connection, subscriber: SubscriptionHandler) extends JavaClientHandler { val logger: Logger = Logger(LoggerFactory.getLogger(this.getClass)) import HttpFactory._ private implicit val mat = connection.materializer() //TODO: Use constructor later make the tests simpler - private val subscriber = new SubscriptionHandlerImpl(connection) - - def deserialize4Java[T](response: HttpResponse, des: Deserializer[T]): Future[Optional[T]] = response match { + + def deserialize[T](response: HttpResponse, des: Deserializer[T]): Future[Optional[T]] = response match { case HttpResponse(status, headers, entity, protocol) if (status.isSuccess()) => Try(Unmarshal(entity).to[String].map(body => des.from(body))) match { @@ -71,36 +66,32 @@ class ClientHandlerImpl(val connection: Connection) extends ClientHandler { case HttpResponse(status, headers, entity, protocol) if (status.isFailure()) => throw new RuntimeException(status.reason()) - } - def get(endpoint: String): Future[HttpResponse] = { - logger.info("Get - URL {}", endpoint) - connection.executeCall(withHttpRequest(endpoint, HttpMethods.GET, Nil, connection.tokenProvider, None)) } - def get4Java[T](endpoint: String, des: Deserializer[T]): java.util.concurrent.Future[Optional[T]] = { - FutureConversions.fromFuture2Future(get(endpoint).flatMap(deserialize4Java(_, des))) + def get[T](endpoint: String, des: Deserializer[T]): java.util.concurrent.Future[Optional[T]] = { + FutureConversions.fromFuture2Future(connection.get(endpoint).flatMap(deserialize(_, des))) } - def get4Java[T](endpoint: String, headers: Seq[HttpHeader], des: Deserializer[T]): java.util.concurrent.Future[Optional[T]] = { - FutureConversions.fromFuture2Future(connection.executeCall(withHttpRequest(endpoint, HttpMethods.GET, headers, connection.tokenProvider, None)).flatMap(deserialize4Java(_, des))) + def get[T](endpoint: String, headers: Seq[HttpHeader], des: Deserializer[T]): java.util.concurrent.Future[Optional[T]] = { + FutureConversions.fromFuture2Future(connection.executeCall(withHttpRequest(endpoint, HttpMethods.GET, headers, connection.tokenProvider, None)).flatMap(deserialize(_, des))) } - def post4Java[T](endpoint: String, model: T)(implicit serializer: Serializer[T]): java.util.concurrent.Future[Void] = { + def post[T](endpoint: String, model: T)(implicit serializer: Serializer[T]): java.util.concurrent.Future[Void] = { val entity = serializer.to(model) logger.info("Posting to endpoint {}", endpoint) logger.debug("Data to post {}", entity) val result = connection.executeCall( - withHttpRequestAndPayload(endpoint, serialize4Java(model), HttpMethods.POST, connection.tokenProvider)) - .flatMap(response4Java(_)) + withHttpRequestAndPayload(endpoint, serialize(model), HttpMethods.POST, connection.tokenProvider)) + .flatMap(response(_)) FutureConversions.fromOption2Void(result) } - private def serialize4Java[T](model: T)(implicit serializer: Serializer[T]): String = + private def serialize[T](model: T)(implicit serializer: Serializer[T]): String = Try(serializer.to(model)) match { case Success(result) => result case Failure(error) => throw new RuntimeException("Failed to serialize: " + error.getMessage) } - private def response4Java[T](response: HttpResponse): Future[Option[String]] = response match { + private def response[T](response: HttpResponse): Future[Option[String]] = response match { case HttpResponse(status, headers, entity, protocol) if (status.isSuccess()) => Try(Unmarshal(entity).to[String]) match { case Success(result) => result.map(Option(_)) @@ -115,16 +106,20 @@ class ClientHandlerImpl(val connection: Connection) extends ClientHandler { } } - def subscribeJava[T <: JEvent](url: String, parameters: JStreamParameters, listener: JListener[T])(implicit des: Deserializer[JEventStreamBatch[T]]) = + def subscribe[T <: JEvent](eventTypeName: String, endpoint: String, parameters: JStreamParameters, listener: JListener[T])(implicit des: Deserializer[JEventStreamBatch[T]]) = FutureConversions.fromFuture2FutureVoid { (Future { import ModelConverter._ val params: Option[ScalaStreamParameters] = toScalaStreamParameters(parameters) val eventHandler: EventHandler = new EventHandlerImpl[T, EmptyScalaEvent](Left((des, listener))) - val finalUrl = withUrl(url, params) - subscriber.subscribe(url, getCursor(params), eventHandler) + val finalUrl = withUrl(endpoint, params) + subscriber.subscribe(eventTypeName, endpoint, getCursor(params), eventHandler) }) } + + def unsubscribe[T <: JEvent](eventTypeName: String,partition:String, listener: JListener[T]) = { + subscriber.unsubscribe(eventTypeName,partition, listener.getId) + } private def getCursor(params: Option[ScalaStreamParameters]): Option[ScalaCursor] = params match { case Some(ScalaStreamParameters(cursor, batchLimit, streamLimit, batchFlushTimeout, streamTimeout, streamKeepAliveLimit, flowId)) => cursor case None => None diff --git a/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala index dd616a4..4ef654c 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala @@ -21,70 +21,70 @@ import org.zalando.nakadi.client.scala.model._ import org.zalando.nakadi.client.utils.Uri import com.fasterxml.jackson.core.`type`.TypeReference import org.zalando.nakadi.client.handler.SubscriptionHandlerImpl +import org.zalando.nakadi.client.handler.SubscriptionHandler -private[scala] class ClientImpl(connection: Connection, charSet: String = "UTF-8") extends Client { +class ClientImpl(connection: Connection, subscriber: SubscriptionHandler, charSet: String = "UTF-8") extends Client { import Uri._ import JacksonJsonMarshaller._ import HttpFactory._ implicit val materializer = connection.materializer() private val logger = Logger(LoggerFactory.getLogger(this.getClass)) - private val subscriber = new SubscriptionHandlerImpl(connection) def getMetrics(): Future[Either[ClientError, Option[Metrics]]] = { - logFutureEither(get(URI_METRICS).flatMap(mapToEither(_)(deserializer(metricsTR)))) + logFutureEither(connection.get(URI_METRICS).flatMap(mapToEither(_)(deserializer(metricsTR)))) } def getEventTypes(): Future[Either[ClientError, Option[Seq[EventType]]]] = { - logFutureEither(get(URI_EVENT_TYPES).flatMap(mapToEither(_)(deserializer(listOfEventTypeTR)))) + logFutureEither(connection.get(URI_EVENT_TYPES).flatMap(mapToEither(_)(deserializer(listOfEventTypeTR)))) } def createEventType(eventType: EventType): Future[Option[ClientError]] = { - logFutureOption(post(URI_EVENT_TYPES, eventType).flatMap(mapToOption(_))) + logFutureOption(connection.post(URI_EVENT_TYPES, eventType).flatMap(mapToOption(_))) } def getEventType(name: String): Future[Either[ClientError, Option[EventType]]] = { - logFutureEither(get(URI_EVENT_TYPE_BY_NAME.format(name)).flatMap(in => mapToEither(in)(deserializer(eventTypeTR)))) + logFutureEither(connection.get(URI_EVENT_TYPE_BY_NAME.format(name)).flatMap(in => mapToEither(in)(deserializer(eventTypeTR)))) } def updateEventType(name: String, eventType: EventType): Future[Option[ClientError]] = { - val result = put(URI_EVENT_TYPE_BY_NAME.format(name), eventType) + val result = connection.put(URI_EVENT_TYPE_BY_NAME.format(name), eventType) logFutureOption(result.flatMap(in => mapToOption(in))) } def deleteEventType(name: String): Future[Option[ClientError]] = { - logFutureOption(delete(URI_EVENT_TYPE_BY_NAME.format(name)).flatMap(in => mapToOption(in))) + logFutureOption(connection.delete(URI_EVENT_TYPE_BY_NAME.format(name)).flatMap(in => mapToOption(in))) } def publishEvents[T <: Event](eventTypeName: String, events: Seq[T], ser: Serializer[Seq[T]]): Future[Option[ClientError]] = { - logFutureOption(post(URI_EVENTS_OF_EVENT_TYPE.format(eventTypeName), events).flatMap(in => mapToOption(in))) + logFutureOption(connection.post(URI_EVENTS_OF_EVENT_TYPE.format(eventTypeName), events).flatMap(in => mapToOption(in))) } def publishEvents[T <: Event](eventTypeName: String, events: Seq[T]): Future[Option[ClientError]] = { - logFutureOption(post(URI_EVENTS_OF_EVENT_TYPE.format(eventTypeName), events).flatMap(in => mapToOption(in))) + logFutureOption(connection.post(URI_EVENTS_OF_EVENT_TYPE.format(eventTypeName), events).flatMap(in => mapToOption(in))) } def publishEvent[T <: Event](name: String, event: T, ser: Serializer[T]): Future[Option[ClientError]] = { - logFutureOption(post(URI_EVENTS_OF_EVENT_TYPE.format(name), event).flatMap(in => mapToOption(in))) + logFutureOption(connection.post(URI_EVENTS_OF_EVENT_TYPE.format(name), event).flatMap(in => mapToOption(in))) } def publishEvent[T <: Event](name: String, event: T): Future[Option[ClientError]] = { - logFutureOption(post(URI_EVENTS_OF_EVENT_TYPE.format(name), event).flatMap(in => mapToOption(in))) + logFutureOption(connection.post(URI_EVENTS_OF_EVENT_TYPE.format(name), event).flatMap(in => mapToOption(in))) } def getPartitions(name: String): Future[Either[ClientError, Option[Seq[Partition]]]] = { - logFutureEither(get(URI_PARTITIONS_BY_EVENT_TYPE.format(name)).flatMap(in => mapToEither(in)(deserializer(listOfPartitionTR)))) + logFutureEither(connection.get(URI_PARTITIONS_BY_EVENT_TYPE.format(name)).flatMap(in => mapToEither(in)(deserializer(listOfPartitionTR)))) } def getValidationStrategies(): Future[Either[ClientError, Option[Seq[EventValidationStrategy.Value]]]] = { - logFutureEither(get(URI_VALIDATION_STRATEGIES).flatMap(mapToEither(_)(deserializer(listOfEventValidationStrategyTR)))) + logFutureEither(connection.get(URI_VALIDATION_STRATEGIES).flatMap(mapToEither(_)(deserializer(listOfEventValidationStrategyTR)))) } def getEnrichmentStrategies(): Future[Either[ClientError, Option[Seq[EventEnrichmentStrategy.Value]]]] = { - logFutureEither(get(URI_ENRICHMENT_STRATEGIES).flatMap(mapToEither(_)(deserializer(listOfEventEnrichmentStrategyTR)))) + logFutureEither(connection.get(URI_ENRICHMENT_STRATEGIES).flatMap(mapToEither(_)(deserializer(listOfEventEnrichmentStrategyTR)))) } def getPartitioningStrategies(): Future[Either[ClientError, Option[Seq[PartitionStrategy.Value]]]] = - logFutureEither(get(URI_PARTITIONING_STRATEGIES).flatMap(mapToEither(_)(deserializer(listOfPartitionStrategyTR)))) + logFutureEither(connection.get(URI_PARTITIONING_STRATEGIES).flatMap(mapToEither(_)(deserializer(listOfPartitionStrategyTR)))) def stop(): Option[ClientError] = { val result = Await.ready(connection.actorSystem().terminate(), Duration.Inf) @@ -95,8 +95,8 @@ private[scala] class ClientImpl(connection: Connection, charSet: String = "UTF-8 subscribe(eventTypeName, parameters, listener)(deserializer(typeRef)) } - def subscribe[T <: Event](eventType: String, params: StreamParameters, listener: Listener[T])(implicit des: Deserializer[EventStreamBatch[T]]): Future[Option[ClientError]] = - (eventType, params, listener) match { + def subscribe[T <: Event](eventTypeName: String, params: StreamParameters, listener: Listener[T])(implicit des: Deserializer[EventStreamBatch[T]]): Future[Option[ClientError]] = + (eventTypeName, params, listener) match { case (_, _, listener) if listener == null => logger.info("listener is null") @@ -110,49 +110,20 @@ private[scala] class ClientImpl(connection: Connection, charSet: String = "UTF-8 val url = URI_EVENTS_OF_EVENT_TYPE.format(eventType) logger.debug("Subscribing listener {} - cursor {} - parameters {} - eventType {} - url {}", listener.id, cursor, params, eventType, url) val finalUrl = withUrl(url, Some(params)) - subscribe(finalUrl, cursor, listener)(des) val eventHandler: EventHandler = new EventHandlerImpl[EmptyJavaEvent, T](Right((des, listener))) - subscriber.subscribe(finalUrl, cursor, eventHandler) + subscriber.subscribe(eventTypeName, finalUrl, cursor, eventHandler) Future.successful(None) } - def unsubscribe[T <: Event](eventType: String, listener: Listener[T]): Future[Option[ClientError]] = ??? - - def unsubscribe[T <: Event](endpoint: String, cursor: Option[Cursor], listener: Listener[T]): Unit = { - val eventHandler: EventHandler = new EventHandlerImpl[EmptyJavaEvent, T](Right((null, listener))) - subscriber.subscribe(endpoint, cursor, eventHandler) + def unsubscribe[T <: Event](eventTypeName: String, partition: String, listener: Listener[T]): Future[Option[ClientError]] = { + subscriber.unsubscribe(eventTypeName, partition, listener.id) + Future.successful(None) } //#################### //# HELPER METHODS # //#################### - private def get(endpoint: String): Future[HttpResponse] = { - logger.info("Get - URL {}", endpoint) - connection.executeCall(withHttpRequest(endpoint, HttpMethods.GET, Nil, connection.tokenProvider, None)) - } - private def delete(endpoint: String): Future[HttpResponse] = { - logger.info("Delete: {}", endpoint) - connection.executeCall(withHttpRequest(endpoint, HttpMethods.DELETE, Nil, connection.tokenProvider, None)) - } - - def put[T](endpoint: String, model: T)(implicit serializer: Serializer[T]): Future[HttpResponse] = { - logger.info("Get: {}", endpoint) - connection.executeCall(withHttpRequest(endpoint, HttpMethods.GET, Nil, connection.tokenProvider, None)) - } - - def post[T](endpoint: String, model: T)(implicit serializer: Serializer[T]): Future[HttpResponse] = { - val entity = serializer.to(model) - logger.info("Posting to endpoint {}", endpoint) - logger.debug("Data to post {}", entity) - connection.executeCall(withHttpRequestAndPayload(endpoint, entity, HttpMethods.POST, connection.tokenProvider)) - } - - def subscribe[T <: Event](endpoint: String, cursor: Option[Cursor], listener: Listener[T])(implicit des: Deserializer[EventStreamBatch[T]]) = { - val eventHandler: EventHandler = new EventHandlerImpl[EmptyJavaEvent, T](Right((des, listener))) - subscriber.subscribe(endpoint, cursor, eventHandler) - } - private def logFutureEither[A, T](future: Future[Either[ClientError, T]]): Future[Either[ClientError, T]] = { future recover { case e: Throwable => diff --git a/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala index fa6f695..65ae9c7 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala @@ -9,11 +9,11 @@ import scala.concurrent.Future import scala.util.Try import org.slf4j.LoggerFactory -import org.zalando.nakadi.client.java.{ ClientHandler => JClientHandler } -import org.zalando.nakadi.client.java.{ ClientHandlerImpl => JClientHandlerImpl } +import org.zalando.nakadi.client.java.JavaClientHandler +import org.zalando.nakadi.client.java.JavaClientHandlerImpl import org.zalando.nakadi.client.java.model.{ Event => JEvent } import org.zalando.nakadi.client.scala.model.Event - +import org.zalando.nakadi.client.Serializer import com.typesafe.scalalogging.Logger import HttpFactory.TokenProvider @@ -32,6 +32,8 @@ import akka.stream.scaladsl.Source import javax.net.ssl.SSLContext import javax.net.ssl.TrustManager import javax.net.ssl.X509TrustManager +import akka.http.scaladsl.model.{ HttpHeader, HttpMethod, HttpMethods, HttpResponse, MediaRange } +import org.zalando.nakadi.client.handler.SubscriptionHandlerImpl sealed trait EmptyJavaEvent extends JEvent sealed trait EmptyScalaEvent extends Event @@ -41,7 +43,10 @@ trait Connection { import HttpFactory.TokenProvider def tokenProvider(): Option[TokenProvider] def executeCall(request: HttpRequest): Future[HttpResponse] - + def get(endpoint: String): Future[HttpResponse] + def delete(endpoint: String): Future[HttpResponse] + def put[T](endpoint: String, model: T)(implicit serializer: Serializer[T]): Future[HttpResponse] + def post[T](endpoint: String, model: T)(implicit serializer: Serializer[T]): Future[HttpResponse] def requestFlow(): Flow[HttpRequest, HttpResponse, Future[Http.OutgoingConnection]] def materializer(): ActorMaterializer def actorSystem(): ActorSystem @@ -70,16 +75,14 @@ object Connection { case _ => None } - /** - * Creates a new Client Handler - */ def newClient(host: String, port: Int, tokenProvider: Option[() => String], securedConnection: Boolean, verifySSlCertificate: Boolean): Client = { val connection = new ConnectionImpl(host, port, tokenProvider, securedConnection, verifySSlCertificate) - new ClientImpl(connection) + new ClientImpl(connection, new SubscriptionHandlerImpl(connection)) } - def newClientHandler4Java(host: String, port: Int, tokenProvider: Option[() => String], securedConnection: Boolean, verifySSlCertificate: Boolean): JClientHandler = { - new JClientHandlerImpl(new ConnectionImpl(host, port, tokenProvider, securedConnection, verifySSlCertificate)) + def newClientHandler4Java(host: String, port: Int, tokenProvider: Option[() => String], securedConnection: Boolean, verifySSlCertificate: Boolean): JavaClientHandler = { + val connection = new ConnectionImpl(host, port, tokenProvider, securedConnection, verifySSlCertificate) + new JavaClientHandlerImpl(connection, new SubscriptionHandlerImpl(connection)) } } @@ -112,6 +115,27 @@ sealed class ConnectionImpl(val host: String, val port: Int, val tokenProvider: http.outgoingConnection(host, port) } + def get(endpoint: String): Future[HttpResponse] = { + logger.info("Get - URL {}", endpoint) + executeCall(withHttpRequest(endpoint, HttpMethods.GET, Nil, tokenProvider, None)) + } + def delete(endpoint: String): Future[HttpResponse] = { + logger.info("Delete: {}", endpoint) + executeCall(withHttpRequest(endpoint, HttpMethods.DELETE, Nil, tokenProvider, None)) + } + + def put[T](endpoint: String, model: T)(implicit serializer: Serializer[T]): Future[HttpResponse] = { + logger.info("Get: {}", endpoint) + executeCall(withHttpRequest(endpoint, HttpMethods.GET, Nil, tokenProvider, None)) + } + + def post[T](endpoint: String, model: T)(implicit serializer: Serializer[T]): Future[HttpResponse] = { + val entity = serializer.to(model) + logger.info("Posting to endpoint {}", endpoint) + logger.debug("Data to post {}", entity) + executeCall(withHttpRequestAndPayload(endpoint, entity, HttpMethods.POST, tokenProvider)) + } + def executeCall(request: HttpRequest): Future[HttpResponse] = { val response: Future[HttpResponse] = Source.single(request) diff --git a/client/src/main/scala/org/zalando/nakadi/client/scala/MessageHandler.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/MessageHandler.scala index 5f16d8b..d6543d9 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/scala/MessageHandler.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/scala/MessageHandler.scala @@ -40,9 +40,9 @@ case class ErrorResult(error: Throwable) extends Result trait EventHandler { def id(): String - def handleOnReceive(url: String, msg: String): Either[ErrorResult, Cursor] + def handleOnReceive(eventTypeName: String, msg: String): Either[ErrorResult, Cursor] def handleOnSubscribed(endpoint: String, cursor: Option[Cursor]): Unit - def handleOnError(url: String, msg: Option[String], exception: Throwable) + def handleOnError(eventTypeName: String, msg: Option[String], exception: Throwable) } class EventHandlerImpl[J <: JEvent, S <: Event]( @@ -63,17 +63,17 @@ class EventHandlerImpl[J <: JEvent, S <: Event]( } } - def handleOnReceive(url: String, msg: String): Either[ErrorResult, Cursor] = { + def handleOnReceive(eventTypeName: String, msg: String): Either[ErrorResult, Cursor] = { eitherOfListeners match { case Left((des, listener)) => // Java transformJava(msg, des).right.flatMap { case JavaResult(Some(events), sCursor, jCursor) => - listener.onReceive(url, jCursor, events) + listener.onReceive(eventTypeName, jCursor, events) Right(sCursor) case JavaResult(None, sCursor, jCursor) => Right(sCursor) case _ => - val errorMsg = s"Could not handle JAVA Transformation url [$url] listener [${listener.getId}] msg [$msg]" + val errorMsg = s"Could not handle JAVA Transformation url [$eventTypeName] listener [${listener.getId}] msg [$msg]" log.error(errorMsg) listener.onError(errorMsg, Optional.empty()) Left(ErrorResult(createException(errorMsg))) @@ -81,12 +81,12 @@ class EventHandlerImpl[J <: JEvent, S <: Event]( case Right((des, listener)) => //Scala transformScala(msg, des).right.flatMap { case ScalaResult(Some(events), cursor) => - listener.onReceive(url, cursor, events) + listener.onReceive(eventTypeName, cursor, events) Right(cursor) case ScalaResult(None, cursor) => Right(cursor) case _ => - val errorMsg = s"Could not handle SCALA Transformation url [$url] listener [${listener.id}] msg [$msg]" + val errorMsg = s"Could not handle SCALA Transformation url [$eventTypeName] listener [${listener.id}] msg [$msg]" listener.onError(errorMsg, None) log.error(errorMsg) Left(ErrorResult(createException(errorMsg))) @@ -94,7 +94,7 @@ class EventHandlerImpl[J <: JEvent, S <: Event]( } } - def handleOnError(url: String, msg: Option[String], exception: Throwable) = { + def handleOnError(eventTypeName: String, msg: Option[String], exception: Throwable) = { val errorMsg = if (msg.isDefined) msg.get else exception.getMessage val clientError = Some(ClientError(errorMsg, exception = Some(exception))) eitherOfListeners match { diff --git a/client/src/test/scala/org/zalando/nakadi/client/scala/ClientTest.scala b/client/src/test/scala/org/zalando/nakadi/client/scala/ClientTest.scala index d46a977..9fe0e5a 100644 --- a/client/src/test/scala/org/zalando/nakadi/client/scala/ClientTest.scala +++ b/client/src/test/scala/org/zalando/nakadi/client/scala/ClientTest.scala @@ -24,12 +24,15 @@ import akka.http.scaladsl.model.HttpEntity import akka.http.scaladsl.model.ContentTypes import org.zalando.nakadi.client.scala.model.JacksonJsonMarshaller import org.zalando.nakadi.client.utils.Uri +import org.zalando.nakadi.client.handler.SubscriptionHandlerImpl +import org.zalando.nakadi.client.handler.SubscriptionHandler -class ClientTest extends WordSpec with Matchers with MockitoSugar with BeforeAndAfter { +class ClientTest extends WordSpec with Matchers with MockitoSugar with BeforeAndAfter { import JacksonJsonMarshaller._ import Uri._ private var connection: Connection = mock[Connection] - private val client: Client = new ClientImpl(connection) + private var subscriber: SubscriptionHandler = mock[SubscriptionHandler] + private val client: Client = new ClientImpl(connection, subscriber) before { reset(connection) } @@ -40,7 +43,7 @@ class ClientTest extends WordSpec with Matchers with MockitoSugar with BeforeAn val entity = HttpEntity(ContentTypes.`application/json`, "abc") val response = new HttpResponse(StatusCodes.NotFound, headers, entity, HttpProtocols.`HTTP/1.1`) val futureResponse = Future.successful(response) -// when(connection.get(URI_PARTITIONING_STRATEGIES)).thenReturn(futureResponse) + // when(connection.get(URI_PARTITIONING_STRATEGIES)).thenReturn(futureResponse) // val result=Await.result(client.partitionStrategies(),500.seconds) // result.isLeft shouldBe true // val Left(clientError) = result diff --git a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala index 12980e0..bef8517 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala @@ -44,7 +44,7 @@ object EventCreationExample extends App { //See the API for more information on the EventType model //https://github.com/zalando/nakadi/blob/nakadi-jvm/api/nakadi-event-bus-api.yaml#L1240 // val eventTypeName = "Example-unique-million-messages" - val eventTypeName = "Example-unique-million-messages" + val eventTypeName = "Example-1" // val eventTypeName = "Example-unique-hundred-messages-3" val owner = "team-laas" @@ -68,14 +68,14 @@ object EventCreationExample extends App { //You need to import the default Serializer if you don't sepecify your own! import JacksonJsonMarshaller._ - client.createEventType(eventType) +// client.createEventType(eventType) // 4. Publish the EventType var counter = 0 - for (n <- 1 to 1000) { + for (n <- 1 to 1) { val event = new MeetingsEvent("2016-04-28T13:28:15+00:00", "Hackaton") var events = ListBuffer[MeetingsEvent]() - for (a <- 1 to 1000) { + for (a <- 1 to 1) { counter += 1 events += MeetingsEvent("2016-04-28T13:28:15+00:00", "Hackaton" + counter) } diff --git a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala index e1acb34..0371d4e 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala @@ -60,7 +60,7 @@ object EventListenerExample extends App { val parameters = new StreamParameters( cursor = Some(cursor) // - , batchLimit = Some(1) // Maximum number of `Event`s in each chunk (and therefore per partition) of the stream. + , batchLimit = Some(100) // Maximum number of `Event`s in each chunk (and therefore per partition) of the stream. // , streamLimit = Some(2) // Maximum number of `Event`s to stream (over all partitions being streamed in this //connection). // , batchFlushTimeout = Some(5) // Maximum time in seconds to wait for the flushing of each chunk (per partition). @@ -76,7 +76,13 @@ object EventListenerExample extends App { import org.zalando.nakadi.client.scala.model.JacksonJsonMarshaller._ // val eventTypeName = "Event-example-with-0-messages" - val eventTypeName = "Example-unique-million-messages" + val eventTypeName = "Example-1" val result = client.subscribe(eventTypeName, parameters, listener) + + Thread.sleep(5000) + + client.unsubscribe(eventTypeName,"0", listener) + + client.subscribe(eventTypeName, parameters, listener) } \ No newline at end of file diff --git a/project/Build.scala b/project/Build.scala index f8337dc..82f726f 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -10,6 +10,8 @@ EclipseKeys.createSrc := EclipseCreateSrc.Default + EclipseCreateSrc.Resource EclipseKeys.withSource := true +private val commonSettings = net.virtualvoid.sbt.graph.DependencyGraphSettings.graphSettings + def whereToPublishTo(isItSnapshot:Boolean) = { val nexus = "https://maven.zalando.net/" if (isItSnapshot) @@ -37,41 +39,40 @@ lazy val root = project.in(file(".")) .aggregate(api, client) lazy val api = withDefaults( - "nakadi-clients-api", + "nakadi-klients-api", project.in(file("api")) - ,true ).settings(libraryDependencies ++= apiDeps) lazy val client = withDefaults( - "nakadi-clients", + "nakadi-klients", project.in(file("client")).dependsOn(api) - ,true ).settings(libraryDependencies ++= clientDeps) lazy val it = withDefaults( - "nakadi-integration-test", + "nakadi-klients-integration-test", project.in(file("it")).dependsOn(api, client) ).settings(libraryDependencies ++= clientDeps) lazy val e2e = withDefaults( - "nakadi-end-2-end-test", + "nakadi-klients-end-2-end-test", project.in(file("e2e")).dependsOn(api, client, it) ).settings(libraryDependencies ++= clientDeps) - def withDefaults(projectName:String, project:sbt.Project, publish:Boolean = false)={ + def withDefaults(projectName:String, project:sbt.Project)={ project.settings( name := projectName, - organization := "org.zalando.laas", - version := "2.0.0-pre-alpha", + organization := "org.zalando.nakadi.client", + version := "2.0.0-pre-alpha.2", crossPaths := false, scalaVersion := "2.11.7", publishTo := whereToPublishTo(isSnapshot.value), resolvers += Resolver.mavenLocal, resolvers += "Maven Central Server" at "http://repo1.maven.org/maven2", - scalacOptions ++= defaultOptions) + scalacOptions ++= defaultOptions, + publishArtifact in (Test, packageBin) := false) .configs(Configs.all: _*) /*.settings( publishArtifact in (Compile, packageDoc) := true //To Publish or Not to Publish scala doc jar diff --git a/project/Dependencies.scala b/project/Dependencies.scala index 4c380d3..74b1770 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -6,36 +6,26 @@ object Dependencies { val apiDeps = { Seq( - "com.fasterxml.jackson.module" %% "jackson-module-scala" % "2.6.3" withSources() withJavadoc(), - "com.fasterxml.jackson.core" % "jackson-core" % "2.7.0" withSources() withJavadoc()) + "com.fasterxml.jackson.module" %% "jackson-module-scala" % "2.6.3", + "com.fasterxml.jackson.core" % "jackson-core" % "2.7.0") } val clientDeps = { Seq( - "com.typesafe.akka" %% "akka-http-spray-json-experimental" % akkaVersion withSources() withJavadoc(), - "com.typesafe.akka" %% "akka-actor" % akkaVersion withSources() withJavadoc(), - "com.typesafe.akka" %% "akka-http-experimental" % akkaVersion withSources() withJavadoc(), - "com.typesafe.akka" %% "akka-stream" % akkaVersion withSources() withJavadoc(), - "com.typesafe.akka" %% "akka-cluster-metrics" % akkaVersion withSources() withJavadoc(), - "com.typesafe.akka" %% "akka-testkit" % akkaVersion % "test" withSources() withJavadoc(), - "joda-time" % "joda-time" % "2.9.3" withSources() withJavadoc(), - "org.joda" % "joda-convert" % "1.8.1" withSources() withJavadoc(), - "com.typesafe" % "config" % "1.3.0" withSources() withJavadoc(), - "com.google.guava" % "guava" % "19.0" withSources() withJavadoc(), - "com.typesafe.scala-logging" %% "scala-logging" % "3.1.0" withSources() withJavadoc(), - "io.spray" %% "spray-json" % "1.3.2" withSources() withJavadoc(), - "com.fasterxml.jackson.core" % "jackson-core" % "2.7.0" withSources() withJavadoc(), - "com.fasterxml.jackson.module" %% "jackson-module-scala" % "2.6.3" withSources() withJavadoc(), - "ch.qos.logback" % "logback-classic" % "1.1.3" withSources() withJavadoc(), - "joda-time" % "joda-time" % "2.9.3" withSources() withJavadoc(), - "org.joda" % "joda-convert" % "1.8.1" withSources() withJavadoc(), - "org.scalatest" %% "scalatest" % "2.2.6" % "test" withSources() withJavadoc(), - //"io.undertow" % "undertow-core" % "1.2.12.Final" % "test" withSources() withJavadoc(), - //"io.undertow" % "undertow-servlet" % "1.2.12.Final" % "test" withSources() withJavadoc(), - "org.apache.commons" % "commons-io" % "1.3.2" % "test" withSources() withJavadoc(), - //"com.google.code.findbugs" % "jsr305" % "1.3.9" % "test" withSources() withJavadoc(), - "junit" % "junit" % "4.12" % "test" withSources() withJavadoc(), - "org.mockito" % "mockito-core" % "1.10.19" % "test" withSources() withJavadoc() + "com.typesafe" % "config" % "1.3.0", + "com.google.guava" % "guava" % "19.0", + "com.typesafe.scala-logging" %% "scala-logging" % "3.1.0", + "com.fasterxml.jackson.core" % "jackson-core" % "2.7.0", + "com.fasterxml.jackson.module" %% "jackson-module-scala" % "2.6.3", + "ch.qos.logback" % "logback-classic" % "1.1.3", + "com.typesafe.akka" %% "akka-actor" % akkaVersion, + "com.typesafe.akka" %% "akka-http-experimental" % akkaVersion, + "com.typesafe.akka" %% "akka-stream" % akkaVersion, + "com.typesafe.akka" %% "akka-testkit" % akkaVersion % "test", + "org.scalatest" %% "scalatest" % "2.2.6" % "test", + "com.google.code.findbugs" % "jsr305" % "1.3.9" % "test", + "junit" % "junit" % "4.12" % "test", + "org.mockito" % "mockito-core" % "1.10.19" % "test" ) } } diff --git a/project/plugins.sbt b/project/plugins.sbt index ca80229..92199b6 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -9,3 +9,5 @@ addSbtPlugin("org.scoverage" % "sbt-coveralls" % "1.0.0") addSbtPlugin("com.codacy" % "sbt-codacy-coverage" % "1.1.0") addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "4.0.0") + +addSbtPlugin("net.virtual-void" % "sbt-dependency-graph" % "0.8.2") From d653537b59bca93abf07bd9311007492650951b5 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Fri, 27 May 2016 10:56:51 +0200 Subject: [PATCH 057/183] techjira:LAAS-60 Renamed actors --- .../{EventConsumingActor.scala => ConsumingActor.scala} | 6 +++--- .../org/zalando/nakadi/client/actor/SupervisingActor.scala | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) rename client/src/main/scala/org/zalando/nakadi/client/actor/{EventConsumingActor.scala => ConsumingActor.scala} (96%) diff --git a/client/src/main/scala/org/zalando/nakadi/client/actor/EventConsumingActor.scala b/client/src/main/scala/org/zalando/nakadi/client/actor/ConsumingActor.scala similarity index 96% rename from client/src/main/scala/org/zalando/nakadi/client/actor/EventConsumingActor.scala rename to client/src/main/scala/org/zalando/nakadi/client/actor/ConsumingActor.scala index fb4efb5..dae986a 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/actor/EventConsumingActor.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/actor/ConsumingActor.scala @@ -35,15 +35,15 @@ import org.zalando.nakadi.client.java.model.{ Event => JEvent } * */ -object EventConsumingActor { +object ConsumingActor { case class Shutdown(handler: EventHandler) } -class EventConsumingActor(eventType: String, +class ConsumingActor(eventType: String, handler: EventHandler) extends Actor with ActorLogging with ActorSubscriber { import ModelConverter._ - import EventConsumingActor._ + import ConsumingActor._ var initialCursor: Option[Cursor] = null diff --git a/client/src/main/scala/org/zalando/nakadi/client/actor/SupervisingActor.scala b/client/src/main/scala/org/zalando/nakadi/client/actor/SupervisingActor.scala index b874496..fa6496f 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/actor/SupervisingActor.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/actor/SupervisingActor.scala @@ -86,7 +86,7 @@ object SupervisingActor { class SupervisingActor(val connection: Connection, val subscriptionHandler: SubscriptionHandler) extends Actor with ActorLogging { import SupervisingActor._ - import EventConsumingActor._ + import ConsumingActor._ type SubscriptionKey = (String,String) //EventTypeName,PartitionId type SubscriptionEntry = (EventHandler, ActorRef) private var subscriptions: Map[SubscriptionKey, SubscriptionEntry] = Map() //SubscriptionKey , SubscriptionEntry @@ -105,7 +105,7 @@ class SupervisingActor(val connection: Connection, val subscriptionHandler: Subs log.info("Subscription nr {} for eventType {} and listener {}", (subscriptions.size + 1), eventTypeName, eventHandler.id()) //Create the Consumer - val consumingActor = context.actorOf(Props(classOf[EventConsumingActor], endpoint, eventHandler), "EventConsumingActor-" + subscriptions.size) + val consumingActor = context.actorOf(Props(classOf[ConsumingActor], endpoint, eventHandler), "EventConsumingActor-" + subscriptions.size) val consumer = ActorSubscriber[ByteString](consumingActor) // Notify listener it is subscribed From 18cd81787b466de18e853c170cc7f7fc5df54227 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Fri, 27 May 2016 10:57:45 +0200 Subject: [PATCH 058/183] techjira:LAAS-60 Renamed actors --- .../scala/org/zalando/nakadi/client/actor/ConsumingActor.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/main/scala/org/zalando/nakadi/client/actor/ConsumingActor.scala b/client/src/main/scala/org/zalando/nakadi/client/actor/ConsumingActor.scala index dae986a..e3959e7 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/actor/ConsumingActor.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/actor/ConsumingActor.scala @@ -55,7 +55,7 @@ class ConsumingActor(eventType: String, override def receive: Receive = { case OnNext(msg: ByteString) => - import util.Random +// import util.Random // if (Random.nextFloat() > 0.9 && Random.nextBoolean() && Random.nextBoolean()) // throw new IllegalStateException("OMG, not again!") val message = msg.utf8String From 519990c4993500de3d744a08cfa8c8f1a830ecd5 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Fri, 27 May 2016 11:45:56 +0200 Subject: [PATCH 059/183] techjira:LAAS-60 Fixes #48, #50. Upgrading jackson-scala-module to 2.7.3 --- .../java/model/JavaJacksonJsonMarshaller.scala | 2 +- .../scala/model/ScalaJacksonJsonMarshaller.scala | 2 +- .../org/zalando/nakadi/client/scala/ClientTest.scala | 1 - .../client/scala/DeserializerSerializerTest.scala | 8 ++++---- .../zalando/nakadi/client/scala/TestFactory.scala | 4 ++-- .../nakadi/client/utils/ModelConverterTest.scala | 12 ++---------- .../nakadi/client/KlientIntegrationTest.scala | 1 - project/Build.scala | 2 +- project/Dependencies.scala | 4 ++-- 9 files changed, 13 insertions(+), 23 deletions(-) diff --git a/client/src/main/scala/org/zalando/nakadi/client/java/model/JavaJacksonJsonMarshaller.scala b/client/src/main/scala/org/zalando/nakadi/client/java/model/JavaJacksonJsonMarshaller.scala index 0c2c76b..b4ed63e 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/java/model/JavaJacksonJsonMarshaller.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/java/model/JavaJacksonJsonMarshaller.scala @@ -68,7 +68,7 @@ object JavaJacksonJsonMarshaller { lazy val defaultObjectMapper: ObjectMapper = new ObjectMapper() // .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) - .setPropertyNamingStrategy(PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES) + .setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE) .setSerializationInclusion(JsonInclude.Include.NON_EMPTY) .addHandler(new DeserializationProblemHandler() { override def handleUnknownProperty(ctxt: DeserializationContext, diff --git a/client/src/main/scala/org/zalando/nakadi/client/scala/model/ScalaJacksonJsonMarshaller.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/model/ScalaJacksonJsonMarshaller.scala index 4940227..ef2f346 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/scala/model/ScalaJacksonJsonMarshaller.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/scala/model/ScalaJacksonJsonMarshaller.scala @@ -67,7 +67,7 @@ object JacksonJsonMarshaller { .registerModule(new DefaultScalaModule) .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) - .setPropertyNamingStrategy(PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES) + .setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE) .setSerializationInclusion(JsonInclude.Include.NON_NULL) .addHandler(new DeserializationProblemHandler() { override def handleUnknownProperty(ctxt: DeserializationContext, diff --git a/client/src/test/scala/org/zalando/nakadi/client/scala/ClientTest.scala b/client/src/test/scala/org/zalando/nakadi/client/scala/ClientTest.scala index 9fe0e5a..4219eb1 100644 --- a/client/src/test/scala/org/zalando/nakadi/client/scala/ClientTest.scala +++ b/client/src/test/scala/org/zalando/nakadi/client/scala/ClientTest.scala @@ -16,7 +16,6 @@ import org.scalatest.WordSpec import org.scalatest.mock.MockitoSugar import akka.http.scaladsl.model.HttpResponse import akka.stream.Materializer -import spray.json.JsonFormat import akka.http.scaladsl.model.StatusCodes import akka.http.scaladsl.model.HttpProtocol import akka.http.scaladsl.model.HttpProtocols diff --git a/client/src/test/scala/org/zalando/nakadi/client/scala/DeserializerSerializerTest.scala b/client/src/test/scala/org/zalando/nakadi/client/scala/DeserializerSerializerTest.scala index 2328086..8296cc1 100644 --- a/client/src/test/scala/org/zalando/nakadi/client/scala/DeserializerSerializerTest.scala +++ b/client/src/test/scala/org/zalando/nakadi/client/scala/DeserializerSerializerTest.scala @@ -9,7 +9,7 @@ import org.zalando.nakadi.client.utils.AkkaConfig import org.zalando.nakadi.client.utils.TestJsonEntity import akka.http.scaladsl.unmarshalling.Unmarshaller import akka.stream.Materializer -import spray.json.JsonFormat +/*import spray.json.JsonFormat import org.zalando.nakadi.client.scala.model.JacksonJsonMarshaller class DeserializerSerializerTest extends WordSpec with Matchers with AkkaConfig { @@ -27,8 +27,8 @@ class DeserializerSerializerTest extends WordSpec with Matchers with AkkaConfig } def checkDeserializationProcessSerialization[T](key: String, input: String)(implicit m: JsonFormat[T] ) { - import spray.json._ - val json = input.parseJson +// import spray.json._ +// val json = input.parseJson println(">>>>>>> IN " + json.compactPrint) val scalaModel = json.convertTo[T] val jsonResult = scalaModel.toJson @@ -38,4 +38,4 @@ class DeserializerSerializerTest extends WordSpec with Matchers with AkkaConfig assert(jsonResult == json, s"Failed to marshall $key") } -} \ No newline at end of file +}*/ \ No newline at end of file diff --git a/client/src/test/scala/org/zalando/nakadi/client/scala/TestFactory.scala b/client/src/test/scala/org/zalando/nakadi/client/scala/TestFactory.scala index aabaaf6..814ead8 100644 --- a/client/src/test/scala/org/zalando/nakadi/client/scala/TestFactory.scala +++ b/client/src/test/scala/org/zalando/nakadi/client/scala/TestFactory.scala @@ -4,7 +4,6 @@ import scala.concurrent.Await import scala.concurrent.Future import scala.concurrent.duration.DurationInt import scala.util.Random -import org.joda.time.DateTime import org.zalando.nakadi.client.scala.model._ import org.zalando.nakadi.client.scala.model.JacksonJsonMarshaller import org.zalando.nakadi.client.Serializer @@ -12,6 +11,7 @@ import org.zalando.nakadi.client.scala.model.JacksonJsonMarshaller import org.zalando.nakadi.client.Deserializer import org.zalando.nakadi.client.Deserializer import org.zalando.nakadi.client.Serializer +import java.util.Calendar case class EventActions(client: Client) { @@ -60,7 +60,7 @@ trait ModelFactory { def createEventMetadata(): EventMetadata = { val length = 5 val eid = java.util.UUID.randomUUID.toString - val occurredAt = new DateTime().toString() + val occurredAt = Calendar.getInstance().toString() new EventMetadata(eid, None, occurredAt, None, Nil, None, None) } } \ No newline at end of file diff --git a/client/src/test/scala/org/zalando/nakadi/client/utils/ModelConverterTest.scala b/client/src/test/scala/org/zalando/nakadi/client/utils/ModelConverterTest.scala index cb5fbf0..b767f96 100644 --- a/client/src/test/scala/org/zalando/nakadi/client/utils/ModelConverterTest.scala +++ b/client/src/test/scala/org/zalando/nakadi/client/utils/ModelConverterTest.scala @@ -1,8 +1,7 @@ package org.zalando.nakadi.client.scala.utils -import scala.concurrent.Future -import scala.concurrent.Await import scala.concurrent.duration.DurationInt + import org.mockito.Matchers.any import org.mockito.Matchers.anyString import org.mockito.Mockito.reset @@ -14,15 +13,8 @@ import org.scalatest.FlatSpec import org.scalatest.Matchers import org.scalatest.WordSpec import org.scalatest.mock.MockitoSugar -import akka.http.scaladsl.model.HttpResponse + import akka.stream.Materializer -import spray.json.JsonFormat -import akka.http.scaladsl.model.StatusCodes -import akka.http.scaladsl.model.HttpProtocol -import akka.http.scaladsl.model.HttpProtocols -import akka.http.scaladsl.model.HttpEntity -import akka.http.scaladsl.model.ContentTypes -import org.zalando.nakadi.client.scala.model.JacksonJsonMarshaller class ModelConverterTest extends WordSpec with Matchers with MockitoSugar { "Conversions" should { diff --git a/it/src/test/scala/org/zalando/nakadi/client/KlientIntegrationTest.scala b/it/src/test/scala/org/zalando/nakadi/client/KlientIntegrationTest.scala index 05d37ba..d5329b7 100644 --- a/it/src/test/scala/org/zalando/nakadi/client/KlientIntegrationTest.scala +++ b/it/src/test/scala/org/zalando/nakadi/client/KlientIntegrationTest.scala @@ -54,7 +54,6 @@ class KlientIntegrationTest extends WordSpec with Matchers with ModelFactory { executeCall(client.deleteEventType(eventType.name)) shouldBe None } "Create the event itself" in { - import spray.json._ //Matches the one defined in the schema of case class EventExample(orderNumber: String, metadata: Option[EventMetadata]) extends Event diff --git a/project/Build.scala b/project/Build.scala index 82f726f..d1425e1 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -65,7 +65,7 @@ lazy val client = withDefaults( project.settings( name := projectName, organization := "org.zalando.nakadi.client", - version := "2.0.0-pre-alpha.2", + version := "2.0.0-pre-alpha.3", crossPaths := false, scalaVersion := "2.11.7", publishTo := whereToPublishTo(isSnapshot.value), diff --git a/project/Dependencies.scala b/project/Dependencies.scala index 74b1770..97d48c0 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -15,8 +15,8 @@ object Dependencies { "com.typesafe" % "config" % "1.3.0", "com.google.guava" % "guava" % "19.0", "com.typesafe.scala-logging" %% "scala-logging" % "3.1.0", - "com.fasterxml.jackson.core" % "jackson-core" % "2.7.0", - "com.fasterxml.jackson.module" %% "jackson-module-scala" % "2.6.3", + "com.fasterxml.jackson.core" % "jackson-core" % "2.7.3", + "com.fasterxml.jackson.module" %% "jackson-module-scala" % "2.7.3", "ch.qos.logback" % "logback-classic" % "1.1.3", "com.typesafe.akka" %% "akka-actor" % akkaVersion, "com.typesafe.akka" %% "akka-http-experimental" % akkaVersion, From 788cd7b772808b55de4702ce93e303440048b734 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Fri, 27 May 2016 13:10:40 +0200 Subject: [PATCH 060/183] techjira:LAAS-60 Fixes #48, #50. Upgrading jackson-scala-module to 2.7.3 --- project/Build.scala | 2 +- project/Dependencies.scala | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/project/Build.scala b/project/Build.scala index d1425e1..539d578 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -65,7 +65,7 @@ lazy val client = withDefaults( project.settings( name := projectName, organization := "org.zalando.nakadi.client", - version := "2.0.0-pre-alpha.3", + version := "2.0.0-pre-alpha.4", crossPaths := false, scalaVersion := "2.11.7", publishTo := whereToPublishTo(isSnapshot.value), diff --git a/project/Dependencies.scala b/project/Dependencies.scala index 97d48c0..45ffc81 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -3,11 +3,11 @@ import sbt._ object Dependencies { val akkaVersion = "2.4.4" - + val jacksonVersion = "2.7.3" val apiDeps = { Seq( - "com.fasterxml.jackson.module" %% "jackson-module-scala" % "2.6.3", - "com.fasterxml.jackson.core" % "jackson-core" % "2.7.0") + "com.fasterxml.jackson.module" %% "jackson-module-scala" % jacksonVersion, + "com.fasterxml.jackson.core" % "jackson-annotations" % jacksonVersion) } val clientDeps = { @@ -15,8 +15,8 @@ object Dependencies { "com.typesafe" % "config" % "1.3.0", "com.google.guava" % "guava" % "19.0", "com.typesafe.scala-logging" %% "scala-logging" % "3.1.0", - "com.fasterxml.jackson.core" % "jackson-core" % "2.7.3", - "com.fasterxml.jackson.module" %% "jackson-module-scala" % "2.7.3", + "com.fasterxml.jackson.core" % "jackson-core" % jacksonVersion, + "com.fasterxml.jackson.module" %% "jackson-module-scala" % jacksonVersion, "ch.qos.logback" % "logback-classic" % "1.1.3", "com.typesafe.akka" %% "akka-actor" % akkaVersion, "com.typesafe.akka" %% "akka-http-experimental" % akkaVersion, From 2409075f2971b395b9a099d006dc460b320d9eaa Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Mon, 30 May 2016 10:03:44 +0200 Subject: [PATCH 061/183] techjira:LAAS-60 LOG Posted events under INFO level, fixes #49 --- .../nakadi/client/scala/Connection.scala | 3 +- .../nakadi/client/scala/TestFactory.scala | 10 +- .../nakadi/client/KlientIntegrationTest.scala | 160 +++++++++--------- .../nakadi/client/example/ClientExample.scala | 36 ---- .../client/example/EventPublisher.scala | 43 ----- .../client/example/EventSubscriber.scala | 29 ---- .../client/example/EventTransformer.scala | 50 ------ project/Build.scala | 8 - project/Configs.scala | 3 +- 9 files changed, 88 insertions(+), 254 deletions(-) delete mode 100644 it/src/test/scala/org/zalando/nakadi/client/example/ClientExample.scala delete mode 100644 it/src/test/scala/org/zalando/nakadi/client/example/EventPublisher.scala delete mode 100644 it/src/test/scala/org/zalando/nakadi/client/example/EventSubscriber.scala delete mode 100644 it/src/test/scala/org/zalando/nakadi/client/example/EventTransformer.scala diff --git a/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala index 65ae9c7..61919e2 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala @@ -131,8 +131,7 @@ sealed class ConnectionImpl(val host: String, val port: Int, val tokenProvider: def post[T](endpoint: String, model: T)(implicit serializer: Serializer[T]): Future[HttpResponse] = { val entity = serializer.to(model) - logger.info("Posting to endpoint {}", endpoint) - logger.debug("Data to post {}", entity) + logger.info("POST URL:{} - DATA:{}", endpoint, entity) executeCall(withHttpRequestAndPayload(endpoint, entity, HttpMethods.POST, tokenProvider)) } diff --git a/client/src/test/scala/org/zalando/nakadi/client/scala/TestFactory.scala b/client/src/test/scala/org/zalando/nakadi/client/scala/TestFactory.scala index 814ead8..714f27e 100644 --- a/client/src/test/scala/org/zalando/nakadi/client/scala/TestFactory.scala +++ b/client/src/test/scala/org/zalando/nakadi/client/scala/TestFactory.scala @@ -15,23 +15,23 @@ import java.util.Calendar case class EventActions(client: Client) { - def create[T <: Event](name: String, event: Seq[T])(implicit ser: Serializer[Seq[T]]) = { + def create[T <: Event](name: String, event: Seq[T]) = { client.publishEvents[T](name, event) } } case class EventTypesActions(client: Client) { import JacksonJsonMarshaller._ - def create(event: EventType)(implicit ser: Serializer[EventType]) = { + def create(event: EventType) = { executeCall(client.createEventType(event)) } - def update(event: EventType)(implicit ser: Serializer[EventType]) = { + def update(event: EventType) = { executeCall(client.updateEventType(event.name, event)) } - def get(name: String)(implicit ser: Deserializer[Option[EventType]]) = { + def get(name: String) = { executeCall(client.getEventType(name)) } - def getAll()(implicit ser: Deserializer[Option[Seq[EventType]]]) = { + def getAll() = { executeCall(client.getEventTypes()) } def delete(name: String) = { diff --git a/it/src/test/scala/org/zalando/nakadi/client/KlientIntegrationTest.scala b/it/src/test/scala/org/zalando/nakadi/client/KlientIntegrationTest.scala index d5329b7..be265c2 100644 --- a/it/src/test/scala/org/zalando/nakadi/client/KlientIntegrationTest.scala +++ b/it/src/test/scala/org/zalando/nakadi/client/KlientIntegrationTest.scala @@ -1,82 +1,84 @@ -package org.zalando.nakadi.client.config - -import scala.concurrent.Await -import scala.concurrent.Future -import scala.concurrent.duration.DurationInt - -import org.scalatest.Matchers -import org.scalatest.WordSpec -import org.zalando.nakadi.client._ -import org.zalando.nakadi.client.scala.model._ -import org.zalando.nakadi.client.scala.ClientFactory -import org.zalando.nakadi.client.scala.ModelFactory - -import com.fasterxml.jackson.core.`type`.TypeReference -import org.zalando.nakadi.client.scala.ClientError - -class KlientIntegrationTest extends WordSpec with Matchers with ModelFactory { - import ClientFactory._ - import JacksonJsonMarshaller._ - "Nakadi Client" should { - "parse multiple PartitionResolutionStrategy" in { - val Right(result) = executeCall(client.getPartitioningStrategies()) - result.size should be > 0 - } - - //TODO: Change it when this endpoint is implemented by nakadi - "parse exsiting validationStrategies" in { - val result = executeCall(client.getValidationStrategies()) - result shouldBe Right(None) - } - //TODO: Change it when this endpoint is implemented by nakadi - "parse exsiting enrishment-strategies" in { - val result = executeCall(client.getEnrichmentStrategies()) - result shouldBe Right(None) - } - //TODO: Change when all events are valid - "parse existing eventTypes" in { - val Right(result) = executeCall(client.getEventTypes()) - result.size should be > 0 - } - "create a new eventType" in { - val eventType = createUniqueEventType() - executeCall(client.createEventType(eventType)) shouldBe None - } - "get EventType" in { - val eventType = createUniqueEventType() - executeCall(client.createEventType(eventType)) shouldBe None - executeCall(client.getEventType(eventType.name)) shouldBe Right(Some(eventType)) - - } - "delete EventType" in { - val eventType = createUniqueEventType() - executeCall(client.createEventType(eventType)) shouldBe None - executeCall(client.deleteEventType(eventType.name)) shouldBe None - } - "Create the event itself" in { - //Matches the one defined in the schema of - - case class EventExample(orderNumber: String, metadata: Option[EventMetadata]) extends Event - implicit val eventExample: TypeReference[Seq[EventExample]] = new TypeReference[Seq[EventExample]] {} - - val event = new EventExample("22301982837", Some(createEventMetadata())) - val eventType = createUniqueEventType() - executeCall(client.createEventType(eventType)) shouldBe None - executeCall(client.publishEvents[EventExample](eventType.name, List(event))) shouldBe None - } - } - private def assertIsNotImplementedYet[T](input: Either[ClientError, Option[List[T]]]) = { - input match { - case Left(error) => error.status shouldBe Some(404) - case Right(result) => - println(" #### " + result) - fail - } - } - private def executeCall[T](call: => Future[T]): T = { - Await.result(call, 10.second) - } -} +package org.zalando.nakadi.client +//import scala.concurrent.Await +//import scala.concurrent.Future +//import scala.concurrent.duration.DurationInt +//import org.scalatest.Matchers +//import org.scalatest.WordSpec +//import org.zalando.nakadi.client._ +//import org.zalando.nakadi.client.scala.model._ +//import org.zalando.nakadi.client.scala.ClientFactory +//import org.zalando.nakadi.client.scala.ModelFactory +//import com.fasterxml.jackson.core.`type`.TypeReference +//import org.zalando.nakadi.client.scala.ClientError +//import scala.Right +//import scala.concurrent.Await +//import scala.concurrent.Future +//import scala.concurrent.Await +// +//class KlientIntegrationTest extends WordSpec with Matchers with ModelFactory { +// import ClientFactory._ +// import JacksonJsonMarshaller._ +// "Nakadi Client" should { +// "parse multiple PartitionResolutionStrategy" in { +// val Right(result) = executeCall(client.getPartitioningStrategies()) +// result.size should be > 0 +// } +// +// //TODO: Change it when this endpoint is implemented by nakadi +// "parse exsiting validationStrategies" in { +// val result = executeCall(client.getValidationStrategies()) +// result shouldBe Right(None) +// } +// //TODO: Change it when this endpoint is implemented by nakadi +// "parse exsiting enrishment-strategies" in { +// val result = executeCall(client.getEnrichmentStrategies()) +// result shouldBe Right(None) +// } +// //TODO: Change when all events are valid +// "parse existing eventTypes" in { +// val Right(result) = executeCall(client.getEventTypes()) +// result.size should be > 0 +// } +// "create a new eventType" in { +// val eventType = createUniqueEventType() +// executeCall(client.createEventType(eventType)) shouldBe None +// } +// "get EventType" in { +// val eventType = createUniqueEventType() +// executeCall(client.createEventType(eventType)) shouldBe None +// executeCall(client.getEventType(eventType.name)) shouldBe Right(Some(eventType)) +// +// } +// "delete EventType" in { +// val eventType = createUniqueEventType() +// executeCall(client.createEventType(eventType)) shouldBe None +// executeCall(client.deleteEventType(eventType.name)) shouldBe None +// } +// "Create the event itself" in { +// //Matches the one defined in the schema of +// +// case class EventExample(orderNumber: String, metadata: Option[EventMetadata]) extends Event +// implicit val eventExample: TypeReference[Seq[EventExample]] = new TypeReference[Seq[EventExample]] {} +// +// val event = new EventExample("22301982837", Some(createEventMetadata())) +// val eventType = createUniqueEventType() +// executeCall(client.createEventType(eventType)) shouldBe None +// executeCall(client.publishEvents[EventExample](eventType.name, List(event))) shouldBe None +// } +// } +// private def assertIsNotImplementedYet[T](input: Either[ClientError, Option[List[T]]]) = { +// input match { +// case Left(error) => error.status shouldBe Some(404) +// case Right(result) => +// println(" #### " + result) +// fail +// } +// } +// private def executeCall[T](call: => Future[T]): T = { +// Await.result(call, 10.second) +// } +//} +// diff --git a/it/src/test/scala/org/zalando/nakadi/client/example/ClientExample.scala b/it/src/test/scala/org/zalando/nakadi/client/example/ClientExample.scala deleted file mode 100644 index 8318849..0000000 --- a/it/src/test/scala/org/zalando/nakadi/client/example/ClientExample.scala +++ /dev/null @@ -1,36 +0,0 @@ -package org.zalando.nakadi.client.example - -import java.math.BigInteger -import akka.actor._ -import akka.stream.actor._ -import org.zalando.nakadi.client.scala.ClientImpl -import org.zalando.nakadi.client.scala.Connection -import org.zalando.nakadi.client.scala.ClientFactory -import org.zalando.nakadi.client.scala.ClientFactory - -object Main extends App { - - val system = ActorSystem("example-stream-system") - ClientExample.startSimplePubSubExample(system) - val client = ClientExample.nakadiClient() - -} - -object ClientExample { - import ClientFactory._ - def nakadiClient() = client - - def startSimplePubSubExample(system: ActorSystem) { - system.log.info("Starting Publisher") - val publisherActor = system.actorOf(Props[EventPublisher]) - val publisher = ActorPublisher[BigInteger](publisherActor) - - system.log.info("Starting Subscriber") - val subscriberActor = system.actorOf(Props(new EventSubscriber(500))) - val subscriber = ActorSubscriber[BigInteger](subscriberActor) - - system.log.info("Subscribing to Publisher") - publisher.subscribe(subscriber) - } - -} \ No newline at end of file diff --git a/it/src/test/scala/org/zalando/nakadi/client/example/EventPublisher.scala b/it/src/test/scala/org/zalando/nakadi/client/example/EventPublisher.scala deleted file mode 100644 index 9c3eda8..0000000 --- a/it/src/test/scala/org/zalando/nakadi/client/example/EventPublisher.scala +++ /dev/null @@ -1,43 +0,0 @@ -package org.zalando.nakadi.client.example - -import java.math.BigInteger -import akka.actor.ActorLogging -import akka.stream.actor.ActorPublisher -import akka.stream.actor.ActorPublisherMessage.{ Cancel, Request } - - -class EventPublisher extends ActorPublisher[BigInteger] with ActorLogging { - - var prev = BigInteger.ZERO - var curr = BigInteger.ZERO - - def receive = { - case Request(cnt) => // 2 - log.debug("[EventPublisher] Received Request ({}) from Subscriber", cnt) - sendFibs() - case Cancel => // 3 - log.info("[EventPublisher] Cancel Message Received -- Stopping") - context.stop(self) - case _ => - log.info("[EventPublisher] Unknown Message") - } - - def sendFibs() { - while (isActive && totalDemand > 0) { // 4 - onNext(nextFib()) - } - } - - def nextFib(): BigInteger = { - if (curr == BigInteger.ZERO) { - curr = BigInteger.ONE - } else { - val tmp = prev.add(curr) - prev = curr - curr = tmp - } - curr - } - - -} \ No newline at end of file diff --git a/it/src/test/scala/org/zalando/nakadi/client/example/EventSubscriber.scala b/it/src/test/scala/org/zalando/nakadi/client/example/EventSubscriber.scala deleted file mode 100644 index cad64ee..0000000 --- a/it/src/test/scala/org/zalando/nakadi/client/example/EventSubscriber.scala +++ /dev/null @@ -1,29 +0,0 @@ -package org.zalando.nakadi.client.example - -import akka.stream.actor.WatermarkRequestStrategy -import java.math.BigInteger -import akka.actor.ActorLogging -import akka.stream.actor.ActorSubscriber -import akka.stream.actor.ActorSubscriberMessage.OnError -import akka.stream.actor.ActorSubscriberMessage.OnNext -import akka.stream.actor.ActorSubscriberMessage.OnComplete - -class EventSubscriber(delay: Long) extends ActorSubscriber with ActorLogging { // 1 - val requestStrategy = WatermarkRequestStrategy(50) // 2 - - def receive = { - case OnNext(fib: BigInteger) => // 3 - log.debug("[EventSubscriber] Received Fibonacci Number: {}", fib) - Thread.sleep(delay) - case OnError(err: Exception) => // 4 - log.error(err, "[EventSubscriber] Receieved Exception in Fibonacci Stream") - context.stop(self) - case OnComplete => // 5 - log.info("[EventSubscriber] Fibonacci Stream Completed!") - context.stop(self) - case _ => - log.info("[EventSubscriber] Unknown!") - } - - -} diff --git a/it/src/test/scala/org/zalando/nakadi/client/example/EventTransformer.scala b/it/src/test/scala/org/zalando/nakadi/client/example/EventTransformer.scala deleted file mode 100644 index b210b0e..0000000 --- a/it/src/test/scala/org/zalando/nakadi/client/example/EventTransformer.scala +++ /dev/null @@ -1,50 +0,0 @@ -package org.zalando.nakadi.client.example - -import java.math.BigInteger -import akka.stream.actor.ActorSubscriber -import akka.stream.actor.MaxInFlightRequestStrategy -import akka.stream.actor.ActorPublisherMessage.Request -import akka.stream.actor.ActorSubscriberMessage.OnError -import akka.stream.actor.ActorSubscriberMessage.OnComplete -import akka.stream.actor.ActorSubscriberMessage.OnNext -import akka.stream.actor.ActorPublisher -import scala.collection.mutable.{ Queue => MQueue } -import akka.stream.actor.ActorPublisherMessage.Cancel -import scala.collection.mutable.{Queue => MQueue} - -class EventTransformer extends ActorSubscriber with ActorPublisher[BigInteger] { // 1 - val dos = BigInteger.valueOf(2L) - val doubledQueue = MQueue[BigInteger]() // 2 - - def receive = { - case OnNext(biggie: BigInteger) => // 3 - doubledQueue.enqueue(biggie.multiply(dos)) - sendDoubled() - case OnError(err: Exception) => // 4 - onError(err) - context.stop(self) - case OnComplete => // 5 - onComplete() - context.stop(self) - case Request(cnt) => // 6 - sendDoubled() - case Cancel => // 7 - cancel() - context.stop(self) - case _ => - } - - def sendDoubled() { - while (isActive && totalDemand > 0 && !doubledQueue.isEmpty) { // 8 - onNext(doubledQueue.dequeue()) - } - } - - - val requestStrategy = new MaxInFlightRequestStrategy(50) { // 9 - override def inFlightInternally = doubledQueue.size - } -} - - - diff --git a/project/Build.scala b/project/Build.scala index 539d578..fb10e79 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -48,19 +48,11 @@ lazy val client = withDefaults( project.in(file("client")).dependsOn(api) ).settings(libraryDependencies ++= clientDeps) - lazy val it = withDefaults( "nakadi-klients-integration-test", project.in(file("it")).dependsOn(api, client) ).settings(libraryDependencies ++= clientDeps) - - lazy val e2e = withDefaults( - "nakadi-klients-end-2-end-test", - project.in(file("e2e")).dependsOn(api, client, it) - ).settings(libraryDependencies ++= clientDeps) - - def withDefaults(projectName:String, project:sbt.Project)={ project.settings( name := projectName, diff --git a/project/Configs.scala b/project/Configs.scala index 62284f9..daae82c 100644 --- a/project/Configs.scala +++ b/project/Configs.scala @@ -2,6 +2,5 @@ import sbt._ object Configs { val IntegrationTest = config("it") extend(Runtime) - val EndToEndTest = config("e2e") extend(Runtime) - val all = Seq(IntegrationTest, EndToEndTest) + val all = Seq(IntegrationTest) } From ba650437743a100455eaf3153c452fe3bb557625 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Tue, 31 May 2016 14:41:11 +0200 Subject: [PATCH 062/183] techjira:LAAS-60 Fixes #40 - Auto-reconnect --- .../nakadi/client/actor/ConsumingActor.scala | 41 +++-- .../client/actor/SupervisingActor.scala | 167 ++++++++---------- .../client/handler/SubscriptionHandler.scala | 6 +- .../examples/java/EventCreationExample.java | 2 +- .../examples/scala/EventCreationExample.scala | 5 +- .../examples/scala/EventListenerExample.scala | 19 +- 6 files changed, 115 insertions(+), 125 deletions(-) diff --git a/client/src/main/scala/org/zalando/nakadi/client/actor/ConsumingActor.scala b/client/src/main/scala/org/zalando/nakadi/client/actor/ConsumingActor.scala index e3959e7..70add17 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/actor/ConsumingActor.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/actor/ConsumingActor.scala @@ -25,6 +25,8 @@ import org.zalando.nakadi.client.scala.ScalaResult import org.zalando.nakadi.client.scala.JavaResult import org.zalando.nakadi.client.scala.ErrorResult import org.zalando.nakadi.client.java.model.{ Event => JEvent } +import SupervisingActor._ +import akka.actor.Terminated /** * This actor serves as Sink for the pipeline.
      @@ -36,16 +38,15 @@ import org.zalando.nakadi.client.java.model.{ Event => JEvent } */ object ConsumingActor { - case class Shutdown(handler: EventHandler) } -class ConsumingActor(eventType: String, - handler: EventHandler) +class ConsumingActor(subscription: SubscriptionKey, + handler: EventHandler) extends Actor with ActorLogging with ActorSubscriber { import ModelConverter._ import ConsumingActor._ - var initialCursor: Option[Cursor] = null + var lastCursor: Option[Cursor] = null override protected def requestStrategy: RequestStrategy = new RequestStrategy { override def requestDemand(remainingRequested: Int): Int = { @@ -55,31 +56,39 @@ class ConsumingActor(eventType: String, override def receive: Receive = { case OnNext(msg: ByteString) => -// import util.Random -// if (Random.nextFloat() > 0.9 && Random.nextBoolean() && Random.nextBoolean()) -// throw new IllegalStateException("OMG, not again!") val message = msg.utf8String - log.debug("Event - cursor {} - eventType {} - msg {}", initialCursor, eventType, message) - handler.handleOnReceive(eventType, message) + log.debug("Event - cursor {} - {} - msg {}", lastCursor, subscription, message) + import util.Random + if (Random.nextFloat() > 0.2 && Random.nextBoolean() && Random.nextBoolean()) + throw new IllegalStateException("FAIIIIIL!") + handler.handleOnReceive(subscription.toString(), message) match { + case Right(cursor) => + lastCursor = Some(cursor) + context.parent ! OffsetMsg(cursor, subscription) + case Left(error) => log.error(error.error.getMessage) + } case OnError(err: Throwable) => - log.error("onError - cursor {} - eventType {} - error {}", initialCursor, eventType, err.getMessage) + log.error("onError - cursor {} - {} - error {}", lastCursor, subscription, err.getMessage) context.stop(self) case OnComplete => - log.info("onComplete - cursor {} - eventType {}", initialCursor, eventType) - context.stop(self) - case Shutdown(shutdownHandler: EventHandler) => - log.info("Shutting down eventType listener-id {} -> {}", handler.id(), shutdownHandler.id()) + log.info("onComplete - cursor {} - {}", lastCursor, subscription) context.stop(self) + case Terminated => + log.info("Received Terminated msg - subscription {} with listener-id {} ", subscription, handler.id()) case a => - log.error("Could not handle message: {}",a) + log.error("Could not handle message: {}", a) } override def postRestart(reason: Throwable) { super.postRestart(reason) log.info(s">>>>>>>>>>>>> <<<<<<<<<<<<<<<") log.info(s">>>>>>>>>>>>> Restarted because of ${reason.getMessage}") - log.info(">>>>>>>>>>>>> Current cursor {} <<<<<<<<<<<<<<<", initialCursor) + log.info(">>>>>>>>>>>>> Current cursor {} <<<<<<<<<<<<<<<", lastCursor) + + } + override def postStop() { + log.info("Shutting down subscription {} with listener-id {} ", subscription, handler.id()) } } diff --git a/client/src/main/scala/org/zalando/nakadi/client/actor/SupervisingActor.scala b/client/src/main/scala/org/zalando/nakadi/client/actor/SupervisingActor.scala index fa6496f..8191650 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/actor/SupervisingActor.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/actor/SupervisingActor.scala @@ -1,139 +1,120 @@ package org.zalando.nakadi.client.actor -import akka.actor.ActorLogging -import akka.actor.Actor -import scala.concurrent.duration._ -import akka.actor.OneForOneStrategy -import akka.actor.SupervisorStrategy -import akka.actor.ActorKilledException -import akka.actor.ActorInitializationException -import akka.actor.SupervisorStrategy._ -import akka.actor.AllForOneStrategy -import akka.actor.ActorRef -import org.zalando.nakadi.client.scala.EventHandler import org.zalando.nakadi.client.handler.SubscriptionHandler import org.zalando.nakadi.client.scala.Connection -import org.zalando.nakadi.client.handler.SubscriptionHandlerImpl -import akka.actor.ActorLogging -import akka.actor.Actor -import scala.concurrent.duration._ -import akka.actor.OneForOneStrategy -import akka.actor.SupervisorStrategy -import akka.actor.ActorKilledException -import akka.actor.ActorInitializationException -import akka.actor.SupervisorStrategy._ -import akka.actor.AllForOneStrategy -import akka.actor.ActorRef import org.zalando.nakadi.client.scala.EventHandler -import java.security.SecureRandom -import java.security.cert.X509Certificate -import java.util.concurrent.TimeoutException - -import scala.concurrent.ExecutionContext.Implicits.global -import scala.concurrent.Future -import scala.util.Try - -import org.reactivestreams.Publisher -import org.reactivestreams.Subscriber -import org.slf4j.LoggerFactory -import org.zalando.nakadi.client.Deserializer -import org.zalando.nakadi.client.Serializer -import org.zalando.nakadi.client.java.{ JavaClientHandler => JClientHandler } -import org.zalando.nakadi.client.java.{ JavaClientHandlerImpl => JClientHandlerImpl } -import org.zalando.nakadi.client.java.model.{ Cursor => JCursor } -import org.zalando.nakadi.client.java.model.{ Event => JEvent } import org.zalando.nakadi.client.scala.model.Cursor -import org.zalando.nakadi.client.scala.model.Event - -import com.typesafe.scalalogging.Logger -import akka.NotUsed +import SupervisingActor.SubscriptionEntry +import SupervisingActor.SubscriptionKey import akka.actor.Actor +import akka.actor.ActorInitializationException +import akka.actor.ActorKilledException +import akka.actor.ActorLogging import akka.actor.ActorRef -import akka.actor.ActorSystem +import akka.actor.OneForOneStrategy +import akka.actor.PoisonPill import akka.actor.Props +import akka.actor.SupervisorStrategy +import akka.actor.SupervisorStrategy.Decider +import akka.actor.SupervisorStrategy.Restart +import akka.actor.SupervisorStrategy._ import akka.actor.Terminated import akka.actor.actorRef2Scala -import akka.http.scaladsl.Http -import akka.http.scaladsl.Http.HostConnectionPool -import akka.http.scaladsl.HttpsConnectionContext -import akka.http.scaladsl.model.HttpRequest -import akka.http.scaladsl.model.HttpResponse -import akka.stream.ActorMaterializer -import akka.stream.ActorMaterializerSettings -import akka.stream.OverflowStrategy -import akka.stream.actor.ActorPublisher import akka.stream.actor.ActorSubscriber -import akka.stream.scaladsl.Flow -import akka.stream.scaladsl.Framing -import akka.stream.scaladsl.Sink -import akka.stream.scaladsl.Source import akka.util.ByteString -import javax.net.ssl.SSLContext -import javax.net.ssl.TrustManager -import javax.net.ssl.X509TrustManager -import akka.http.scaladsl.model.EntityStreamException -import java.util.concurrent.atomic.AtomicLong -import org.zalando.nakadi.client.scala.Connection -import org.zalando.nakadi.client.scala.HttpFactory +import org.zalando.nakadi.client.actor.utils.SupervisorHelper object SupervisingActor { - sealed trait Subscription - case class Subscribe(eventyTypeName: String, endpoint: String, cursor: Option[Cursor], handler: EventHandler) extends Subscription - case class Unsubscribe(eventTypeName: String, partition:String, eventHandlerId: String) extends Subscription - + case class SubscribeMsg(eventTypeName: String, endpoint: String, cursor: Option[Cursor], handler: EventHandler) { + override def toString(): String = cursor match { + case Some(Cursor(partition, offset)) => "SubscriptionKey(eventTypeName:" + eventTypeName + " - partition:" + partition + ")" + case None => "SubscriptionKey(eventTypeName:" + eventTypeName + ")" + }; + } + case class UnsubscribeMsg(eventTypeName: String, partition: String, eventHandlerId: String) + case class OffsetMsg(cursor: Cursor, subKey: SubscriptionKey) + case class SubscriptionKey(eventTypeName: String, partition: String) { + override def toString(): String = s"SubscriptionKey(eventTypeName:$eventTypeName - Partition:$partition)"; + } + case class SubscriptionEntry(subuscription: SubscribeMsg, actor: ActorRef) } -class SupervisingActor(val connection: Connection, val subscriptionHandler: SubscriptionHandler) extends Actor with ActorLogging { +class SupervisingActor(val connection: Connection, val subscriptionHandler: SubscriptionHandler) extends Actor with ActorLogging with SupervisorHelper { import SupervisingActor._ import ConsumingActor._ - type SubscriptionKey = (String,String) //EventTypeName,PartitionId - type SubscriptionEntry = (EventHandler, ActorRef) - private var subscriptions: Map[SubscriptionKey, SubscriptionEntry] = Map() //SubscriptionKey , SubscriptionEntry + val subscriptions: SubscriptionHolder = new SubscriptionHolderImpl() + + override val supervisorStrategy: SupervisorStrategy = { + def defaultDecider: Decider = { + case _: ActorInitializationException ⇒ Stop + case _: ActorKilledException ⇒ Stop + case _: IllegalStateException ⇒ Stop + case _: Exception ⇒ Stop + case _: Throwable ⇒ Stop + } + OneForOneStrategy()(defaultDecider) + } def receive: Receive = { - case subscrition: Subscribe => + case OffsetMsg(cursor, subKey) => + log.info("Received cursor {} - subKey {}", cursor, subKey) + subscriptions.addCursor(subKey, cursor) + case subscrition: SubscribeMsg => log.info("New subscription {}", subscrition) subscribe(subscrition) - case unsubscription: Unsubscribe => + case unsubscription: UnsubscribeMsg => log.info("Number of subscriptions {}", subscriptions.size) unsubscribe(unsubscription) + case Terminated(terminatedActor) => + log.info(s"ConsumingActor terminated {}", terminatedActor.path.name) + subscriptions.entryByActor(terminatedActor) match { + case Some(SubscriptionEntry(SubscribeMsg(eventTypeName, endpoint, Some(Cursor(partition, offset)), handler), actor: ActorRef)) => + val unsubscription = UnsubscribeMsg(eventTypeName, partition, handler.id()) + unsubscribe(unsubscription) + val cursor = subscriptions.cursorByActor(terminatedActor) + val subscription = SubscribeMsg(eventTypeName, endpoint, cursor, handler) + subscribe(subscription) + case None => + log.warning("Did not find any SubscriptionKey for {}", terminatedActor.path.toString()) + case e => + log.warning("None exhaustive match! {}", e) + } } - def subscribe(subscribe: Subscribe) = { - val Subscribe(eventTypeName, endpoint, cursor, eventHandler) = subscribe + def subscribe(subscribe: SubscribeMsg) = { + val SubscribeMsg(eventTypeName, endpoint, cursor, eventHandler) = subscribe log.info("Subscription nr {} for eventType {} and listener {}", (subscriptions.size + 1), eventTypeName, eventHandler.id()) + val Some(Cursor(partition, _)) = cursor + val subKey: SubscriptionKey = SubscriptionKey(eventTypeName, partition) + //Create the Consumer - val consumingActor = context.actorOf(Props(classOf[ConsumingActor], endpoint, eventHandler), "EventConsumingActor-" + subscriptions.size) + val consumingActor = context.actorOf(Props(classOf[ConsumingActor], subKey, eventHandler), "EventConsumingActor-" + subscriptions.size) val consumer = ActorSubscriber[ByteString](consumingActor) + context.watch(consumingActor) //If the streaming is ending!! + val subEntry: SubscriptionEntry = SubscriptionEntry(subscribe, consumingActor) // Notify listener it is subscribed eventHandler.handleOnSubscribed(endpoint, cursor) //Create the pipeline subscriptionHandler.createPipeline(cursor, consumer, endpoint, eventHandler) - val subscription = (eventHandler, consumingActor) - val Some(Cursor(partition,_))=cursor - val key :SubscriptionKey = (eventTypeName,partition) - val entry: SubscriptionEntry = (eventHandler, consumingActor) - subscriptions = subscriptions + ((key, entry)) + subscriptions.addSubscription(subKey, consumingActor, subEntry) } - def unsubscribe(unsubscription: Unsubscribe) = { - val Unsubscribe(eventTypeName, partition, eventHandlerId) = unsubscription - val key :SubscriptionKey = (eventTypeName,partition) - log.info("Unsubscribe({}) for eventType {} and listener {}",subscriptions, eventTypeName, eventHandlerId) - subscriptions.get(key) match { - case Some(subscription) => - val (handler,actor)= subscription - log.info("Sending shutdown message to actor: {}", actor) - actor ! Shutdown(handler) - + def unsubscribe(unsubscription: UnsubscribeMsg) = { + val UnsubscribeMsg(eventTypeName, partition, eventHandlerId) = unsubscription + val key: SubscriptionKey = SubscriptionKey(eventTypeName, partition) + log.info("Unsubscribe({}) ", unsubscription) + subscriptions.entry(key) match { + case Some(SubscriptionEntry(handler, actor)) => + log.info("Unsubscribing Listener : {} from actor: {}", handler, actor) + actor ! PoisonPill + subscriptions.unsubscribe(key) case None => log.warning("Listener not found for {}", unsubscription) } - } } \ No newline at end of file diff --git a/client/src/main/scala/org/zalando/nakadi/client/handler/SubscriptionHandler.scala b/client/src/main/scala/org/zalando/nakadi/client/handler/SubscriptionHandler.scala index 43c1881..76284d0 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/handler/SubscriptionHandler.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/handler/SubscriptionHandler.scala @@ -7,7 +7,7 @@ import scala.concurrent.ExecutionContext.Implicits.global import org.reactivestreams.Subscriber import org.slf4j.LoggerFactory import org.zalando.nakadi.client.actor.SupervisingActor -import org.zalando.nakadi.client.actor.SupervisingActor.Subscribe +import org.zalando.nakadi.client.actor.SupervisingActor.SubscribeMsg import org.zalando.nakadi.client.scala.Connection import org.zalando.nakadi.client.scala.EventHandler import org.zalando.nakadi.client.scala.HttpFactory @@ -51,10 +51,10 @@ class SubscriptionHandlerImpl(val connection: Connection) extends SubscriptionHa val logger = Logger(LoggerFactory.getLogger(this.getClass)) def subscribe(eventTypeName: String, endpoint: String, cursor: Option[Cursor], eventHandler: EventHandler) = { - supervisingActor ! Subscribe(eventTypeName,endpoint, cursor, eventHandler) + supervisingActor ! SubscribeMsg(eventTypeName,endpoint, cursor, eventHandler) } def unsubscribe(eventTypeName: String,partition:String, listenerId: String)= { - supervisingActor ! Unsubscribe(eventTypeName,partition, listenerId) + supervisingActor ! UnsubscribeMsg(eventTypeName,partition, listenerId) } def createPipeline(cursor: Option[Cursor], subscriber: Subscriber[ByteString], url: String, eventHandler: EventHandler) = { diff --git a/it/src/main/java/org/zalando/nakadi/client/examples/java/EventCreationExample.java b/it/src/main/java/org/zalando/nakadi/client/examples/java/EventCreationExample.java index 0772053..b3fd133 100644 --- a/it/src/main/java/org/zalando/nakadi/client/examples/java/EventCreationExample.java +++ b/it/src/main/java/org/zalando/nakadi/client/examples/java/EventCreationExample.java @@ -83,7 +83,7 @@ public static void main(String[] args) throws InterruptedException, * their own partitions 4. A unique identifier for Subscribing * */ - String eventTypeName = "MeetingsEvent-example-E"; + String eventTypeName = "Example-1"; /** * Create client diff --git a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala index bef8517..192326f 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala @@ -44,7 +44,7 @@ object EventCreationExample extends App { //See the API for more information on the EventType model //https://github.com/zalando/nakadi/blob/nakadi-jvm/api/nakadi-event-bus-api.yaml#L1240 // val eventTypeName = "Example-unique-million-messages" - val eventTypeName = "Example-1" + val eventTypeName = "Example-2" // val eventTypeName = "Example-unique-hundred-messages-3" val owner = "team-laas" @@ -69,13 +69,14 @@ object EventCreationExample extends App { import JacksonJsonMarshaller._ // client.createEventType(eventType) + Thread.sleep(1000) // 4. Publish the EventType var counter = 0 for (n <- 1 to 1) { val event = new MeetingsEvent("2016-04-28T13:28:15+00:00", "Hackaton") var events = ListBuffer[MeetingsEvent]() - for (a <- 1 to 1) { + for (a <- 1 to 100) { counter += 1 events += MeetingsEvent("2016-04-28T13:28:15+00:00", "Hackaton" + counter) } diff --git a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala index 0371d4e..793cadd 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala @@ -33,8 +33,8 @@ class EventCounterListener(val id: String) extends Listener[MeetingsEvent] { } def onSubscribed(endpoint: String, cursor: Option[Cursor]): Unit = { log.info("########## onSubscribed ############") - log.info("Endpoint " + endpoint ) - log.info("Cursor " + cursor ) + log.info("Endpoint " + endpoint) + log.info("Cursor " + cursor) log.info("#####################################") } @@ -60,7 +60,7 @@ object EventListenerExample extends App { val parameters = new StreamParameters( cursor = Some(cursor) // - , batchLimit = Some(100) // Maximum number of `Event`s in each chunk (and therefore per partition) of the stream. + , batchLimit = Some(1) // Maximum number of `Event`s in each chunk (and therefore per partition) of the stream. // , streamLimit = Some(2) // Maximum number of `Event`s to stream (over all partitions being streamed in this //connection). // , batchFlushTimeout = Some(5) // Maximum time in seconds to wait for the flushing of each chunk (per partition). @@ -76,13 +76,12 @@ object EventListenerExample extends App { import org.zalando.nakadi.client.scala.model.JacksonJsonMarshaller._ // val eventTypeName = "Event-example-with-0-messages" - val eventTypeName = "Example-1" + val eventTypeName = "Example-2" val result = client.subscribe(eventTypeName, parameters, listener) - - Thread.sleep(5000) - - client.unsubscribe(eventTypeName,"0", listener) - - client.subscribe(eventTypeName, parameters, listener) + + Thread.sleep(3000) + // client.stop() + // client.unsubscribe(eventTypeName,"0", listener) + // client.subscribe(eventTypeName, parameters, listener) } \ No newline at end of file From 524222e7e84d8bd5efabd343dc767b8249b7fec9 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Tue, 31 May 2016 14:44:12 +0200 Subject: [PATCH 063/183] techjira:LAAS-60 Fixes #40 - Auto-reconnect --- .../nakadi/client/actor/ConsumingActor.scala | 2 +- .../client/actor/SubscriptionHolder.scala | 63 +++++++++++++++++++ .../client/actor/utils/SupervisorHelper.scala | 20 ++++++ .../client/utils/GeneralConversionsTest.scala | 37 +++++++++++ .../examples/scala/EventListenerExample.scala | 4 +- .../integration/java/GetEnventTypes.scala | 12 ++++ 6 files changed, 135 insertions(+), 3 deletions(-) create mode 100644 client/src/main/scala/org/zalando/nakadi/client/actor/SubscriptionHolder.scala create mode 100644 client/src/main/scala/org/zalando/nakadi/client/actor/utils/SupervisorHelper.scala create mode 100644 client/src/test/scala/org/zalando/nakadi/client/utils/GeneralConversionsTest.scala create mode 100644 it/src/main/scala/org/zalando/nakadi/client/integration/java/GetEnventTypes.scala diff --git a/client/src/main/scala/org/zalando/nakadi/client/actor/ConsumingActor.scala b/client/src/main/scala/org/zalando/nakadi/client/actor/ConsumingActor.scala index 70add17..6812908 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/actor/ConsumingActor.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/actor/ConsumingActor.scala @@ -59,7 +59,7 @@ class ConsumingActor(subscription: SubscriptionKey, val message = msg.utf8String log.debug("Event - cursor {} - {} - msg {}", lastCursor, subscription, message) import util.Random - if (Random.nextFloat() > 0.2 && Random.nextBoolean() && Random.nextBoolean()) + if (Random.nextFloat() > 0.9 && Random.nextBoolean() && Random.nextBoolean()) throw new IllegalStateException("FAIIIIIL!") handler.handleOnReceive(subscription.toString(), message) match { case Right(cursor) => diff --git a/client/src/main/scala/org/zalando/nakadi/client/actor/SubscriptionHolder.scala b/client/src/main/scala/org/zalando/nakadi/client/actor/SubscriptionHolder.scala new file mode 100644 index 0000000..6fc2e92 --- /dev/null +++ b/client/src/main/scala/org/zalando/nakadi/client/actor/SubscriptionHolder.scala @@ -0,0 +1,63 @@ +package org.zalando.nakadi.client.actor + +import org.zalando.nakadi.client.scala.model.Cursor + +import akka.actor.ActorRef +import org.slf4j.LoggerFactory +import com.typesafe.scalalogging.Logger + +trait SubscriptionHolder { + import SupervisingActor._ + def addCursor(key: SubscriptionKey, cursor: Cursor): Unit + def addSubscription(key: SubscriptionKey, key2: ActorRef, entry: SubscriptionEntry): Unit + def entry(key: SubscriptionKey): Option[SubscriptionEntry] + def entryByActor(actor: ActorRef): Option[SubscriptionEntry] + def cursorByActor(actor: ActorRef): Option[Cursor] + def unsubscribe(key: SubscriptionKey): Unit + def size: Int +} + +class SubscriptionHolderImpl extends SubscriptionHolder { + import SupervisingActor._ + private var subscriptions: Map[SubscriptionKey, SubscriptionEntry] = Map() //EventTypeName+Partition + private var cursors: Map[SubscriptionKey, Cursor] = Map() + private var actors: Map[String, SubscriptionKey] = Map() + private val logger = Logger(LoggerFactory.getLogger(this.getClass)) + + def addCursor(key: SubscriptionKey, cursor: Cursor): Unit = { + cursors = cursors + ((key, cursor)) + } + + def addSubscription(key: SubscriptionKey, key2: ActorRef, entry: SubscriptionEntry) = { + subscriptions = subscriptions + ((key, entry)) + actors = actors + ((key2.path.toString(), key)) + } + + def unsubscribe(key: SubscriptionKey): Unit ={ + + } + + def entry(key: SubscriptionKey): Option[SubscriptionEntry] = { + subscriptions.get(key) + } + + def entryByActor(actor: ActorRef): Option[SubscriptionEntry] = + actors.get(actor.path.toString()).flatMap(x => subscriptions.get(x)) + + def size: Int = { + subscriptions.size + } + + def addActor(actor: ActorRef, key: SubscriptionKey): Unit = { + actors = actors + ((actor.path.toString(), key)) + } + + def key(actor: ActorRef): Option[SubscriptionKey] = { + actors.get(actor.path.toString()) + } + + def cursorByActor(actor: ActorRef): Option[Cursor] = { + actors.get(actor.path.toString()).flatMap(x => cursors.get(x)) + } + +} \ No newline at end of file diff --git a/client/src/main/scala/org/zalando/nakadi/client/actor/utils/SupervisorHelper.scala b/client/src/main/scala/org/zalando/nakadi/client/actor/utils/SupervisorHelper.scala new file mode 100644 index 0000000..1465c9f --- /dev/null +++ b/client/src/main/scala/org/zalando/nakadi/client/actor/utils/SupervisorHelper.scala @@ -0,0 +1,20 @@ +package org.zalando.nakadi.client.actor.utils + +import akka.actor.SupervisorStrategy._ +import akka.actor.SupervisorStrategy +import akka.actor.ActorInitializationException +import akka.actor.ActorKilledException +import akka.actor.OneForOneStrategy + +trait SupervisorHelper { + + def escalate: SupervisorStrategy = { + def defaultDecider: Decider = { + case _: ActorInitializationException ⇒ Stop + case _: ActorKilledException ⇒ Stop + case _: Throwable ⇒ Stop + } + OneForOneStrategy()(defaultDecider) + } + +} \ No newline at end of file diff --git a/client/src/test/scala/org/zalando/nakadi/client/utils/GeneralConversionsTest.scala b/client/src/test/scala/org/zalando/nakadi/client/utils/GeneralConversionsTest.scala new file mode 100644 index 0000000..3d553c6 --- /dev/null +++ b/client/src/test/scala/org/zalando/nakadi/client/utils/GeneralConversionsTest.scala @@ -0,0 +1,37 @@ +package org.zalando.nakadi.client.utils + +import java.util.Optional + +import org.mockito.Mockito.reset +import org.scalatest.BeforeAndAfter +import org.scalatest.FlatSpec +import org.scalatest.Matchers +import org.scalatest.WordSpec +import org.scalatest.mock.MockitoSugar + +class GeneralConversionsTest extends FlatSpec with Matchers with MockitoSugar { + + case class Test(in: String, out: Integer) + val test = Test("Default", 99) + + "toOptional" should "return a Java Optional object" in { + val result: Optional[Test] = GeneralConversions.toOptional[Test](Some(test)) + assert(result!=null,"Result must not be null!!") + val res= result.get + assert(res.in==test.in) + assert(res.out==test.out) + } + "toOption" should "return a scala Option object" in { + val result:Option[Test] = GeneralConversions.toOption(Optional.of(test)) + assert(result.isDefined,"Result must not be null") + val Some(res)=result + assert(res.in==test.in) + assert(res.out==test.out) + + + } + "toOptionOfSeq" should "return a Sequence of Options" in { + + } + +} \ No newline at end of file diff --git a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala index 793cadd..aba0438 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala @@ -76,10 +76,10 @@ object EventListenerExample extends App { import org.zalando.nakadi.client.scala.model.JacksonJsonMarshaller._ // val eventTypeName = "Event-example-with-0-messages" - val eventTypeName = "Example-2" + val eventTypeName = "Example-1" val result = client.subscribe(eventTypeName, parameters, listener) - Thread.sleep(3000) +// Thread.sleep(3000) // client.stop() // client.unsubscribe(eventTypeName,"0", listener) // client.subscribe(eventTypeName, parameters, listener) diff --git a/it/src/main/scala/org/zalando/nakadi/client/integration/java/GetEnventTypes.scala b/it/src/main/scala/org/zalando/nakadi/client/integration/java/GetEnventTypes.scala new file mode 100644 index 0000000..b0a087d --- /dev/null +++ b/it/src/main/scala/org/zalando/nakadi/client/integration/java/GetEnventTypes.scala @@ -0,0 +1,12 @@ +package org.zalando.nakadi.client.integration.java + +import org.zalando.nakadi.client.scala.ClientFactory + +object GetEnventTypes extends App { + + val client = ClientFactory.getJavaClient(); + + val t = client.getEventTypes.get + println(">> " + t) + +} \ No newline at end of file From 6abc3ca63b9236751382ab35b0858ad3f0f866f3 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Tue, 31 May 2016 18:01:25 +0200 Subject: [PATCH 064/183] techjira:LAAS-60 Enum SCHEMA-TYPE has been changed. --- .../org/zalando/nakadi/client/java/enumerator/SchemaType.java | 2 +- project/Build.scala | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/api/src/main/java/org/zalando/nakadi/client/java/enumerator/SchemaType.java b/api/src/main/java/org/zalando/nakadi/client/java/enumerator/SchemaType.java index 9bfc46b..7cbee9c 100644 --- a/api/src/main/java/org/zalando/nakadi/client/java/enumerator/SchemaType.java +++ b/api/src/main/java/org/zalando/nakadi/client/java/enumerator/SchemaType.java @@ -5,7 +5,7 @@ import com.fasterxml.jackson.annotation.JsonValue; public enum SchemaType { - JSON("json_schema"); + JSON("JSON_SCHEMA"); private final String schema; diff --git a/project/Build.scala b/project/Build.scala index fb10e79..85e2746 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -57,7 +57,7 @@ lazy val client = withDefaults( project.settings( name := projectName, organization := "org.zalando.nakadi.client", - version := "2.0.0-pre-alpha.4", + version := "2.0.0-pre-alpha.5", crossPaths := false, scalaVersion := "2.11.7", publishTo := whereToPublishTo(isSnapshot.value), From 35d62b4cb6d55ba7627b690a4d2d6c007d9f5b28 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Tue, 31 May 2016 18:10:12 +0200 Subject: [PATCH 065/183] techjira:LAAS-60 Development release 2.0.0-pre-alpha.6 --- project/Build.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/Build.scala b/project/Build.scala index 85e2746..1dcd723 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -57,7 +57,7 @@ lazy val client = withDefaults( project.settings( name := projectName, organization := "org.zalando.nakadi.client", - version := "2.0.0-pre-alpha.5", + version := "2.0.0-pre-alpha.6", crossPaths := false, scalaVersion := "2.11.7", publishTo := whereToPublishTo(isSnapshot.value), From fd050c99e9189e7f5f13d0cf37216c5d483ed85f Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Wed, 1 Jun 2016 15:44:55 +0200 Subject: [PATCH 066/183] techjira:LAAS-60 Enum SCHEMA-TYPE has been changed. Version up: 2.0.0-pre-alpha.7 --- .../org/zalando/nakadi/client/scala/model/Model.scala | 10 +++++----- project/Build.scala | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/api/src/main/scala/org/zalando/nakadi/client/scala/model/Model.scala b/api/src/main/scala/org/zalando/nakadi/client/scala/model/Model.scala index 117460d..f37b161 100644 --- a/api/src/main/scala/org/zalando/nakadi/client/scala/model/Model.scala +++ b/api/src/main/scala/org/zalando/nakadi/client/scala/model/Model.scala @@ -5,11 +5,11 @@ import scala.collection.JavaConversions._ import com.fasterxml.jackson.module.scala.JsonScalaEnumeration import com.fasterxml.jackson.core.`type`.TypeReference -// Updated untill commit 7839be3 +// Updated untill commit 7839be3 // Compare -// Updated untill commit 7839be3 +// Updated untill commit 7839be3 // Compare /** @@ -26,7 +26,7 @@ import com.fasterxml.jackson.core.`type`.TypeReference * @param title */ trait Event { - + } /** * @param partition Id of the partition pointed to by this cursor. @@ -300,6 +300,6 @@ class BatchItemStepType extends TypeReference[BatchItemStep.type] case object SchemaType extends Enumeration { type SchemaType = Value - val JSON = Value("json_schema") + val JSON = Value("JSON_SCHEMA") } -class SchemaTypeType extends TypeReference[SchemaType.type] \ No newline at end of file +class SchemaTypeType extends TypeReference[SchemaType.type] diff --git a/project/Build.scala b/project/Build.scala index 1dcd723..814d0f4 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -57,7 +57,7 @@ lazy val client = withDefaults( project.settings( name := projectName, organization := "org.zalando.nakadi.client", - version := "2.0.0-pre-alpha.6", + version := "2.0.0-pre-alpha.7", crossPaths := false, scalaVersion := "2.11.7", publishTo := whereToPublishTo(isSnapshot.value), From 806e13047912072323598eb208358d4455cc4332 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Fri, 3 Jun 2016 11:40:35 +0200 Subject: [PATCH 067/183] techjira:LAAS-60 Fixes #53 #40 --- .../nakadi/client/actor/ConsumingActor.scala | 3 - .../client/actor/SubscriptionHolder.scala | 16 +++-- .../client/actor/SupervisingActor.scala | 32 +++++----- .../client/actor/utils/SupervisorHelper.scala | 20 ------ .../client/handler/SubscriptionHandler.scala | 62 +++++++++++++------ .../nakadi/client/scala/Connection.scala | 17 ++--- .../examples/java/EventCreationExample.java | 3 +- .../examples/scala/EventCreationExample.scala | 4 +- .../examples/scala/EventListenerExample.scala | 12 ++-- .../nakadi/client/scala/ClientFactory.scala | 24 ++++--- 10 files changed, 107 insertions(+), 86 deletions(-) delete mode 100644 client/src/main/scala/org/zalando/nakadi/client/actor/utils/SupervisorHelper.scala diff --git a/client/src/main/scala/org/zalando/nakadi/client/actor/ConsumingActor.scala b/client/src/main/scala/org/zalando/nakadi/client/actor/ConsumingActor.scala index 6812908..789688c 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/actor/ConsumingActor.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/actor/ConsumingActor.scala @@ -58,9 +58,6 @@ class ConsumingActor(subscription: SubscriptionKey, case OnNext(msg: ByteString) => val message = msg.utf8String log.debug("Event - cursor {} - {} - msg {}", lastCursor, subscription, message) - import util.Random - if (Random.nextFloat() > 0.9 && Random.nextBoolean() && Random.nextBoolean()) - throw new IllegalStateException("FAIIIIIL!") handler.handleOnReceive(subscription.toString(), message) match { case Right(cursor) => lastCursor = Some(cursor) diff --git a/client/src/main/scala/org/zalando/nakadi/client/actor/SubscriptionHolder.scala b/client/src/main/scala/org/zalando/nakadi/client/actor/SubscriptionHolder.scala index 6fc2e92..6350a77 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/actor/SubscriptionHolder.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/actor/SubscriptionHolder.scala @@ -14,7 +14,7 @@ trait SubscriptionHolder { def entryByActor(actor: ActorRef): Option[SubscriptionEntry] def cursorByActor(actor: ActorRef): Option[Cursor] def unsubscribe(key: SubscriptionKey): Unit - def size: Int + def activeSize: Int } class SubscriptionHolderImpl extends SubscriptionHolder { @@ -22,6 +22,7 @@ class SubscriptionHolderImpl extends SubscriptionHolder { private var subscriptions: Map[SubscriptionKey, SubscriptionEntry] = Map() //EventTypeName+Partition private var cursors: Map[SubscriptionKey, Cursor] = Map() private var actors: Map[String, SubscriptionKey] = Map() + private var subscriptionCounter = 0 private val logger = Logger(LoggerFactory.getLogger(this.getClass)) def addCursor(key: SubscriptionKey, cursor: Cursor): Unit = { @@ -31,10 +32,11 @@ class SubscriptionHolderImpl extends SubscriptionHolder { def addSubscription(key: SubscriptionKey, key2: ActorRef, entry: SubscriptionEntry) = { subscriptions = subscriptions + ((key, entry)) actors = actors + ((key2.path.toString(), key)) + subscriptionCounter+=1 } - - def unsubscribe(key: SubscriptionKey): Unit ={ - + + def unsubscribe(key: SubscriptionKey): Unit = { + } def entry(key: SubscriptionKey): Option[SubscriptionEntry] = { @@ -44,10 +46,14 @@ class SubscriptionHolderImpl extends SubscriptionHolder { def entryByActor(actor: ActorRef): Option[SubscriptionEntry] = actors.get(actor.path.toString()).flatMap(x => subscriptions.get(x)) - def size: Int = { + def activeSize: Int = { subscriptions.size } + def count(): Int = { + subscriptionCounter + } + def addActor(actor: ActorRef, key: SubscriptionKey): Unit = { actors = actors + ((actor.path.toString(), key)) } diff --git a/client/src/main/scala/org/zalando/nakadi/client/actor/SupervisingActor.scala b/client/src/main/scala/org/zalando/nakadi/client/actor/SupervisingActor.scala index 8191650..0c2e557 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/actor/SupervisingActor.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/actor/SupervisingActor.scala @@ -23,14 +23,11 @@ import akka.actor.Terminated import akka.actor.actorRef2Scala import akka.stream.actor.ActorSubscriber import akka.util.ByteString -import org.zalando.nakadi.client.actor.utils.SupervisorHelper object SupervisingActor { case class SubscribeMsg(eventTypeName: String, endpoint: String, cursor: Option[Cursor], handler: EventHandler) { - override def toString(): String = cursor match { - case Some(Cursor(partition, offset)) => "SubscriptionKey(eventTypeName:" + eventTypeName + " - partition:" + partition + ")" - case None => "SubscriptionKey(eventTypeName:" + eventTypeName + ")" - }; + override def toString(): String = + s"SubscriptionKey(eventTypeName: $eventTypeName - endpoint: $endpoint - cursor: $cursor - ${handler.id})" } case class UnsubscribeMsg(eventTypeName: String, partition: String, eventHandlerId: String) case class OffsetMsg(cursor: Cursor, subKey: SubscriptionKey) @@ -40,7 +37,7 @@ object SupervisingActor { case class SubscriptionEntry(subuscription: SubscribeMsg, actor: ActorRef) } -class SupervisingActor(val connection: Connection, val subscriptionHandler: SubscriptionHandler) extends Actor with ActorLogging with SupervisorHelper { +class SupervisingActor(val connection: Connection, val subscriptionHandler: SubscriptionHandler) extends Actor with ActorLogging { import SupervisingActor._ import ConsumingActor._ val subscriptions: SubscriptionHolder = new SubscriptionHolderImpl() @@ -64,7 +61,7 @@ class SupervisingActor(val connection: Connection, val subscriptionHandler: Subs log.info("New subscription {}", subscrition) subscribe(subscrition) case unsubscription: UnsubscribeMsg => - log.info("Number of subscriptions {}", subscriptions.size) + log.info("Number of subscriptions {}", subscriptions.activeSize) unsubscribe(unsubscription) case Terminated(terminatedActor) => log.info(s"ConsumingActor terminated {}", terminatedActor.path.name) @@ -83,24 +80,29 @@ class SupervisingActor(val connection: Connection, val subscriptionHandler: Subs } def subscribe(subscribe: SubscribeMsg) = { - val SubscribeMsg(eventTypeName, endpoint, cursor, eventHandler) = subscribe - log.info("Subscription nr {} for eventType {} and listener {}", (subscriptions.size + 1), eventTypeName, eventHandler.id()) + val SubscribeMsg(eventTypeName, endpoint, optCursor, eventHandler) = subscribe + log.info("Subscription nr {} - cursor {} - eventType {} - listener {}", (subscriptions.activeSize + 1), optCursor,eventTypeName, eventHandler.id()) - val Some(Cursor(partition, _)) = cursor + val Some(cursor) = optCursor + val Cursor(partition, _) = cursor val subKey: SubscriptionKey = SubscriptionKey(eventTypeName, partition) //Create the Consumer - val consumingActor = context.actorOf(Props(classOf[ConsumingActor], subKey, eventHandler), "EventConsumingActor-" + subscriptions.size) - val consumer = ActorSubscriber[ByteString](consumingActor) + val consumingActor = context.actorOf(Props(classOf[ConsumingActor], subKey, eventHandler), "EventConsumingActor-" + subscriptions.activeSize) + context.watch(consumingActor) //If the streaming is ending!! + val subEntry: SubscriptionEntry = SubscriptionEntry(subscribe, consumingActor) // Notify listener it is subscribed - eventHandler.handleOnSubscribed(endpoint, cursor) - + eventHandler.handleOnSubscribed(endpoint, Option(cursor)) + + + //Create the pipeline - subscriptionHandler.createPipeline(cursor, consumer, endpoint, eventHandler) + subscriptionHandler.createPipeline(Option(cursor), consumingActor, endpoint, eventHandler) subscriptions.addSubscription(subKey, consumingActor, subEntry) + subscriptions.addCursor(subKey, cursor) } def unsubscribe(unsubscription: UnsubscribeMsg) = { diff --git a/client/src/main/scala/org/zalando/nakadi/client/actor/utils/SupervisorHelper.scala b/client/src/main/scala/org/zalando/nakadi/client/actor/utils/SupervisorHelper.scala deleted file mode 100644 index 1465c9f..0000000 --- a/client/src/main/scala/org/zalando/nakadi/client/actor/utils/SupervisorHelper.scala +++ /dev/null @@ -1,20 +0,0 @@ -package org.zalando.nakadi.client.actor.utils - -import akka.actor.SupervisorStrategy._ -import akka.actor.SupervisorStrategy -import akka.actor.ActorInitializationException -import akka.actor.ActorKilledException -import akka.actor.OneForOneStrategy - -trait SupervisorHelper { - - def escalate: SupervisorStrategy = { - def defaultDecider: Decider = { - case _: ActorInitializationException ⇒ Stop - case _: ActorKilledException ⇒ Stop - case _: Throwable ⇒ Stop - } - OneForOneStrategy()(defaultDecider) - } - -} \ No newline at end of file diff --git a/client/src/main/scala/org/zalando/nakadi/client/handler/SubscriptionHandler.scala b/client/src/main/scala/org/zalando/nakadi/client/handler/SubscriptionHandler.scala index 76284d0..fdea2f6 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/handler/SubscriptionHandler.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/handler/SubscriptionHandler.scala @@ -4,10 +4,10 @@ import java.util.concurrent.TimeoutException import scala.concurrent.ExecutionContext.Implicits.global -import org.reactivestreams.Subscriber import org.slf4j.LoggerFactory import org.zalando.nakadi.client.actor.SupervisingActor import org.zalando.nakadi.client.actor.SupervisingActor.SubscribeMsg +import org.zalando.nakadi.client.actor.SupervisingActor.UnsubscribeMsg import org.zalando.nakadi.client.scala.Connection import org.zalando.nakadi.client.scala.EventHandler import org.zalando.nakadi.client.scala.HttpFactory @@ -16,34 +16,43 @@ import org.zalando.nakadi.client.scala.model.Cursor import com.typesafe.scalalogging.Logger import akka.NotUsed +import akka.actor.ActorRef +import akka.actor.PoisonPill import akka.actor.Props import akka.actor.actorRef2Scala import akka.http.scaladsl.model.EntityStreamException import akka.http.scaladsl.model.HttpRequest import akka.http.scaladsl.model.HttpResponse +import akka.stream.ActorAttributes import akka.stream.OverflowStrategy +import akka.stream.Supervision +import akka.stream.actor.ActorSubscriber import akka.stream.scaladsl.Flow import akka.stream.scaladsl.Framing import akka.stream.scaladsl.Sink import akka.stream.scaladsl.Source import akka.util.ByteString +import akka.stream.StreamTcpException +import akka.stream.StreamTcpException +import scala.util.Success +import scala.util._ trait SubscriptionHandler { /** * Handles the subscription for an eventHandler. */ def subscribe(eventTypeName: String, endpoint: String, cursor: Option[Cursor], eventHandler: EventHandler) - def unsubscribe(eventTypeName: String,partition:String, listenerId: String) - def createPipeline(cursor: Option[Cursor], subscriber: Subscriber[ByteString], url: String, eventHandler: EventHandler) + def unsubscribe(eventTypeName: String, partition: String, listenerId: String) + def createPipeline(cursor: Option[Cursor], consumingActor: ActorRef, url: String, eventHandler: EventHandler) } class SubscriptionHandlerImpl(val connection: Connection) extends SubscriptionHandler { import HttpFactory._ import SupervisingActor._ //Local variables - private implicit val materializer = connection.materializer() + private implicit val materializer = connection.materializer(decider()) - val supervisingActor = connection.actorSystem.actorOf(Props(classOf[SupervisingActor], connection, this), "EventReceivingActor-" + System.currentTimeMillis()) + private val supervisingActor = connection.actorSystem.actorOf(Props(classOf[SupervisingActor], connection, this), "EventReceivingActor-" + System.currentTimeMillis()) private val RECEIVE_BUFFER_SIZE = 40960 private val EVENT_DELIMITER = "\n" @@ -51,36 +60,49 @@ class SubscriptionHandlerImpl(val connection: Connection) extends SubscriptionHa val logger = Logger(LoggerFactory.getLogger(this.getClass)) def subscribe(eventTypeName: String, endpoint: String, cursor: Option[Cursor], eventHandler: EventHandler) = { - supervisingActor ! SubscribeMsg(eventTypeName,endpoint, cursor, eventHandler) + supervisingActor ! SubscribeMsg(eventTypeName, endpoint, cursor, eventHandler) } - def unsubscribe(eventTypeName: String,partition:String, listenerId: String)= { - supervisingActor ! UnsubscribeMsg(eventTypeName,partition, listenerId) + def unsubscribe(eventTypeName: String, partition: String, listenerId: String) = { + supervisingActor ! UnsubscribeMsg(eventTypeName, partition, listenerId) } - def createPipeline(cursor: Option[Cursor], subscriber: Subscriber[ByteString], url: String, eventHandler: EventHandler) = { + def decider(): Supervision.Decider = { + case _ => Supervision.Stop + } + + def createPipeline(cursor: Option[Cursor], consumingActor: ActorRef, url: String, eventHandler: EventHandler) = { + + val subscriber = ActorSubscriber[ByteString](consumingActor) + //Setup a flow for the request val requestFlow = Flow[Option[Cursor]].via(requestCreator(url)) .via(connection.requestFlow()) - .buffer(RECEIVE_BUFFER_SIZE, OverflowStrategy.backpressure) + .buffer(RECEIVE_BUFFER_SIZE, OverflowStrategy.fail) .via(requestRenderer) + .withAttributes(ActorAttributes.supervisionStrategy(decider())) //create the pipeline val result = Source(List(cursor)) - .via(requestFlow) + .via(requestFlow).withAttributes(ActorAttributes.supervisionStrategy(decider())) .runForeach(_.runWith(Sink.fromSubscriber(subscriber))) - result.onFailure { - case exception: TimeoutException => //When connection does not react within the timeout range - logger.error("Received an Exception timeout, restarting the client {}", exception.getMessage) - eventHandler.handleOnError(url, None, exception) - case exception: EntityStreamException => //When connection is broken without - logger.error("Received an Exception timeout, restarting the client {}", exception.getMessage) - eventHandler.handleOnError(url, None, exception) - case e => - logger.error("Exception not handled" + e) + result.onComplete { + case Success(requestSource) ⇒ + logger.info("@@@@@@@@@@ YAY @@@@@@@@@@@@@") + case Failure(e) ⇒ + val msg = "An exception occurred: " + e.getMessage + eventHandler.handleOnError(url, Some(msg), e) + logger.error(msg + e) + Thread.sleep(10000) + stopActor(consumingActor) } } + private def stopActor(actor: ActorRef) = { + logger.info("Stopping actor {} with a PoisonPill", actor.path.toString()) + actor ! PoisonPill + } + private def requestCreator(url: String): Flow[Option[Cursor], HttpRequest, NotUsed] = Flow[Option[Cursor]].map(withHttpRequest(url, _, None, connection.tokenProvider)) diff --git a/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala index 61919e2..1bd1ed9 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala @@ -34,6 +34,7 @@ import javax.net.ssl.TrustManager import javax.net.ssl.X509TrustManager import akka.http.scaladsl.model.{ HttpHeader, HttpMethod, HttpMethods, HttpResponse, MediaRange } import org.zalando.nakadi.client.handler.SubscriptionHandlerImpl +import akka.stream.Supervision sealed trait EmptyJavaEvent extends JEvent sealed trait EmptyScalaEvent extends Event @@ -48,6 +49,7 @@ trait Connection { def put[T](endpoint: String, model: T)(implicit serializer: Serializer[T]): Future[HttpResponse] def post[T](endpoint: String, model: T)(implicit serializer: Serializer[T]): Future[HttpResponse] def requestFlow(): Flow[HttpRequest, HttpResponse, Future[Http.OutgoingConnection]] + def materializer(decider: Supervision.Decider): ActorMaterializer def materializer(): ActorMaterializer def actorSystem(): ActorSystem } @@ -56,7 +58,7 @@ trait Connection { * Companion object with factory methods. */ object Connection { - val RECEIVE_BUFFER_SIZE = 40960 + val RECEIVE_BUFFER_SIZE = 10240 val EVENT_DELIMITER = "\n" /** * Creates a new SSL context for usage with connections based on the HTTPS protocol. @@ -98,11 +100,12 @@ sealed class ConnectionImpl(val host: String, val port: Int, val tokenProvider: type StepResult[T] = (T, Option[String]) implicit val actorSystem = ActorSystem("Nakadi-Client-Connections") - implicit val materializer = ActorMaterializer( - ActorMaterializerSettings(actorSystem) - .withInputBuffer( - initialSize = 8, - maxSize = 16)) + def materializer(decider: Supervision.Decider): ActorMaterializer = { + ActorMaterializer( ActorMaterializerSettings(actorSystem) + .withSupervisionStrategy(decider)) + } + def materializer() = ActorMaterializer( + ActorMaterializerSettings(actorSystem)) private implicit val http = Http(actorSystem) private val actors: Map[String, Actor] = Map() @@ -139,7 +142,7 @@ sealed class ConnectionImpl(val host: String, val port: Int, val tokenProvider: val response: Future[HttpResponse] = Source.single(request) .via(requestFlow). - runWith(Sink.head) + runWith(Sink.head)(materializer()) logError(response) response } diff --git a/it/src/main/java/org/zalando/nakadi/client/examples/java/EventCreationExample.java b/it/src/main/java/org/zalando/nakadi/client/examples/java/EventCreationExample.java index b3fd133..e14c9e1 100644 --- a/it/src/main/java/org/zalando/nakadi/client/examples/java/EventCreationExample.java +++ b/it/src/main/java/org/zalando/nakadi/client/examples/java/EventCreationExample.java @@ -83,7 +83,7 @@ public static void main(String[] args) throws InterruptedException, * their own partitions 4. A unique identifier for Subscribing * */ - String eventTypeName = "Example-1"; + String eventTypeName = "Example-"+System.currentTimeMillis(); /** * Create client @@ -130,6 +130,7 @@ public static void main(String[] args) throws InterruptedException, result = client.publishEvents(eventTypeName, Arrays.asList(event3), SerializationUtils.defaultSerializer()); result.get(); + client.stop(); } } diff --git a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala index 192326f..9badd5d 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala @@ -44,7 +44,7 @@ object EventCreationExample extends App { //See the API for more information on the EventType model //https://github.com/zalando/nakadi/blob/nakadi-jvm/api/nakadi-event-bus-api.yaml#L1240 // val eventTypeName = "Example-unique-million-messages" - val eventTypeName = "Example-2" + val eventTypeName = "Example-2000" // val eventTypeName = "Example-unique-hundred-messages-3" val owner = "team-laas" @@ -73,7 +73,7 @@ object EventCreationExample extends App { // 4. Publish the EventType var counter = 0 - for (n <- 1 to 1) { + for (n <- 1 to 10000) { val event = new MeetingsEvent("2016-04-28T13:28:15+00:00", "Hackaton") var events = ListBuffer[MeetingsEvent]() for (a <- 1 to 100) { diff --git a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala index aba0438..51d3870 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala @@ -20,7 +20,7 @@ class EventCounterListener(val id: String) extends Listener[MeetingsEvent] { private var callerCount: AtomicLong = new AtomicLong(0); def onError(sourceUrl: String, error: Option[ClientError]): Unit = { - println("Error %s %s %s".format(sourceUrl, error)) + println("Error %s %s".format(sourceUrl, error)) } def onReceive(sourceUrl: String, cursor: Cursor, events: Seq[MeetingsEvent]): Unit = { @@ -56,16 +56,16 @@ object EventListenerExample extends App { * Create the Parameters with the cursor. */ - val cursor = Cursor("0", "BEGIN") + val cursor = Cursor("0", "0") val parameters = new StreamParameters( cursor = Some(cursor) // - , batchLimit = Some(1) // Maximum number of `Event`s in each chunk (and therefore per partition) of the stream. + , batchLimit = Some(250) // Maximum number of `Event`s in each chunk (and therefore per partition) of the stream. // , streamLimit = Some(2) // Maximum number of `Event`s to stream (over all partitions being streamed in this //connection). - // , batchFlushTimeout = Some(5) // Maximum time in seconds to wait for the flushing of each chunk (per partition). + , batchFlushTimeout = Some(30) // Maximum time in seconds to wait for the flushing of each chunk (per partition). // ,streamKeepAliveLimit=Some(4) - // , streamTimeout = Some(30) + , streamTimeout = Some(30) ) /** @@ -76,7 +76,7 @@ object EventListenerExample extends App { import org.zalando.nakadi.client.scala.model.JacksonJsonMarshaller._ // val eventTypeName = "Event-example-with-0-messages" - val eventTypeName = "Example-1" + val eventTypeName = "Example-2000" val result = client.subscribe(eventTypeName, parameters, listener) // Thread.sleep(3000) diff --git a/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala b/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala index cdd0ad8..b145eef 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala @@ -6,10 +6,10 @@ import java.util.function.Supplier object ClientFactory { import sys.process._ import scala.language.postfixOps - def host(): String = "nakadi-sandbox.aruha-test.zalan.do" + def host(): String = "nakadi.test.fernando.io" def localHost(): String = "localhost" def localPort(): Integer = 8080 - def OAuth2Token(): Option[() => String] = Option(() => "63e78acc-3fae-46d6-b8c5-1c213d99ba7d") + def OAuth2Token(): Option[() => String] = Option(() => "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa") def port(): Integer = 443 def client(): Client = Connection.newClient(host, port, OAuth2Token(), true, false) @@ -19,21 +19,31 @@ object ClientFactory { def getScalaClient() = builder().build() private def builder() = { -// useRemote() +// useTest() +// useStaging() useLocal() } private def useLocal() = { new ClientBuilder() // - .withHost(ClientFactory.localHost()) // - .withPort(ClientFactory.localPort()) // + .withHost("localhost") // + .withPort(8080) // .withSecuredConnection(false) // s .withVerifiedSslCertificate(false) // s } - private def useRemote() = { + private def useTest() = { ClientBuilder() - .withHost(ClientFactory.host()) + .withHost("nakadi-sandbox.aruha-test.zalan.do") + .withPort(443) .withSecuredConnection(true) //s .withVerifiedSslCertificate(false) //s .withTokenProvider(ClientFactory.OAuth2Token()) } + private def useStaging() = { + ClientBuilder() + .withHost("nakadi-staging.aruha-test.zalan.do") + .withPort(443) + .withSecuredConnection(true) //s + .withVerifiedSslCertificate(false) //s + .withTokenProvider(ClientFactory.OAuth2Token()) + } } \ No newline at end of file From e8c3b0724d965b66013f71a18f21d53dc4a22d2f Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Tue, 7 Jun 2016 11:33:11 +0200 Subject: [PATCH 068/183] techjira:LAAS-60 Adding sbt-coverage to monitor coverage of code. --- project/plugins.sbt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/project/plugins.sbt b/project/plugins.sbt index 92199b6..e2a661d 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -2,8 +2,6 @@ resolvers += "Typesafe repository" at "https://repo.typesafe.com/typesafe/releas addSbtPlugin("com.typesafe.sbt" % "sbt-git" % "0.8.5") -addSbtPlugin("org.scoverage" % "sbt-scoverage" % "1.0.4") - addSbtPlugin("org.scoverage" % "sbt-coveralls" % "1.0.0") addSbtPlugin("com.codacy" % "sbt-codacy-coverage" % "1.1.0") @@ -11,3 +9,5 @@ addSbtPlugin("com.codacy" % "sbt-codacy-coverage" % "1.1.0") addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "4.0.0") addSbtPlugin("net.virtual-void" % "sbt-dependency-graph" % "0.8.2") + +addSbtPlugin("org.scoverage" % "sbt-scoverage" % "1.3.5") From 56bf9e67d631d33714c62fcff3c9827096a9a8d5 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Tue, 7 Jun 2016 11:42:14 +0200 Subject: [PATCH 069/183] techjira:LAAS-60 removing unnecessary code and using scheduler instead of sleeping thread to delay reconnection attempt --- .../nakadi/client/actor/ConsumingActor.scala | 12 +----------- .../client/handler/SubscriptionHandler.scala | 19 +++++++++---------- 2 files changed, 10 insertions(+), 21 deletions(-) diff --git a/client/src/main/scala/org/zalando/nakadi/client/actor/ConsumingActor.scala b/client/src/main/scala/org/zalando/nakadi/client/actor/ConsumingActor.scala index 789688c..0ec1ffd 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/actor/ConsumingActor.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/actor/ConsumingActor.scala @@ -74,19 +74,9 @@ class ConsumingActor(subscription: SubscriptionKey, log.info("Received Terminated msg - subscription {} with listener-id {} ", subscription, handler.id()) case a => log.error("Could not handle message: {}", a) + context.stop(self) } - override def postRestart(reason: Throwable) { - super.postRestart(reason) - log.info(s">>>>>>>>>>>>> <<<<<<<<<<<<<<<") - log.info(s">>>>>>>>>>>>> Restarted because of ${reason.getMessage}") - log.info(">>>>>>>>>>>>> Current cursor {} <<<<<<<<<<<<<<<", lastCursor) - - } - - override def postStop() { - log.info("Shutting down subscription {} with listener-id {} ", subscription, handler.id()) - } } diff --git a/client/src/main/scala/org/zalando/nakadi/client/handler/SubscriptionHandler.scala b/client/src/main/scala/org/zalando/nakadi/client/handler/SubscriptionHandler.scala index fdea2f6..43eef90 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/handler/SubscriptionHandler.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/handler/SubscriptionHandler.scala @@ -1,8 +1,9 @@ package org.zalando.nakadi.client.handler -import java.util.concurrent.TimeoutException - import scala.concurrent.ExecutionContext.Implicits.global +import scala.concurrent.duration.DurationInt +import scala.util.Failure +import scala.util.Success import org.slf4j.LoggerFactory import org.zalando.nakadi.client.actor.SupervisingActor @@ -20,7 +21,6 @@ import akka.actor.ActorRef import akka.actor.PoisonPill import akka.actor.Props import akka.actor.actorRef2Scala -import akka.http.scaladsl.model.EntityStreamException import akka.http.scaladsl.model.HttpRequest import akka.http.scaladsl.model.HttpResponse import akka.stream.ActorAttributes @@ -32,10 +32,6 @@ import akka.stream.scaladsl.Framing import akka.stream.scaladsl.Sink import akka.stream.scaladsl.Source import akka.util.ByteString -import akka.stream.StreamTcpException -import akka.stream.StreamTcpException -import scala.util.Success -import scala.util._ trait SubscriptionHandler { /** @@ -88,19 +84,22 @@ class SubscriptionHandlerImpl(val connection: Connection) extends SubscriptionHa result.onComplete { case Success(requestSource) ⇒ - logger.info("@@@@@@@@@@ YAY @@@@@@@@@@@@@") + logger.info("Connection established with success!") case Failure(e) ⇒ val msg = "An exception occurred: " + e.getMessage eventHandler.handleOnError(url, Some(msg), e) logger.error(msg + e) + materializer.scheduleOnce(5.seconds, { stopActor(consumingActor) }) Thread.sleep(10000) stopActor(consumingActor) } } private def stopActor(actor: ActorRef) = { - logger.info("Stopping actor {} with a PoisonPill", actor.path.toString()) - actor ! PoisonPill + new Runnable { + logger.info("Stopping the actor {}.", actor.path.toString()) + def run = actor ! PoisonPill + } } private def requestCreator(url: String): Flow[Option[Cursor], HttpRequest, NotUsed] = From 0eee4096b2cbf0a969c98e73aa56a05268cf8ee5 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Tue, 7 Jun 2016 11:42:28 +0200 Subject: [PATCH 070/183] techjira:LAAS-60 removing unnecessary code and using scheduler instead of sleeping thread to delay reconnection attempt --- .../nakadi/client/examples/java/EventListenerExample.java | 4 ++-- .../nakadi/client/examples/scala/EventCreationExample.scala | 6 +++--- project/Build.scala | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/it/src/main/java/org/zalando/nakadi/client/examples/java/EventListenerExample.java b/it/src/main/java/org/zalando/nakadi/client/examples/java/EventListenerExample.java index 2baf43f..eeee24b 100644 --- a/it/src/main/java/org/zalando/nakadi/client/examples/java/EventListenerExample.java +++ b/it/src/main/java/org/zalando/nakadi/client/examples/java/EventListenerExample.java @@ -31,7 +31,7 @@ public static void main(String[] args) throws InterruptedException, Listener listener = new EventCounterListener("Java-Test"); StreamParameters params = new StreamParameters(Optional.of(new Cursor( - "0", "BEGIN")), Optional.empty(),// batchLimit, + "0", "BEGIN")), Optional.of(200),// batchLimit, Optional.empty(),// streamLimit, Optional.empty(),// batchFlushTimeout, Optional.empty(),// streamTimeout, @@ -41,7 +41,7 @@ public static void main(String[] args) throws InterruptedException, // String eventTypeName = "Example-unique-hundred-messages-3"; - String eventTypeName = "Example-unique-million-messages"; + String eventTypeName = "Example-2000"; TypeReference> typeRef = new TypeReference>() { }; diff --git a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala index 9badd5d..a5274aa 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala @@ -68,15 +68,15 @@ object EventCreationExample extends App { //You need to import the default Serializer if you don't sepecify your own! import JacksonJsonMarshaller._ -// client.createEventType(eventType) - Thread.sleep(1000) + client.createEventType(eventType) + Thread.sleep(3000) // 4. Publish the EventType var counter = 0 for (n <- 1 to 10000) { val event = new MeetingsEvent("2016-04-28T13:28:15+00:00", "Hackaton") var events = ListBuffer[MeetingsEvent]() - for (a <- 1 to 100) { + for (a <- 1 to 200) { counter += 1 events += MeetingsEvent("2016-04-28T13:28:15+00:00", "Hackaton" + counter) } diff --git a/project/Build.scala b/project/Build.scala index 814d0f4..a5031c3 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -57,9 +57,9 @@ lazy val client = withDefaults( project.settings( name := projectName, organization := "org.zalando.nakadi.client", - version := "2.0.0-pre-alpha.7", + version := "2.0.0-pre-alpha.8", crossPaths := false, - scalaVersion := "2.11.7", + scalaVersion := "2.11.8", publishTo := whereToPublishTo(isSnapshot.value), resolvers += Resolver.mavenLocal, resolvers += "Maven Central Server" at "http://repo1.maven.org/maven2", From 6a22d15b6a102265dc1c93c092db86410e05e7ba Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Tue, 7 Jun 2016 16:01:11 +0200 Subject: [PATCH 071/183] techjira:LAAS-60 Fixes #52 --- README.md | 179 ------------------ .../zalando/nakadi/client/java/Client.java | 2 +- .../zalando/nakadi/client/scala/Client.scala | 2 +- .../nakadi/client/java/ClientImpl.java | 4 +- .../client/actor/SubscriptionHolder.scala | 13 +- .../client/actor/SupervisingActor.scala | 44 +++-- .../client/handler/SubscriptionHandler.scala | 21 +- .../client/java/JavaClientHandler.scala | 8 +- .../nakadi/client/scala/ClientImpl.scala | 37 ++-- .../nakadi/client/utils/ModelConverter.scala | 2 +- .../examples/java/EventListenerExample.java | 2 +- .../examples/scala/EventCreationExample.scala | 4 +- .../examples/scala/EventListenerExample.scala | 4 +- project/Build.scala | 2 +- project/Dependencies.scala | 7 + 15 files changed, 89 insertions(+), 242 deletions(-) diff --git a/README.md b/README.md index 8120444..153522a 100644 --- a/README.md +++ b/README.md @@ -9,184 +9,5 @@ Please note that the client provides a Scala as well as a Java interface. - Java >= 1.8 - Scala >= 2.11 - - -## Tutorial - -### Configuration - -``` -nakadi.client { - noListenerReconnectDelay = 10 seconds // if no listener could be found, no connection to Nakadi is established. - // noListenerReconnectDelay specifies the delay after which the actor - // should try again to connect - pollParallelism = 100 // number of parallel polls from a specific host - receiveBufferSize = 1024 bytes // initial buffer size for event retrieval - - defaultBatchFlushTimeout = 5 seconds // default batch flush timeout set in ListenParameters - defaultBatchLimit = 1 // default batch limit set in ListenParameters - defaultStreamLimit = 0 // default stream limit set in ListenParameters - - supervisor { - // note: Supervisor strategy parameter names are from the Akka - keep them like this - maxNrOfRetries = 100 - withinTimeRange = 5 minutes - - resolveActorTimeout = 1 second // timeout for resolving PartitionReceiver actor reference - } -} -``` - - -### Instantiate Client - -`Scala` -```scala -val klient = KlientBuilder() - .withEndpoint(new URI("localhost")) - .withPort(8080) - .withSecuredConnection(false) - .withTokenProvider(() => "") - .build() -``` - -`Java` -```Java -final Client client = new KlientBuilder() - .withEndpoint(new URI("localhost")) - .withPort(8080) - .withSecuredConnection(false) - .withJavaTokenProvider(() -> "") - .buildJavaClient(); -``` - -### Get Monitoring Metrics -NOTE: metrics format is not defined / fixed - -`Scala` -```scala -klient.getMetrics map ( _ match { - case Left(error) => fail(s"could not retrieve metrics: $error") - case Right(metrics: Map[String, Any]) => logger.debug(s"metrics => $metrics") -}) -``` - -`Java` -```java -Future> metrics = client.getMetrics(); -``` - -### List All Known Topics - -`Scala` -```scala -klient.getTopics map ( _ match { - case Left(error) => fail(s"could not retrieve topics: $error") - case Right(topics: List[Topic]) => logger.debug(s"topics => $topics") -}) -``` - -`Java` -```java -Fuure> topics = klient.getTopics(); -``` - -### Post a Single Event to the Given Topic -Partition selection is done using the defined partition resolution. The partition resolution strategy is defined per -topic and is managed by the event bus (currently resolved from a hash over `ordering_key`). - -`Scala` -```scala -val event = Event("eventType", - "orderingKey", - Map("id" -> "1234567890"), - Map("greeting" -> "hello", - "target" -> "world")) - -klient.postEvent(topic, event) map (_ match { - case Left(error) => fail(s"an error occurred while posting event to topic $topic") - case Right(_) => logger.debug("event post request was successful") -}) -``` - -`Java` -```java -HashMap meta = Maps.newHashMap(); -meta.put("id", "1234567890"); - -HashMap body = Maps.newHashMap(); -body.put("greeting", "hello"); -body.put("target", "world"); - -Future f = client.postEvent("test", new Event("eventType", "orderingKey", meta, body); -``` - - -### Get Partition Information of a Given Topic - -`Scala` -```scala -klient.getPartitions("topic") map (_ match { - case Left(error: String) => fail(s"could not retrieve partitions: $error") - case Right(partitions: List[TopicPartition]) => partitions -}) -``` - -`Java` -```java -Future> topics = klient.getPartitions("topic"); -``` - -### Subscribe to a given topic -Non-blocking subscription to a topic requires a `Listener` implementation. The event listener does not have to be -thread-safe because each listener is handled by its own `Akka` actor - -`Scala` -```scala -klient.subscribeToTopic("topic", ListenParameters(), listener, autoReconnect = true) // autoReconnect default is true -``` - -`Java` -```java -public final class MyListener implements JListener {...} - -client.subscribeToTopic("topic", - ListenParametersUtils.defaultInstance(), - new JListenerWrapper(new MyListener()), - true); - -``` - -### Subscribe to a Specific Partition -Non blocking subscription to events of specified topic and partition. - -`Scala` -```scala -klient.listenForEvents("topic", - "partitionId", - ListenParameters(), - listener, - autoReconnect = false) // default is false -``` - -`Java` -```java -public final class MyListener implements JListener {...} - -client.listenForEvent("topic", - "partitionId", - ListenParametersUtils.defaultInstance(), - new JListenerWrapper(new MyListener())); -``` - - -## See -- [Nakadi event bus](https://github.com/zalando/nakadi) -- [STUPS](https://github.com/zalando-stups) -- [STUPS' tokens library](https://github.com/zalando-stups/tokens) - -## TODO -- [ ] handle case where separate clusters consisting of 1 member are built - ## License http://opensource.org/licenses/MIT diff --git a/api/src/main/java/org/zalando/nakadi/client/java/Client.java b/api/src/main/java/org/zalando/nakadi/client/java/Client.java index fc6d2d1..5a1e950 100644 --- a/api/src/main/java/org/zalando/nakadi/client/java/Client.java +++ b/api/src/main/java/org/zalando/nakadi/client/java/Client.java @@ -159,5 +159,5 @@ public interface Client { * @param listener Listener to pass the event to when it is received. * @return Void in case of success */ - void unsubscribe(String eventTypeName,String partition, Listener listener); + void unsubscribe(String eventTypeName,Optional partition, Listener listener); } \ No newline at end of file diff --git a/api/src/main/scala/org/zalando/nakadi/client/scala/Client.scala b/api/src/main/scala/org/zalando/nakadi/client/scala/Client.scala index 04b52b1..babae17 100644 --- a/api/src/main/scala/org/zalando/nakadi/client/scala/Client.scala +++ b/api/src/main/scala/org/zalando/nakadi/client/scala/Client.scala @@ -177,7 +177,7 @@ trait Client { * @param partition The partition assigned to this listener. * @param listener - Listener to unsubscribe from the streaming events. */ - def unsubscribe[T <: Event](eventTypeName: String, partition:String, listener: Listener[T]): Future[Option[ClientError]] + def unsubscribe[T <: Event](eventTypeName: String, partition: Option[String], listener: Listener[T]): Future[Option[ClientError]] } diff --git a/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java b/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java index 1061ac6..9da723d 100644 --- a/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java +++ b/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java @@ -133,8 +133,8 @@ public void subscribe(String eventTypeName, StreamParameters p } @Override - public void unsubscribe(String eventTypeName, String partition, Listener listener) { - handler.unsubscribe(eventTypeName,partition, listener); + public void unsubscribe(String eventTypeName, Optional partition, Listener listener) { + handler.unsubscribe(eventTypeName, partition, listener); } } \ No newline at end of file diff --git a/client/src/main/scala/org/zalando/nakadi/client/actor/SubscriptionHolder.scala b/client/src/main/scala/org/zalando/nakadi/client/actor/SubscriptionHolder.scala index 6350a77..441a8e6 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/actor/SubscriptionHolder.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/actor/SubscriptionHolder.scala @@ -6,9 +6,12 @@ import akka.actor.ActorRef import org.slf4j.LoggerFactory import com.typesafe.scalalogging.Logger +/** + * Class that keeps track of the subscriptions + */ trait SubscriptionHolder { import SupervisingActor._ - def addCursor(key: SubscriptionKey, cursor: Cursor): Unit + def addCursor(key: SubscriptionKey, cursor: Option[Cursor]): Unit def addSubscription(key: SubscriptionKey, key2: ActorRef, entry: SubscriptionEntry): Unit def entry(key: SubscriptionKey): Option[SubscriptionEntry] def entryByActor(actor: ActorRef): Option[SubscriptionEntry] @@ -20,12 +23,12 @@ trait SubscriptionHolder { class SubscriptionHolderImpl extends SubscriptionHolder { import SupervisingActor._ private var subscriptions: Map[SubscriptionKey, SubscriptionEntry] = Map() //EventTypeName+Partition - private var cursors: Map[SubscriptionKey, Cursor] = Map() + private var cursors: Map[SubscriptionKey, Option[Cursor]] = Map() private var actors: Map[String, SubscriptionKey] = Map() - private var subscriptionCounter = 0 + private var subscriptionCounter = 1 private val logger = Logger(LoggerFactory.getLogger(this.getClass)) - def addCursor(key: SubscriptionKey, cursor: Cursor): Unit = { + def addCursor(key: SubscriptionKey, cursor: Option[Cursor]): Unit = { cursors = cursors + ((key, cursor)) } @@ -63,7 +66,7 @@ class SubscriptionHolderImpl extends SubscriptionHolder { } def cursorByActor(actor: ActorRef): Option[Cursor] = { - actors.get(actor.path.toString()).flatMap(x => cursors.get(x)) + actors.get(actor.path.toString()).flatMap(x => cursors.get(x).flatMap(a => a )) } } \ No newline at end of file diff --git a/client/src/main/scala/org/zalando/nakadi/client/actor/SupervisingActor.scala b/client/src/main/scala/org/zalando/nakadi/client/actor/SupervisingActor.scala index 0c2e557..b8ddcf8 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/actor/SupervisingActor.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/actor/SupervisingActor.scala @@ -26,13 +26,16 @@ import akka.util.ByteString object SupervisingActor { case class SubscribeMsg(eventTypeName: String, endpoint: String, cursor: Option[Cursor], handler: EventHandler) { - override def toString(): String = + override def toString(): String = s"SubscriptionKey(eventTypeName: $eventTypeName - endpoint: $endpoint - cursor: $cursor - ${handler.id})" } - case class UnsubscribeMsg(eventTypeName: String, partition: String, eventHandlerId: String) + case class UnsubscribeMsg(eventTypeName: String, partition: Option[String], eventHandlerId: String) case class OffsetMsg(cursor: Cursor, subKey: SubscriptionKey) - case class SubscriptionKey(eventTypeName: String, partition: String) { - override def toString(): String = s"SubscriptionKey(eventTypeName:$eventTypeName - Partition:$partition)"; + case class SubscriptionKey(eventTypeName: String, partition: Option[String]) { + override def toString(): String = partition match { + case Some(p) => s"SubscriptionKey(eventTypeName:$eventTypeName - Partition:$p)"; + case None => s"SubscriptionKey(eventTypeName:$eventTypeName)"; + } } case class SubscriptionEntry(subuscription: SubscribeMsg, actor: ActorRef) } @@ -56,7 +59,7 @@ class SupervisingActor(val connection: Connection, val subscriptionHandler: Subs def receive: Receive = { case OffsetMsg(cursor, subKey) => log.info("Received cursor {} - subKey {}", cursor, subKey) - subscriptions.addCursor(subKey, cursor) + subscriptions.addCursor(subKey, Some(cursor)) case subscrition: SubscribeMsg => log.info("New subscription {}", subscrition) subscribe(subscrition) @@ -67,7 +70,13 @@ class SupervisingActor(val connection: Connection, val subscriptionHandler: Subs log.info(s"ConsumingActor terminated {}", terminatedActor.path.name) subscriptions.entryByActor(terminatedActor) match { case Some(SubscriptionEntry(SubscribeMsg(eventTypeName, endpoint, Some(Cursor(partition, offset)), handler), actor: ActorRef)) => - val unsubscription = UnsubscribeMsg(eventTypeName, partition, handler.id()) + val unsubscription = UnsubscribeMsg(eventTypeName, Option(partition), handler.id()) + unsubscribe(unsubscription) + val cursor = subscriptions.cursorByActor(terminatedActor) + val subscription = SubscribeMsg(eventTypeName, endpoint, cursor, handler) + subscribe(subscription) + case Some(SubscriptionEntry(SubscribeMsg(eventTypeName, endpoint, None, handler), actor: ActorRef)) => + val unsubscription = UnsubscribeMsg(eventTypeName, None, handler.id()) unsubscribe(unsubscription) val cursor = subscriptions.cursorByActor(terminatedActor) val subscription = SubscribeMsg(eventTypeName, endpoint, cursor, handler) @@ -81,28 +90,27 @@ class SupervisingActor(val connection: Connection, val subscriptionHandler: Subs def subscribe(subscribe: SubscribeMsg) = { val SubscribeMsg(eventTypeName, endpoint, optCursor, eventHandler) = subscribe - log.info("Subscription nr {} - cursor {} - eventType {} - listener {}", (subscriptions.activeSize + 1), optCursor,eventTypeName, eventHandler.id()) + log.info("Subscription nr {} - cursor {} - eventType {} - listener {}", subscriptions.activeSize , optCursor, eventTypeName, eventHandler.id()) - val Some(cursor) = optCursor - val Cursor(partition, _) = cursor - val subKey: SubscriptionKey = SubscriptionKey(eventTypeName, partition) + val subKey: SubscriptionKey = optCursor match { + case Some(Cursor(partition, _)) => SubscriptionKey(eventTypeName, Some(partition)) + case None => SubscriptionKey(eventTypeName, None) + } //Create the Consumer val consumingActor = context.actorOf(Props(classOf[ConsumingActor], subKey, eventHandler), "EventConsumingActor-" + subscriptions.activeSize) - + context.watch(consumingActor) //If the streaming is ending!! - + val subEntry: SubscriptionEntry = SubscriptionEntry(subscribe, consumingActor) // Notify listener it is subscribed - eventHandler.handleOnSubscribed(endpoint, Option(cursor)) - - - + eventHandler.handleOnSubscribed(endpoint, optCursor) + //Create the pipeline - subscriptionHandler.createPipeline(Option(cursor), consumingActor, endpoint, eventHandler) + subscriptionHandler.createPipeline(optCursor, consumingActor, endpoint, eventHandler) subscriptions.addSubscription(subKey, consumingActor, subEntry) - subscriptions.addCursor(subKey, cursor) + subscriptions.addCursor(subKey, optCursor) } def unsubscribe(unsubscription: UnsubscribeMsg) = { diff --git a/client/src/main/scala/org/zalando/nakadi/client/handler/SubscriptionHandler.scala b/client/src/main/scala/org/zalando/nakadi/client/handler/SubscriptionHandler.scala index 43eef90..4868eba 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/handler/SubscriptionHandler.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/handler/SubscriptionHandler.scala @@ -32,13 +32,14 @@ import akka.stream.scaladsl.Framing import akka.stream.scaladsl.Sink import akka.stream.scaladsl.Source import akka.util.ByteString +import org.zalando.nakadi.client.scala.ClientError trait SubscriptionHandler { /** * Handles the subscription for an eventHandler. */ - def subscribe(eventTypeName: String, endpoint: String, cursor: Option[Cursor], eventHandler: EventHandler) - def unsubscribe(eventTypeName: String, partition: String, listenerId: String) + def subscribe(eventTypeName: String, endpoint: String, cursor: Option[Cursor], eventHandler: EventHandler): Option[ClientError] + def unsubscribe(eventTypeName: String, partition: Option[String], listenerId: String): Option[ClientError] def createPipeline(cursor: Option[Cursor], consumingActor: ActorRef, url: String, eventHandler: EventHandler) } @@ -48,7 +49,7 @@ class SubscriptionHandlerImpl(val connection: Connection) extends SubscriptionHa //Local variables private implicit val materializer = connection.materializer(decider()) - private val supervisingActor = connection.actorSystem.actorOf(Props(classOf[SupervisingActor], connection, this), "EventReceivingActor-" + System.currentTimeMillis()) + private val supervisingActor = connection.actorSystem.actorOf(Props(classOf[SupervisingActor], connection, this), "SupervisingActor" + System.currentTimeMillis()) private val RECEIVE_BUFFER_SIZE = 40960 private val EVENT_DELIMITER = "\n" @@ -57,9 +58,11 @@ class SubscriptionHandlerImpl(val connection: Connection) extends SubscriptionHa def subscribe(eventTypeName: String, endpoint: String, cursor: Option[Cursor], eventHandler: EventHandler) = { supervisingActor ! SubscribeMsg(eventTypeName, endpoint, cursor, eventHandler) + None } - def unsubscribe(eventTypeName: String, partition: String, listenerId: String) = { + def unsubscribe(eventTypeName: String, partition: Option[String], listenerId: String) = { supervisingActor ! UnsubscribeMsg(eventTypeName, partition, listenerId) + None } def decider(): Supervision.Decider = { @@ -89,17 +92,13 @@ class SubscriptionHandlerImpl(val connection: Connection) extends SubscriptionHa val msg = "An exception occurred: " + e.getMessage eventHandler.handleOnError(url, Some(msg), e) logger.error(msg + e) - materializer.scheduleOnce(5.seconds, { stopActor(consumingActor) }) - Thread.sleep(10000) - stopActor(consumingActor) + connection.actorSystem().scheduler.scheduleOnce(5.seconds)(stopActor(consumingActor)) //TODO: Make it configurable } } private def stopActor(actor: ActorRef) = { - new Runnable { - logger.info("Stopping the actor {}.", actor.path.toString()) - def run = actor ! PoisonPill - } + logger.info("Stopping the actor [{}]", actor.path.toString()) + actor ! PoisonPill } private def requestCreator(url: String): Flow[Option[Cursor], HttpRequest, NotUsed] = diff --git a/client/src/main/scala/org/zalando/nakadi/client/java/JavaClientHandler.scala b/client/src/main/scala/org/zalando/nakadi/client/java/JavaClientHandler.scala index e10c9a8..cef47e3 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/java/JavaClientHandler.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/java/JavaClientHandler.scala @@ -35,6 +35,7 @@ import akka.http.scaladsl.model.HttpResponse import akka.http.scaladsl.unmarshalling.Unmarshal import org.zalando.nakadi.client.handler.SubscriptionHandlerImpl import org.zalando.nakadi.client.handler.SubscriptionHandler +import org.zalando.nakadi.client.utils.GeneralConversions /** * Handler for mapping(Java<->Scala) and handling http calls and listener subscriptions for the Java API. @@ -45,13 +46,14 @@ trait JavaClientHandler { def get[T](endpoint: String, headers: Seq[HttpHeader], des: Deserializer[T]): java.util.concurrent.Future[Optional[T]] def post[T](endpoint: String, model: T)(implicit serializer: Serializer[T]): java.util.concurrent.Future[Void] def subscribe[T <: JEvent](eventTypeName: String, endpoint: String, parameters: JStreamParameters, listener: JListener[T])(implicit des: Deserializer[JEventStreamBatch[T]]) - def unsubscribe[T <: JEvent](eventTypeName: String,partition:String, listener: JListener[T]) + def unsubscribe[T <: JEvent](eventTypeName: String, partition: Optional[String], listener: JListener[T]) } class JavaClientHandlerImpl(val connection: Connection, subscriber: SubscriptionHandler) extends JavaClientHandler { val logger: Logger = Logger(LoggerFactory.getLogger(this.getClass)) import HttpFactory._ + import GeneralConversions._ private implicit val mat = connection.materializer() //TODO: Use constructor later make the tests simpler @@ -117,8 +119,8 @@ class JavaClientHandlerImpl(val connection: Connection, subscriber: Subscription }) } - def unsubscribe[T <: JEvent](eventTypeName: String,partition:String, listener: JListener[T]) = { - subscriber.unsubscribe(eventTypeName,partition, listener.getId) + def unsubscribe[T <: JEvent](eventTypeName: String, partition: Optional[String], listener: JListener[T]) = { + subscriber.unsubscribe(eventTypeName, toOption(partition), listener.getId) } private def getCursor(params: Option[ScalaStreamParameters]): Option[ScalaCursor] = params match { case Some(ScalaStreamParameters(cursor, batchLimit, streamLimit, batchFlushTimeout, streamTimeout, streamKeepAliveLimit, flowId)) => cursor diff --git a/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala index 4ef654c..47d71ea 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala @@ -1,27 +1,34 @@ package org.zalando.nakadi.client.scala -import scala.{ Left, Right } +import scala.Left +import scala.Right +import scala.concurrent.Await import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.Future +import scala.concurrent.duration.Duration import scala.concurrent.duration.DurationInt + import org.slf4j.LoggerFactory -import com.typesafe.scalalogging.Logger -import akka.actor.Terminated -import akka.http.scaladsl.model.{ HttpHeader, HttpMethod, HttpMethods, HttpResponse, MediaRange } -import akka.http.scaladsl.model.MediaTypes.`application/json` -import akka.http.scaladsl.model.StatusCodes -import akka.http.scaladsl.model.headers.{ Accept, RawHeader } -import akka.http.scaladsl.unmarshalling.Unmarshal -import scala.concurrent.Await -import scala.concurrent.duration.Duration -import org.zalando.nakadi.client.scala.model.JacksonJsonMarshaller import org.zalando.nakadi.client.Deserializer import org.zalando.nakadi.client.Serializer -import org.zalando.nakadi.client.scala.model._ +import org.zalando.nakadi.client.handler.SubscriptionHandler +import org.zalando.nakadi.client.scala.model.Event +import org.zalando.nakadi.client.scala.model.EventEnrichmentStrategy +import org.zalando.nakadi.client.scala.model.EventStreamBatch +import org.zalando.nakadi.client.scala.model.EventType +import org.zalando.nakadi.client.scala.model.EventValidationStrategy +import org.zalando.nakadi.client.scala.model.JacksonJsonMarshaller +import org.zalando.nakadi.client.scala.model.Metrics +import org.zalando.nakadi.client.scala.model.Partition +import org.zalando.nakadi.client.scala.model.PartitionStrategy import org.zalando.nakadi.client.utils.Uri + import com.fasterxml.jackson.core.`type`.TypeReference -import org.zalando.nakadi.client.handler.SubscriptionHandlerImpl -import org.zalando.nakadi.client.handler.SubscriptionHandler +import com.typesafe.scalalogging.Logger + +import akka.http.scaladsl.model.HttpResponse +import akka.http.scaladsl.model.StatusCodes +import akka.http.scaladsl.unmarshalling.Unmarshal class ClientImpl(connection: Connection, subscriber: SubscriptionHandler, charSet: String = "UTF-8") extends Client { import Uri._ @@ -115,7 +122,7 @@ class ClientImpl(connection: Connection, subscriber: SubscriptionHandler, charSe Future.successful(None) } - def unsubscribe[T <: Event](eventTypeName: String, partition: String, listener: Listener[T]): Future[Option[ClientError]] = { + def unsubscribe[T <: Event](eventTypeName: String, partition: Option[String], listener: Listener[T]): Future[Option[ClientError]] = { subscriber.unsubscribe(eventTypeName, partition, listener.id) Future.successful(None) } diff --git a/client/src/main/scala/org/zalando/nakadi/client/utils/ModelConverter.scala b/client/src/main/scala/org/zalando/nakadi/client/utils/ModelConverter.scala index d7954c8..b35b12e 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/utils/ModelConverter.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/utils/ModelConverter.scala @@ -55,7 +55,7 @@ object ModelConverter { } def toJavaCursor(in: Option[Cursor]): Optional[JCursor] = in match { case Some(Cursor(partition, offset)) => Optional.of(new JCursor(partition, offset)) - case None => null + case None => Optional.empty() } diff --git a/it/src/main/java/org/zalando/nakadi/client/examples/java/EventListenerExample.java b/it/src/main/java/org/zalando/nakadi/client/examples/java/EventListenerExample.java index eeee24b..b9168d6 100644 --- a/it/src/main/java/org/zalando/nakadi/client/examples/java/EventListenerExample.java +++ b/it/src/main/java/org/zalando/nakadi/client/examples/java/EventListenerExample.java @@ -31,7 +31,7 @@ public static void main(String[] args) throws InterruptedException, Listener listener = new EventCounterListener("Java-Test"); StreamParameters params = new StreamParameters(Optional.of(new Cursor( - "0", "BEGIN")), Optional.of(200),// batchLimit, + "0", "BEGIN")), Optional.of(100),// batchLimit, Optional.empty(),// streamLimit, Optional.empty(),// batchFlushTimeout, Optional.empty(),// streamTimeout, diff --git a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala index a5274aa..21a8dfb 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala @@ -73,10 +73,10 @@ object EventCreationExample extends App { // 4. Publish the EventType var counter = 0 - for (n <- 1 to 10000) { + for (n <- 1 to 5000) { val event = new MeetingsEvent("2016-04-28T13:28:15+00:00", "Hackaton") var events = ListBuffer[MeetingsEvent]() - for (a <- 1 to 200) { + for (a <- 1 to 100) { counter += 1 events += MeetingsEvent("2016-04-28T13:28:15+00:00", "Hackaton" + counter) } diff --git a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala index 51d3870..582d2c5 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala @@ -59,11 +59,11 @@ object EventListenerExample extends App { val cursor = Cursor("0", "0") val parameters = new StreamParameters( - cursor = Some(cursor) // + cursor = None//Some(cursor) // , batchLimit = Some(250) // Maximum number of `Event`s in each chunk (and therefore per partition) of the stream. // , streamLimit = Some(2) // Maximum number of `Event`s to stream (over all partitions being streamed in this //connection). - , batchFlushTimeout = Some(30) // Maximum time in seconds to wait for the flushing of each chunk (per partition). + , batchFlushTimeout = Some(5) // Maximum time in seconds to wait for the flushing of each chunk (per partition). // ,streamKeepAliveLimit=Some(4) , streamTimeout = Some(30) ) diff --git a/project/Build.scala b/project/Build.scala index a5031c3..380ac84 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -51,7 +51,7 @@ lazy val client = withDefaults( lazy val it = withDefaults( "nakadi-klients-integration-test", project.in(file("it")).dependsOn(api, client) - ).settings(libraryDependencies ++= clientDeps) + ).settings(libraryDependencies ++= itDeps) def withDefaults(projectName:String, project:sbt.Project)={ project.settings( diff --git a/project/Dependencies.scala b/project/Dependencies.scala index 45ffc81..17b4454 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -28,4 +28,11 @@ object Dependencies { "org.mockito" % "mockito-core" % "1.10.19" % "test" ) } + + val itDeps = clientDeps ++ { + Seq( + "org.zalando.stups" % "tokens" % "0.9.9", + "org.apache.httpcomponents" % "httpclient" % "4.5.2" + ) + } } From e572613707130bd4857597b85bd1feb81b5ce8f8 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Tue, 7 Jun 2016 17:25:34 +0200 Subject: [PATCH 072/183] techjira:LAAS-60 Changes in readme. Development release 2.0.0-pre-alpha.9 --- README.md | 9 ++++++++- project/Build.scala | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 153522a..bba936c 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,17 @@ -Nakadi Klients +Nakadi Klients (IN DEVELOPMENT) ============== Implementation of a non blocking client accessing the low level API of the [Nakadi event bus](https://github.com/zalando/nakadi). Internally, it uses [Akka](http://akka.io/) and [Akka Http](http://doc.akka.io/docs/akka-stream-and-http-experimental/2.0.2/scala/http/) to implement its communication tasks. + Please note that the client provides a Scala as well as a Java interface. +## Note + +* The new client (version >= '2.0.0') is still in development and not production ready. +* The new client documentation can be found in the [wiki](https://github.com/zalando/nakadi-klients/wiki). + + ## Prerequisites - Java >= 1.8 - Scala >= 2.11 diff --git a/project/Build.scala b/project/Build.scala index 380ac84..223aa45 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -57,7 +57,7 @@ lazy val client = withDefaults( project.settings( name := projectName, organization := "org.zalando.nakadi.client", - version := "2.0.0-pre-alpha.8", + version := "2.0.0-pre-alpha.9", crossPaths := false, scalaVersion := "2.11.8", publishTo := whereToPublishTo(isSnapshot.value), From 2dd9f3ff17bffdc17845d8efa12cbffc27f2b8b5 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Tue, 7 Jun 2016 17:42:32 +0200 Subject: [PATCH 073/183] techjira:LAAS-60 Development release 2.0.0-pre-alpha.10 --- project/Build.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/Build.scala b/project/Build.scala index 223aa45..5e7ec43 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -57,7 +57,7 @@ lazy val client = withDefaults( project.settings( name := projectName, organization := "org.zalando.nakadi.client", - version := "2.0.0-pre-alpha.9", + version := "2.0.0-pre-alpha.10", crossPaths := false, scalaVersion := "2.11.8", publishTo := whereToPublishTo(isSnapshot.value), From f53ab5391a708a6c37bb4ee940a75ab7d147322f Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Thu, 9 Jun 2016 13:29:19 +0200 Subject: [PATCH 074/183] techjira:LAAS-60 Model is up to date untill Nakadi(57cace). Fixes #43, #41 --- .../enumerator/BatchItemPublishingStatus.java | 6 +- .../client/java/enumerator/BatchItemStep.java | 12 +- .../enumerator/EventValidationStrategy.java | 2 +- .../client/java/model/BatchItemResponse.java | 12 +- .../client/java/model/DataChangeEvent.java | 17 ++- .../client/java/model/EventMetadata.java | 24 +++- .../client/java/model/EventStreamBatch.java | 7 +- .../nakadi/client/java/model/EventType.java | 3 +- .../java/model/EventTypeStatistics.java | 19 ++- .../nakadi/client/java/model/Metrics.java | 7 +- .../nakadi/client/java/model/Partition.java | 6 +- .../nakadi/client/java/model/Problem.java | 21 ++- .../nakadi/client/scala/model/Model.scala | 69 ++++----- .../nakadi/client/scala/ClientImpl.scala | 19 ++- .../nakadi/client/utils/TestScalaEntity.scala | 12 +- .../nakadi/client/scala/TestFactory.scala | 56 ++++---- .../scala/test/factory/ClientActions.scala | 36 +++++ .../scala/test/factory/EventGenerator.scala | 94 +++++++++++++ .../test/factory/EventIntegrationTest.scala | 45 ++++++ .../test/factory/events/MySimpleEvent.scala | 14 ++ it/src/test/resources/reference.conf | 21 +++ .../client/ClientSubscriptionTest.scala | 4 +- .../client/EventTypesIntegrationTest.scala | 16 ++- .../client/example2/ClientExample.scala | 52 ------- .../client/example2/ServerExample.scala | 53 ------- .../client/logic/ReactiveStreamsSupport.scala | 16 --- .../nakadi/client/logic/Receiver.scala | 56 -------- .../nakadi/client/logic/ReceiverGraph.scala | 56 -------- .../zalando/nakadi/client/logic/package.scala | 7 - .../scala/SimpleEventIntegrationTest.scala | 132 ++++++++++++++++++ 30 files changed, 528 insertions(+), 366 deletions(-) rename {client/src/test => it/src/main}/scala/org/zalando/nakadi/client/scala/TestFactory.scala (53%) create mode 100644 it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/ClientActions.scala create mode 100644 it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/EventGenerator.scala create mode 100644 it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/EventIntegrationTest.scala create mode 100644 it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/events/MySimpleEvent.scala create mode 100644 it/src/test/resources/reference.conf delete mode 100644 it/src/test/scala/org/zalando/nakadi/client/example2/ClientExample.scala delete mode 100644 it/src/test/scala/org/zalando/nakadi/client/example2/ServerExample.scala delete mode 100644 it/src/test/scala/org/zalando/nakadi/client/logic/ReactiveStreamsSupport.scala delete mode 100644 it/src/test/scala/org/zalando/nakadi/client/logic/Receiver.scala delete mode 100644 it/src/test/scala/org/zalando/nakadi/client/logic/ReceiverGraph.scala delete mode 100644 it/src/test/scala/org/zalando/nakadi/client/logic/package.scala create mode 100644 it/src/test/scala/org/zalando/nakadi/client/scala/SimpleEventIntegrationTest.scala diff --git a/api/src/main/java/org/zalando/nakadi/client/java/enumerator/BatchItemPublishingStatus.java b/api/src/main/java/org/zalando/nakadi/client/java/enumerator/BatchItemPublishingStatus.java index 0622e92..2aabe68 100644 --- a/api/src/main/java/org/zalando/nakadi/client/java/enumerator/BatchItemPublishingStatus.java +++ b/api/src/main/java/org/zalando/nakadi/client/java/enumerator/BatchItemPublishingStatus.java @@ -13,9 +13,9 @@ *
      */ public enum BatchItemPublishingStatus { - SUBMITTED("SUBMITTED"), // - FAILED("FAILED"), // - ABORTED("ABORTED"); + SUBMITTED("submitted"), // + FAILED("failed"), // + ABORTED("aborted"); private final String status; private BatchItemPublishingStatus(String status) { diff --git a/api/src/main/java/org/zalando/nakadi/client/java/enumerator/BatchItemStep.java b/api/src/main/java/org/zalando/nakadi/client/java/enumerator/BatchItemStep.java index 71d466c..34bc794 100644 --- a/api/src/main/java/org/zalando/nakadi/client/java/enumerator/BatchItemStep.java +++ b/api/src/main/java/org/zalando/nakadi/client/java/enumerator/BatchItemStep.java @@ -13,14 +13,14 @@ * only in the case of aborting the publishing during the validation of another (previous) Event.
      * - VALIDATING, ENRICHING, PARTITIONING and PUBLISHING indicate all the corresponding steps of the * publishing process.

      - * Values = NONE("NONE"), VALIDATING("VALIDATING"), ENRICHING("ENRICHING"), PARTITIONING("PARTITIONING"), PUBLISHING("PUBLISHING") + * Values = NONE("none"), VALIDATING("validating"), ENRICHING("enriching"), PARTITIONING("partitioning"), PUBLISHING("publishing") */ public enum BatchItemStep { - NONE("NONE"), // - VALIDATING("VALIDATING"), // - ENRICHING("ENRICHING"), // - PARTITIONING("PARTITIONING"), // - PUBLISHING("PUBLISHING"); + NONE("none"), // + VALIDATING("validating"), // + ENRICHING("enriching"), // + PARTITIONING("partitioning"), // + PUBLISHING("publishing"); private final String step; private BatchItemStep(String step) { diff --git a/api/src/main/java/org/zalando/nakadi/client/java/enumerator/EventValidationStrategy.java b/api/src/main/java/org/zalando/nakadi/client/java/enumerator/EventValidationStrategy.java index de99c6d..cb437e5 100644 --- a/api/src/main/java/org/zalando/nakadi/client/java/enumerator/EventValidationStrategy.java +++ b/api/src/main/java/org/zalando/nakadi/client/java/enumerator/EventValidationStrategy.java @@ -10,7 +10,7 @@ * See GET /registry/validation-strategies for a list of available rules. */ public enum EventValidationStrategy { - NONE("None"); + SCHEMA_VALIDATION("schema-validation"); private final String strategy; private EventValidationStrategy(String strategy) { diff --git a/api/src/main/java/org/zalando/nakadi/client/java/model/BatchItemResponse.java b/api/src/main/java/org/zalando/nakadi/client/java/model/BatchItemResponse.java index 9e5b67d..a95220d 100644 --- a/api/src/main/java/org/zalando/nakadi/client/java/model/BatchItemResponse.java +++ b/api/src/main/java/org/zalando/nakadi/client/java/model/BatchItemResponse.java @@ -3,6 +3,9 @@ import org.zalando.nakadi.client.java.enumerator.BatchItemPublishingStatus; import org.zalando.nakadi.client.java.enumerator.BatchItemStep; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + /** * A status corresponding to one individual Event's publishing attempt. * @@ -41,9 +44,12 @@ public class BatchItemResponse { * Items that are not SUBMITTED should have a description. * */ - public BatchItemResponse(String eid, - BatchItemPublishingStatus publishingStatus, BatchItemStep step, - String detail) { + @JsonCreator + public BatchItemResponse( + @JsonProperty("eid") String eid, + @JsonProperty("publishing_status") BatchItemPublishingStatus publishingStatus, + @JsonProperty("step") BatchItemStep step, + @JsonProperty("detail") String detail) { this.eid = eid; this.publishingStatus = publishingStatus; this.step = step; diff --git a/api/src/main/java/org/zalando/nakadi/client/java/model/DataChangeEvent.java b/api/src/main/java/org/zalando/nakadi/client/java/model/DataChangeEvent.java index 5f4270c..7841b01 100644 --- a/api/src/main/java/org/zalando/nakadi/client/java/model/DataChangeEvent.java +++ b/api/src/main/java/org/zalando/nakadi/client/java/model/DataChangeEvent.java @@ -2,15 +2,26 @@ import org.zalando.nakadi.client.java.enumerator.DataOperation; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + public class DataChangeEvent implements DataChangeEventQualifier, Event { private final T data; private final String dataType; private final DataOperation dataOperation; + private final EventMetadata metadata; - public DataChangeEvent(T data, String dataType, DataOperation dataOperation) { + @JsonCreator + public DataChangeEvent( + @JsonProperty T data, + @JsonProperty("data_type") String dataType, + @JsonProperty("data_op") DataOperation dataOperation, + @JsonProperty("metadata") EventMetadata metadata + ) { this.data = data; this.dataType = dataType; this.dataOperation = dataOperation; + this.metadata=metadata; } public T getData() { @@ -27,4 +38,8 @@ public DataOperation getDataOperation() { return dataOperation; } + public EventMetadata getMetadata() { + return metadata; + } + } diff --git a/api/src/main/java/org/zalando/nakadi/client/java/model/EventMetadata.java b/api/src/main/java/org/zalando/nakadi/client/java/model/EventMetadata.java index cb3688a..932d2f7 100644 --- a/api/src/main/java/org/zalando/nakadi/client/java/model/EventMetadata.java +++ b/api/src/main/java/org/zalando/nakadi/client/java/model/EventMetadata.java @@ -2,6 +2,9 @@ import java.util.List; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + /** * Metadata for this Event. Contains commons fields for both Business and * DataChange Events. Most are enriched by Nakadi upon reception, but they in @@ -9,7 +12,7 @@ */ public class EventMetadata { private final String eid; - private final EventType eventType; + private final String eventTypeName; private final String occurredAt; private final String receivedAt; private final List parentEids; @@ -27,7 +30,7 @@ public class EventMetadata { * and this SHOULD be guaranteed to be unique from the * perspective of the producer. Consumers MIGHT use this value to * assert uniqueness of reception of the Event. - * @param eventType + * @param eventTypeName * The EventType of this Event. This is enriched by Nakadi on * reception of the Event based on the endpoint where the * Producer sent the Event to. If provided MUST match the @@ -51,11 +54,18 @@ public class EventMetadata { * logic. Should be mainly enriched by the Consumer. * */ - public EventMetadata(String eid, EventType eventType, String occurredAt, String receivedAt, - List parentEids, String flowId, String partition) { + @JsonCreator + public EventMetadata( + @JsonProperty("eid") String eid, + @JsonProperty("event_type")String eventTypeName, + @JsonProperty("occurred_at")String occurredAt, + @JsonProperty("received_at")String receivedAt, + @JsonProperty("parent_eids")List parentEids, + @JsonProperty("flow_id")String flowId, + @JsonProperty("partition")String partition) { super(); this.eid = eid; - this.eventType = eventType; + this.eventTypeName = eventTypeName; this.occurredAt = occurredAt; this.receivedAt = receivedAt; this.parentEids = parentEids; @@ -67,8 +77,8 @@ public String getEid() { return eid; } - public EventType getEventType() { - return eventType; + public String getEventTypeName() { + return eventTypeName; } public String getOccurredAt() { diff --git a/api/src/main/java/org/zalando/nakadi/client/java/model/EventStreamBatch.java b/api/src/main/java/org/zalando/nakadi/client/java/model/EventStreamBatch.java index e0553be..e5147c5 100644 --- a/api/src/main/java/org/zalando/nakadi/client/java/model/EventStreamBatch.java +++ b/api/src/main/java/org/zalando/nakadi/client/java/model/EventStreamBatch.java @@ -43,11 +43,8 @@ public class EventStreamBatch { @JsonCreator public EventStreamBatch( - @JsonProperty("cursor") - Cursor cursor, - - @JsonProperty("events") - List events + @JsonProperty("cursor") Cursor cursor, + @JsonProperty("events") List events ) { super(); this.cursor = cursor; diff --git a/api/src/main/java/org/zalando/nakadi/client/java/model/EventType.java b/api/src/main/java/org/zalando/nakadi/client/java/model/EventType.java index 6a9ff93..8cc9a4c 100644 --- a/api/src/main/java/org/zalando/nakadi/client/java/model/EventType.java +++ b/api/src/main/java/org/zalando/nakadi/client/java/model/EventType.java @@ -109,14 +109,13 @@ public EventType( @JsonProperty("name") String name, @JsonProperty("owning_application") String owningApplication, @JsonProperty("category") EventTypeCategory category, - @JsonProperty("validation_strategies") List validationStrategies, @JsonProperty("enrichment_strategies") List enrichmentStrategies, @JsonProperty("partition_strategy") PartitionStrategy partitionStrategy, @JsonProperty("schema") EventTypeSchema schema, @JsonProperty("data_key_fields") List dataKeyFields, @JsonProperty("partition_key_fields") List partitionKeyFields, - @JsonProperty("statistics") EventTypeStatistics statistics) { + @JsonProperty("default_statistics") EventTypeStatistics statistics) { this.name = name; this.owningApplication = owningApplication; this.category = category; diff --git a/api/src/main/java/org/zalando/nakadi/client/java/model/EventTypeStatistics.java b/api/src/main/java/org/zalando/nakadi/client/java/model/EventTypeStatistics.java index ba56e12..cc7371f 100644 --- a/api/src/main/java/org/zalando/nakadi/client/java/model/EventTypeStatistics.java +++ b/api/src/main/java/org/zalando/nakadi/client/java/model/EventTypeStatistics.java @@ -1,21 +1,28 @@ package org.zalando.nakadi.client.java.model; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + public class EventTypeStatistics { - private final Integer expectedWriteRate; + private final Integer messagesPerMinute; private final Integer messageSize; private final Integer readParallelism; private final Integer writeParallelism; - - public EventTypeStatistics(Integer expectedWriteRate, Integer messageSize, Integer readParallelism, Integer writeParallelism) { - this.expectedWriteRate = expectedWriteRate; + @JsonCreator + public EventTypeStatistics( + @JsonProperty("messages_per_minute") Integer messagesPerMinute, + @JsonProperty("message_size") Integer messageSize, + @JsonProperty("read_parallelism") Integer readParallelism, + @JsonProperty("write_parallelism") Integer writeParallelism) { + this.messagesPerMinute = messagesPerMinute; this.messageSize = messageSize; this.readParallelism = readParallelism; this.writeParallelism = writeParallelism; } - public Integer getExpectedWriteRate() { - return expectedWriteRate; + public Integer getMessagesPerMinute() { + return messagesPerMinute; } public Integer getMessageSize() { return messageSize; diff --git a/api/src/main/java/org/zalando/nakadi/client/java/model/Metrics.java b/api/src/main/java/org/zalando/nakadi/client/java/model/Metrics.java index bb1fb1c..33ce946 100644 --- a/api/src/main/java/org/zalando/nakadi/client/java/model/Metrics.java +++ b/api/src/main/java/org/zalando/nakadi/client/java/model/Metrics.java @@ -2,16 +2,19 @@ import java.util.Map; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + public class Metrics { private final Map metrics; - public Metrics(Map metrics) { + @JsonCreator + public Metrics(@JsonProperty Map metrics) { this.metrics = metrics; } public Map getMetrics() { return metrics; } - } diff --git a/api/src/main/java/org/zalando/nakadi/client/java/model/Partition.java b/api/src/main/java/org/zalando/nakadi/client/java/model/Partition.java index 1593d8e..9813b09 100644 --- a/api/src/main/java/org/zalando/nakadi/client/java/model/Partition.java +++ b/api/src/main/java/org/zalando/nakadi/client/java/model/Partition.java @@ -1,6 +1,7 @@ package org.zalando.nakadi.client.java.model; import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; /** * Partition information. Can be helpful when trying to start a stream using an @@ -33,7 +34,10 @@ public class Partition { * oldest available event in the partition. */ @JsonCreator - public Partition(String partition, String oldestAvailableOffset, String newestAvailableOffset) { + public Partition( + @JsonProperty("partition") String partition, + @JsonProperty("oldest_available_offset")String oldestAvailableOffset, + @JsonProperty("newest_available_offset")String newestAvailableOffset) { this.partition = partition; this.oldestAvailableOffset = oldestAvailableOffset; this.newestAvailableOffset = newestAvailableOffset; diff --git a/api/src/main/java/org/zalando/nakadi/client/java/model/Problem.java b/api/src/main/java/org/zalando/nakadi/client/java/model/Problem.java index 764fc12..2fc5be6 100644 --- a/api/src/main/java/org/zalando/nakadi/client/java/model/Problem.java +++ b/api/src/main/java/org/zalando/nakadi/client/java/model/Problem.java @@ -1,28 +1,37 @@ package org.zalando.nakadi.client.java.model; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + public class Problem { - private final String problemType; + private final String type; private final String title; private final Integer status; private final String detail; private final String instance; /** - * @ param problemType An absolute URI that identifies the problem type. When dereferenced, it SHOULD provide human-readable API documentation for the problem type (e.g., using HTML). This Problem object is the same as provided by https://github.com/zalando/problem + * @ param type An absolute URI that identifies the problem type. When dereferenced, it SHOULD provide human-readable API documentation for the problem type (e.g., using HTML). This Problem object is the same as provided by https://github.com/zalando/problem * @ param title A short, summary of the problem type. Written in English and readable for engineers (usually not suited for non technical stakeholders and not localized) * @ param status The HTTP status code generated by the origin server for this occurrence of the problem. * @ param detail A human readable explanation specific to this occurrence of the problem. * @ param instance An absolute URI that identifies the specific occurrence of the problem. It may or may not yield further information if dereferenced. */ - public Problem(String problemType, String title, Integer status, String detail, String instance) { - this.problemType = problemType; + @JsonCreator + public Problem( + @JsonProperty("type") String type, + @JsonProperty("title") String title, + @JsonProperty("status") Integer status, + @JsonProperty("detail") String detail, + @JsonProperty("instance") String instance) { + this.type = type; this.title = title; this.status = status; this.detail = detail; this.instance = instance; } - public String getProblemType() { - return problemType; + public String getType() { + return type; } public String getTitle() { return title; diff --git a/api/src/main/scala/org/zalando/nakadi/client/scala/model/Model.scala b/api/src/main/scala/org/zalando/nakadi/client/scala/model/Model.scala index f37b161..41759c8 100644 --- a/api/src/main/scala/org/zalando/nakadi/client/scala/model/Model.scala +++ b/api/src/main/scala/org/zalando/nakadi/client/scala/model/Model.scala @@ -5,12 +5,9 @@ import scala.collection.JavaConversions._ import com.fasterxml.jackson.module.scala.JsonScalaEnumeration import com.fasterxml.jackson.core.`type`.TypeReference -// Updated untill commit 7839be3 -// Compare +// Updated untill commit 57cace - -// Updated untill commit 7839be3 -// Compare + /** * The Event definition will be externalized in future versions of this document. @@ -50,7 +47,7 @@ case class Cursor( */ case class EventMetadata( eid: String, - eventType: Option[EventType], + @JsonProperty("event_type") eventTypeName: Option[String], occurredAt: String, receivedAt: Option[String], parentEids: Seq[String], @@ -85,6 +82,7 @@ trait DataChangeEventQualifier { case class DataChangeEvent[T]( data: T, dataType: String, + @JsonProperty("data_op") @JsonScalaEnumeration(classOf[DataOperationType]) dataOperation: DataOperation.Value, metadata: Option[EventMetadata]) extends DataChangeEventQualifier with Event @@ -96,6 +94,7 @@ case class DataChangeEvent[T]( * @ param instance An absolute URI that identifies the specific occurrence of the problem. It may or may not yield further information if dereferenced. */ case class Problem( + @JsonProperty("type") problemType: String, title: String, status: Integer, @@ -135,7 +134,7 @@ case class EventStreamBatch[T <: Event]( * @param partitionStrategy Determines how the assignment of the event to a Partition should be handled. * @param schema The schema for this EventType. This is expected to be a json schema in yaml format (other formats might be added in the future). * @param dataKeyFields Indicators of the path of the properties that constitute the primary key (identifier) of the data within this Event. If set MUST be a valid required field as defined in the schema. (TBD should be required? Is applicable only to both Business and DataChange Events?) - * @param partitioningKeyFields Indicator of the field used for guaranteeing the ordering of Events of this type (used by the PartitionResolutionStrategy). If set MUST be a valid required field as defined in the schema. + * @param partitionKeyFields Indicator of the field used for guaranteeing the ordering of Events of this type (used by the PartitionResolutionStrategy). If set MUST be a valid required field as defined in the schema. * @param statistics Statistics of this EventType used for optimization purposes. Internal use of these values might change over time. (TBD: measured statistics). * */ @@ -143,58 +142,54 @@ case class EventType( name: String, owningApplication: String, @JsonScalaEnumeration(classOf[EventTypeCategoryType]) category: EventTypeCategory.Value, - @JsonScalaEnumeration(classOf[EventValidationStrategyType]) validationStrategies: Option[Seq[EventValidationStrategy.Value]], + @JsonScalaEnumeration(classOf[EventValidationStrategyType]) validationStrategies: Seq[EventValidationStrategy.Value], @JsonScalaEnumeration(classOf[EventEnrichmentStrategyType]) enrichmentStrategies: Seq[EventEnrichmentStrategy.Value], @JsonScalaEnumeration(classOf[PartitionStrategyType]) partitionStrategy: Option[PartitionStrategy.Value], - schema: Option[EventTypeSchema], - dataKeyFields: Option[Seq[String]], - partitionKeyFields: Option[Seq[String]], - statistics: Option[EventTypeStatistics]) + schema: EventTypeSchema, + dataKeyFields: Seq[String], + partitionKeyFields: Seq[String], + @JsonProperty("default_statistics")statistics: Option[EventTypeStatistics]) /** * The schema for an EventType, expected to be a json schema in yaml * format (other formats might be added in the future). - * @param type The type of schema definition (avro, json-schema, etc). + * @param schemaType The type of schema definition (avro, json-schema, etc). * @param schema The schema as string in the syntax defined in the field type. * Failure to respect the syntax will fail any operation on an EventType. */ case class EventTypeSchema( - @JsonProperty("type")@JsonScalaEnumeration(classOf[SchemaTypeType]) schemaType: SchemaType.Value, //Name is type (keyword in scala) + @JsonProperty("type")@JsonScalaEnumeration(classOf[SchemaTypeType]) schemaType: SchemaType.Value, schema: String) /** * Operational statistics for an EventType. This data is generated by Nakadi * based on the runtime and might be used to guide changes in internal parameters. - * @param expectedWriteRate - Write rate for events of this EventType. This rate encompasses all producers of this EventType for a Nakadi cluster. Measured in kb/minutes. + * @param messagesPerMinute - Write rate for events of this EventType. This rate encompasses all producers of this EventType for a Nakadi cluster. Measured in kb/minutes. * @param messageSize - Average message size for each Event of this EventType. Includes in the count the whole serialized form of the event, including metadata. Measured in bytes. * @param readParallelism - Amount of parallel readers (consumers) to this EventType. * @param writeParallelism - Amount of parallel writers (producers) to this EventType. * */ case class EventTypeStatistics( - expectedWriteRate: Option[Int], - messageSize: Option[Int], - readParallelism: Option[Int], - writeParallelism: Option[Int]) + messagesPerMinute: Integer, + messageSize: Integer, + readParallelism: Integer, + writeParallelism: Integer) /** * Defines a rule for validation of an incoming Event. Rules might require additional parameters; see the `doc` field of the existing rules for details. See GET /registry/validation-strategies for a list of available rules. - * @param name Name of the strategy. - * @param doc Documentation for the validation. */ case object EventValidationStrategy extends Enumeration { - type EventValidationStrategyxtends = Value - val NONE = Value("None") + type EventValidationStrategy = Value + val SCHEMA_VALIDATION = Value("schema-validation") } class EventValidationStrategyType extends TypeReference[EventValidationStrategy.type] /** * Defines a rule for the resolution of incoming Events into partitions. Rules might require additional parameters; see the `doc` field of the existing rules for details. See `GET /registry/partition-strategies` for a list of available rules. - * @param name Name of the strategy. - * @param doc Documentation for the partition resolution. */ case object PartitionStrategy extends Enumeration { - type PartitionResolutionStrategy = Value + type PartitionStrategy = Value val HASH = Value("hash") val USER_DEFINED = Value("user_defined") val RANDOM = Value("random") @@ -203,8 +198,6 @@ class PartitionStrategyType extends TypeReference[PartitionStrategy.type] /** * Defines a rule for transformation of an incoming Event. No existing fields might be modified. In practice this is used to set automatically values in the Event upon reception (i.e. set a reception timestamp on the Event). Rules might require additional parameters; see the `doc` field of the existing rules for details. See GET /registry/enrichment-strategies for a list of available rules. - * @param name Name of the strategy. - * @param doc Documentation for the enrichment. */ case object EventEnrichmentStrategy extends Enumeration { type EventEnrichmentStrategy = Value @@ -216,7 +209,7 @@ class EventEnrichmentStrategyType extends TypeReference[EventEnrichmentStrategy. * A status corresponding to one individual Event's publishing attempt. * @param eid Eid of the corresponding item. Will be absent if missing on the incoming Event. * @param publishingStatus Indicator of the submission of the Event within a Batch. - SUBMITTED indicates successful submission, including commit on he underlying broker. - FAILED indicates the message submission was not possible and can be resubmitted if so desired. - ABORTED indicates that the submission of this item was not attempted any further due to a failure on another item in the batch. - * @param step Indicator of the step in the pulbishing process this Event reached. In Items that FAILED means the step of the failure. - NONE indicates that nothing was yet attempted for the publishing of this Event. Should be present only in the case of aborting the publishing during the validation of another (previous) Event. - VALIDATING, ENRICHING, PARTITIONING and PUBLISHING indicate all the corresponding steps of the publishing process. + * @param step Indicator of the step in the publishing process this Event reached. In Items that FAILED means the step of the failure. - NONE indicates that nothing was yet attempted for the publishing of this Event. Should be present only in the case of aborting the publishing during the validation of another (previous) Event. - VALIDATING, ENRICHING, PARTITIONING and PUBLISHING indicate all the corresponding steps of the publishing process. * @param detail Human readable information about the failure on this item. Items that are not SUBMITTED should have a description. * */ @@ -271,9 +264,9 @@ class EventTypeCategoryType extends TypeReference[EventTypeCategory.type] */ case object BatchItemPublishingStatus extends Enumeration { type BatchItemPublishingStatus = Value - val SUBMITTED = Value("SUBMITTED") - val FAILED = Value("FAILED") - val ABORTED = Value("ABORTED") + val SUBMITTED = Value("submitted") + val FAILED = Value("failed") + val ABORTED = Value("aborted") } class BatchItemPublishingStatusType extends TypeReference[BatchItemPublishingStatus.type] @@ -285,15 +278,15 @@ class BatchItemPublishingStatusType extends TypeReference[BatchItemPublishingSta * only in the case of aborting the publishing during the validation of another (previous) Event.
      * - VALIDATING, ENRICHING, PARTITIONING and PUBLISHING indicate all the corresponding steps of the * publishing process.

      - * Values = NONE("NONE"), VALIDATING("VALIDATING"), ENRICHING("ENRICHING"), PARTITIONING("PARTITIONING"), PUBLISHING("PUBLISHING") + * Values = NONE("none"), VALIDATING("validating"), ENRICHING("enriching"), PARTITIONING("partitioning"), PUBLISHING("publishing") */ case object BatchItemStep extends Enumeration { type BatchItemStep = Value - val NONE = Value("NONE") - val VALIDATING = Value("VALIDATING") - val ENRICHING = Value("ENRICHING") - val PARTITIONING = Value("PARTITIONING") - val PUBLISHING = Value("PUBLISHING") + val NONE = Value("none") + val VALIDATING = Value("validating") + val ENRICHING = Value("enriching") + val PARTITIONING = Value("partitioning") + val PUBLISHING = Value("publishing") } class BatchItemStepType extends TypeReference[BatchItemStep.type] diff --git a/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala index 47d71ea..9eacd64 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala @@ -159,14 +159,15 @@ class ClientImpl(connection: Connection, subscriber: SubscriptionHandler, charSe Future.successful(Left(ClientError(msg, Some(status.intValue())))) } case HttpResponse(StatusCodes.NotFound, headers, entity, protocol) => - Future.successful(Right(None)) + val msg = "Not found!" + Future.successful(Left(ClientError(msg, Some(StatusCodes.NotFound.intValue)))) case HttpResponse(status, headers, entity, protocol) if (status.isRedirection()) => val msg = "Not implemented: http-status (" + status.intValue() + "}) and reason:" + status.reason() logger.info(msg) Future.successful(Left(ClientError(msg, Some(status.intValue())))) - case HttpResponse(status, headers, entity, protocol) if (status.isFailure()) => + case HttpResponse(status, headers, entity, protocol) => Unmarshal(entity).to[String].map { body => - val msg = "An error occurred, http-status: %s (%s) Message: %s".format(status.intValue(), status.reason(), body) + val msg = "Service return http-status: %s (%s) Message: %s".format(status.intValue(), status.reason(), body) logger.warn(msg) Left(ClientError(msg, Some(status.intValue()))) } @@ -176,18 +177,22 @@ class ClientImpl(connection: Connection, subscriber: SubscriptionHandler, charSe private[client] def mapToOption[T](response: HttpResponse): Future[Option[ClientError]] = { response.status match { case status if (status.isSuccess()) => - logger.debug("Success. http-status: %s".format(status.intValue())) + logger.info(s"Success. http-status: ${status.intValue()}") + response.entity.toStrict(10.second).map { body => + logger.debug("Success - http-status: %s, body:[%s]".format(status.intValue().toString(), body.data.decodeString(charSet))) + } Future.successful(None) case status if (status.isRedirection()) => - val msg = "Redirection - http-status: %s, reason[%s]".format(status.intValue().toString(), status.reason()) + val msg = s"Redirection - http-status: ${status.intValue()}, reason[${status.reason()}]" logger.info(msg) response.entity.toStrict(10.second).map { body => - logger.debug("Redirection - http-status: %s, reason[%s], body:[%s]".format(status.intValue().toString(), status.reason(), body.data.decodeString(charSet))) + logger.debug(s"Redirection - http-status: ${status.intValue().toString()}, reason[${status.reason()}], body:[${body.data.decodeString(charSet)}]") } Future.successful(Option(ClientError(msg, Some(status.intValue())))) case status if (status.isFailure()) => + logger.warn(s"Failure. http-status: ${status.intValue()}") response.entity.toStrict(10.second).map { body => - val msg = "Failure - http-status: %s, reason[%s], body:[%s]".format(status.intValue().toString(), status.reason(), body.data.decodeString(charSet)) + val msg = s"Failure - http-status: ${status.intValue()}, reason[${status.reason()}], body:[${body.data.decodeString(charSet)}]" logger.warn(msg) Option(ClientError(msg, Some(status.intValue()))) } diff --git a/client/src/test/scala/org/zalando/nakadi/client/utils/TestScalaEntity.scala b/client/src/test/scala/org/zalando/nakadi/client/utils/TestScalaEntity.scala index 3f6d0c0..7d8a677 100644 --- a/client/src/test/scala/org/zalando/nakadi/client/utils/TestScalaEntity.scala +++ b/client/src/test/scala/org/zalando/nakadi/client/utils/TestScalaEntity.scala @@ -10,14 +10,18 @@ object TestScalaEntity { val partition = new Partition("0", "132", "4423") val cursor = new Cursor("0", "120") val eventTypeSchema = new EventTypeSchema(SchemaType.JSON, "schema") - val eventValidationStrategy = EventValidationStrategy.NONE + val eventValidationStrategy = EventValidationStrategy.SCHEMA_VALIDATION val partitionResolutionStrategy = PartitionStrategy.HASH val eventEnrichmentStrategy = EventEnrichmentStrategy.METADATA //Complex objects - val eventTypeStatistics = new EventTypeStatistics(Option(9281002), Option(19283), Option(21), Option(312)) - val eventType = new EventType("name", "owner", EventTypeCategory.BUSINESS, Option(List(EventValidationStrategy.NONE)), List(EventEnrichmentStrategy.METADATA), Some(partitionResolutionStrategy), Option(eventTypeSchema), Option(List("dataKeyFields")), Option(List("partitioningKeyFields")), Option(eventTypeStatistics)) - val eventMetadata = new EventMetadata("eid", Option(eventType), "occurredAt", Option("receivedAt"), List("parentEids"), Option("flowId"), Option("partition")) + val eventTypeStatistics = new EventTypeStatistics(9281002, 19283, 21, 312) + val eventType = new EventType("name", "owner", + EventTypeCategory.BUSINESS, List(EventValidationStrategy.SCHEMA_VALIDATION) + , List(EventEnrichmentStrategy.METADATA), Some(partitionResolutionStrategy), + eventTypeSchema, + List("dataKeyFields"), List("partitioningKeyFields"), Option(eventTypeStatistics)) + val eventMetadata = new EventMetadata("eid", Option(eventType.name), "occurredAt", Option("receivedAt"), List("parentEids"), Option("flowId"), Option("partition")) case class MyEvent(name: String, metadata: Option[EventMetadata]) extends Event val myEvent = new MyEvent("test", Some(eventMetadata)) val eventStreamBatch = new EventStreamBatch[MyEvent](cursor, Option(List(myEvent))) diff --git a/client/src/test/scala/org/zalando/nakadi/client/scala/TestFactory.scala b/it/src/main/scala/org/zalando/nakadi/client/scala/TestFactory.scala similarity index 53% rename from client/src/test/scala/org/zalando/nakadi/client/scala/TestFactory.scala rename to it/src/main/scala/org/zalando/nakadi/client/scala/TestFactory.scala index 714f27e..bd11d7e 100644 --- a/client/src/test/scala/org/zalando/nakadi/client/scala/TestFactory.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/scala/TestFactory.scala @@ -1,34 +1,40 @@ package org.zalando.nakadi.client.scala +import java.util.Calendar + import scala.concurrent.Await import scala.concurrent.Future import scala.concurrent.duration.DurationInt import scala.util.Random + import org.zalando.nakadi.client.scala.model._ -import org.zalando.nakadi.client.scala.model.JacksonJsonMarshaller -import org.zalando.nakadi.client.Serializer -import org.zalando.nakadi.client.scala.model.JacksonJsonMarshaller -import org.zalando.nakadi.client.Deserializer -import org.zalando.nakadi.client.Deserializer -import org.zalando.nakadi.client.Serializer -import java.util.Calendar +import com.fasterxml.jackson.core.`type`.TypeReference +import org.zalando.nakadi.client.scala.model._ + -case class EventActions(client: Client) { - def create[T <: Event](name: String, event: Seq[T]) = { - client.publishEvents[T](name, event) +sealed trait TestUtil { + + def executeCall[T](call: => Future[T]): T = { + Await.result(call, 10.second) } } -case class EventTypesActions(client: Client) { +case class EventActions(client: Client)extends TestUtil { + def publish[T <: Event](name: String, event: Seq[T]) = { + executeCall(client.publishEvents[T](name, event)) + } +} + +case class EventTypesActions(client: Client)extends TestUtil { import JacksonJsonMarshaller._ def create(event: EventType) = { executeCall(client.createEventType(event)) } - def update(event: EventType) = { + def update(event: EventType) = { executeCall(client.updateEventType(event.name, event)) } - def get(name: String) = { + def get(name: String) = { executeCall(client.getEventType(name)) } def getAll() = { @@ -38,24 +44,26 @@ case class EventTypesActions(client: Client) { executeCall(client.deleteEventType(name)) } - private def executeCall[T](call: => Future[T]): T = { - Await.result(call, 10.second) - } + } + trait ModelFactory { val x = Random.alphanumeric + case class MyEventExample(orderNumber: String) extends Event + implicit def myEventExampleTR: TypeReference[EventStreamBatch[MyEventExample]] = new TypeReference[EventStreamBatch[MyEventExample]] {} + //EventType def paritionKeyFields() = List("order_number") - def schemaDefinition() = """{ "properties": { "order_number": { "type": "string" } } }""" def eventTypeSchema() = new EventTypeSchema(SchemaType.JSON, schemaDefinition) + def schemaDefinition() = """{ "properties": { "order_number": { "type": "string" } } }""" - def createUniqueEventType(): EventType = { - //Unique events mean unique name - new EventType("test-client-integration-event-" + Random.nextInt() + "-" + Random.nextInt() + Random.nextInt(), // - "laas-team", // - EventTypeCategory.UNDEFINED, None, Nil, // - Some(PartitionStrategy.RANDOM), Option(eventTypeSchema), // - None, Option(paritionKeyFields), None) + def createEventType(name: String, + owningApplication: String = "nakadi-klients"): EventType = { + new EventType(name, // + owningApplication, // + EventTypeCategory.UNDEFINED, Nil, Nil, // + Some(PartitionStrategy.RANDOM), eventTypeSchema, // + Nil, paritionKeyFields, None) } def createEventMetadata(): EventMetadata = { val length = 5 diff --git a/it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/ClientActions.scala b/it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/ClientActions.scala new file mode 100644 index 0000000..6aa23c9 --- /dev/null +++ b/it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/ClientActions.scala @@ -0,0 +1,36 @@ +package org.zalando.nakadi.client.scala.test.factory + +import scala.concurrent.Await +import scala.concurrent.Future +import scala.concurrent.duration.DurationInt +import org.zalando.nakadi.client.scala.model._ +import org.zalando.nakadi.client.scala.model._ +import org.zalando.nakadi.client.scala.Client + +case class ClientActions(client: Client) { + def publish[T <: Event](name: String, event: Seq[T]) = { + executeCall(client.publishEvents[T](name, event)) + } + + import JacksonJsonMarshaller._ + def createEventType(event: EventType) = { + executeCall(client.createEventType(event)) + } + def updateEventType(event: EventType) = { + executeCall(client.updateEventType(event.name, event)) + } + def getEventType(name: String) = { + executeCall(client.getEventType(name)) + } + def getEventTypes() = { + executeCall(client.getEventTypes()) + } + def deleteEventType(name: String) = { + executeCall(client.deleteEventType(name)) + } + + private def executeCall[T](call: => Future[T]): T = { + Await.result(call, 10.second) + } + +} \ No newline at end of file diff --git a/it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/EventGenerator.scala b/it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/EventGenerator.scala new file mode 100644 index 0000000..bf44c5f --- /dev/null +++ b/it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/EventGenerator.scala @@ -0,0 +1,94 @@ +package org.zalando.nakadi.client.scala.test.factory + +import org.zalando.nakadi.client.scala.model.Event +import org.zalando.nakadi.client.scala.model.EventEnrichmentStrategy +import org.zalando.nakadi.client.scala.model.EventType +import org.zalando.nakadi.client.scala.model.EventTypeCategory +import org.zalando.nakadi.client.scala.model.EventValidationStrategy +import org.zalando.nakadi.client.scala.model._ +import org.zalando.nakadi.client.scala.model.EventTypeStatistics +import org.zalando.nakadi.client.scala.model.EventTypeSchema +import org.zalando.nakadi.client.scala.model.SchemaType + +trait EventGenerator { + /** + * Should generate an uniqueId that can be used for uniqueness of the Event, + * this function MUST HAVE side effects. + */ + def newId(): String + + /** + * Should generate an new Event. + * This function MUST HAVE side effects. + */ + def newEvent(): Event + + /** + * Returns the EventType. + */ + def eventType: EventType = new EventType(name, // + owner, // + category, // + validationStrategies, // + enrichmentStrategies, // + partitionStrategy, // + schemaType, // + dataKeyFields, // + partitionKeyFields, // + statistics) + + /** + * Returns the EventTypeName. + */ + + def name: String + /** + * Returns the schema definition of the Event. + */ + def schemaDefinition: String + + /** + * Returns the owningApplication value. Default = "Nakadi-klients(integration-test-suite)" + */ + def owner: String = "Nakadi-klients(integration-test-suite)" + + /** + * Returns the category value. Default = UNDEFINED + */ + def category: EventTypeCategory.Value = EventTypeCategory.UNDEFINED + + /** + * Returns the validationStrategies value. Default = Nil + */ + def validationStrategies: Seq[EventValidationStrategy.Value] = Nil + /** + * Returns the enrichmentStrategies value. Default = Nil + */ + def enrichmentStrategies: Seq[EventEnrichmentStrategy.Value] = Nil + + /** + * Returns the partitionStrategy value. Default = Random + */ + def partitionStrategy: Option[PartitionStrategy.Value] = Some(PartitionStrategy.RANDOM) + + /** + * Returns the eventSchemaType value. Default = new EventTypeSchema(SchemaType.JSON, schemaDefinition) + */ + def schemaType: EventTypeSchema = new EventTypeSchema(SchemaType.JSON, schemaDefinition) + + /** + * Returns the dataKeyFields value. Default = Nil + */ + def dataKeyFields: Seq[String] = Nil + + /** + * Returns the partitionKeyFields value. Default = Nil + */ + def partitionKeyFields: Seq[String] = Nil + + /** + * Returns the partitionKeyFields value. Default = None + */ + def statistics: Option[EventTypeStatistics] = None + +} \ No newline at end of file diff --git a/it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/EventIntegrationTest.scala b/it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/EventIntegrationTest.scala new file mode 100644 index 0000000..18a8136 --- /dev/null +++ b/it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/EventIntegrationTest.scala @@ -0,0 +1,45 @@ +package org.zalando.nakadi.client.scala.test.factory + +import scala.concurrent.duration.DurationInt + +import org.scalatest.Matchers +import org.scalatest.WordSpec +import org.zalando.nakadi.client.scala.Client +import org.zalando.nakadi.client.scala.Client +import org.zalando.nakadi.client.scala.ClientError +import org.zalando.nakadi.client.scala.model._ +import org.zalando.nakadi.client.scala.model._ +import org.zalando.nakadi.client.scala.model._ +import org.zalando.nakadi.client.scala.model.Event + +class EventIntegrationTest(generator: EventGenerator, client: Client) extends WordSpec with Matchers { + + val actions = ClientActions(client) + //fixed Variables that should not be generated twice + val eventType = generator.eventType + + def createEventType(): EventType = { + failIfClientError(actions.createEventType(eventType)) + eventType + } + + def deleteEventType() = failIfClientError(actions.deleteEventType(eventType.name)) + + def publishEvents(nrOfEvents: Int): Seq[Event] = { + val events = for { + a <- 0 to nrOfEvents + } yield generator.newEvent() + failIfClientError(actions.publish(eventType.name, events)) + events + } + + //Private methods + private def failIfClientError(in: Option[ClientError]) = in match { + case Some(clientError) => + fail("Failed with clientError " + clientError) + case _ => + } + +} + + \ No newline at end of file diff --git a/it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/events/MySimpleEvent.scala b/it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/events/MySimpleEvent.scala new file mode 100644 index 0000000..6d6c49a --- /dev/null +++ b/it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/events/MySimpleEvent.scala @@ -0,0 +1,14 @@ +package org.zalando.nakadi.client.scala.test.factory.events + +import org.zalando.nakadi.client.scala.model._ +import com.fasterxml.jackson.core.`type`.TypeReference +import org.zalando.nakadi.client.scala.model.EventStreamBatch + +object MySimpleEvent { + def schemaDefinition() = """{ "properties": { "order_number": { "type": "string" } } }""" + implicit def myEventExampleTR: TypeReference[EventStreamBatch[MySimpleEvent]] = new TypeReference[EventStreamBatch[MySimpleEvent]] {} +} + +case class MySimpleEvent(orderNumber: String) extends Event + + \ No newline at end of file diff --git a/it/src/test/resources/reference.conf b/it/src/test/resources/reference.conf new file mode 100644 index 0000000..0faa71a --- /dev/null +++ b/it/src/test/resources/reference.conf @@ -0,0 +1,21 @@ +akka { + loglevel = "INFO" + stdout-loglevel = "INFO" + + actor { + debug { + # enable DEBUG logging of actor lifecycle changes + lifecycle = on + unhandled = on + } + } + +} + + +# Enable metrics extension in akka-cluster-metrics. +#akka.extensions=["akka.cluster.metrics.ClusterMetricsExtension"] + +# Sigar native library extract location during tests. +# Note: use per-jvm-instance folder when running multiple jvm on one host. +#akka.cluster.metrics.native-library-extract-folder=${user.dir}/target/native diff --git a/it/src/test/scala/org/zalando/nakadi/client/ClientSubscriptionTest.scala b/it/src/test/scala/org/zalando/nakadi/client/ClientSubscriptionTest.scala index 27a1325..46ef72a 100644 --- a/it/src/test/scala/org/zalando/nakadi/client/ClientSubscriptionTest.scala +++ b/it/src/test/scala/org/zalando/nakadi/client/ClientSubscriptionTest.scala @@ -14,8 +14,6 @@ import org.zalando.nakadi.client.scala.ClientError class ClientSubscriptionTest extends WordSpec with Matchers with ModelFactory { import ClientFactory._ import JacksonJsonMarshaller._ - case class MyEventExample(orderNumber: String) extends Event - implicit def myEventExampleTR: TypeReference[EventStreamBatch[MyEventExample]] = new TypeReference[EventStreamBatch[MyEventExample]] {} val eventAction = new EventActions(client) val eventTypeAction = new EventTypesActions(client) "POST/PUT/GET/DELETE single EventType " in { @@ -50,7 +48,7 @@ class ClientSubscriptionTest extends WordSpec with Matchers with ModelFactory { a <- 0 to 4005 } yield MyEventExample("order-" + a) // eventAction.create("test-client-integration-event-1936085527-148383828851369665", List(MyEventExample("test-1"))) - eventAction.create("test-client-integration-event-1936085527-148383828851369665", events) + eventAction.publish("test-client-integration-event-1936085527-148383828851369665", events) } } diff --git a/it/src/test/scala/org/zalando/nakadi/client/EventTypesIntegrationTest.scala b/it/src/test/scala/org/zalando/nakadi/client/EventTypesIntegrationTest.scala index 59e72cf..13b24d2 100644 --- a/it/src/test/scala/org/zalando/nakadi/client/EventTypesIntegrationTest.scala +++ b/it/src/test/scala/org/zalando/nakadi/client/EventTypesIntegrationTest.scala @@ -14,9 +14,9 @@ import JacksonJsonMarshaller._ val eventAction = new EventActions(client) val eventTypeAction = new EventTypesActions(client) "POST/PUT/GET/DELETE single EventType " in { - + val eventTypeName="test-client-integration-event-1936085527-148383828851369665" //Create event - val eventType = createUniqueEventType() + val eventType = createEventType(eventTypeName) val creationResult = eventTypeAction.create(eventType) creationResult.isDefined shouldBe false @@ -29,7 +29,7 @@ import JacksonJsonMarshaller._ a <- 0 to 12 } yield MyEventExample("order-"+a) // eventAction.create("test-client-integration-event-1936085527-148383828851369665", List(MyEventExample("test-1"))) - eventAction.create("test-client-integration-event-1936085527-148383828851369665", events) + eventAction.publish("test-client-integration-event-1936085527-148383828851369665", events) //TODO: Enable this when PUT is supported. // Update the event @@ -49,10 +49,11 @@ import JacksonJsonMarshaller._ } "POST/GET/DELETE multiple EventTypes " in { - +val eventTypeName1="test-client-integration-event-1936085527-1" +val eventTypeName2="test-client-integration-event-1936085527-2" //Create 2 EventTypes - val eventType1 = createUniqueEventType() - val eventType2 = createUniqueEventType() + val eventType1 = createEventType(eventTypeName1) + val eventType2 = createEventType(eventTypeName2) eventTypeAction.create(eventType1) checkEventTypeExists(eventType1) @@ -84,8 +85,9 @@ import JacksonJsonMarshaller._ //TODO: Enable when implemented "UpdateEventTypes" in { + val eventTypeName1="test-client-integration-event-1936085527-3" //Create 2 EventTypes - val eventType = createUniqueEventType() + val eventType = createEventType(eventTypeName1) eventTypeAction.create(eventType) checkEventTypeExists(eventType) diff --git a/it/src/test/scala/org/zalando/nakadi/client/example2/ClientExample.scala b/it/src/test/scala/org/zalando/nakadi/client/example2/ClientExample.scala deleted file mode 100644 index 3e5fbb1..0000000 --- a/it/src/test/scala/org/zalando/nakadi/client/example2/ClientExample.scala +++ /dev/null @@ -1,52 +0,0 @@ -package org.zalando.nakadi.client.example2 - -import scala.concurrent.ExecutionContext.Implicits.global -import akka.stream.scaladsl._ -import akka.stream.scaladsl.Keep._ -import scala.concurrent.Future -import akka.actor.ActorSystem -import akka.stream.ActorMaterializer -import akka.http.scaladsl.Http -import org.zalando.nakadi.client.scala.Connection -import java.net.InetSocketAddress -import scala.concurrent.Future -import akka.stream.scaladsl.Tcp -import akka.actor.ActorSystem -import akka.stream.scaladsl.Tcp.ServerBinding -import akka.stream.scaladsl.Tcp.IncomingConnection -import akka.util.ByteString - -object Client extends App { - private implicit val actorSystem = ActorSystem("Nakadi-Client-Connections") - private implicit val http = Http(actorSystem) - implicit val materializer = ActorMaterializer() - val server = new Server() - val client = new Client() - client.connect() - -} - -class Client(implicit system: ActorSystem, m: ActorMaterializer) { - val connection = Tcp().outgoingConnection("127.0.0.1", 8888) - - def connect() = { - - val replParser = - Flow[String].takeWhile(_ != "q") - .concat(Source.single("BYE")) - .map(elem => ByteString(s"$elem\n")) - - val repl = Flow[ByteString] - .via(Framing.delimiter( - ByteString("\n"), - maximumFrameLength = 256, - allowTruncation = true)) - .map(_.utf8String) - .map(text => println("Server: " + text)) - .map(_ => scala.io.StdIn.readLine("> ")) - .via(replParser) - println("Connecting client") - connection.join(repl).run() - } -} - diff --git a/it/src/test/scala/org/zalando/nakadi/client/example2/ServerExample.scala b/it/src/test/scala/org/zalando/nakadi/client/example2/ServerExample.scala deleted file mode 100644 index d5e522f..0000000 --- a/it/src/test/scala/org/zalando/nakadi/client/example2/ServerExample.scala +++ /dev/null @@ -1,53 +0,0 @@ -package org.zalando.nakadi.client.example2 - -import scala.concurrent.ExecutionContext.Implicits.global -import akka.stream.scaladsl._ -import akka.stream.scaladsl.Keep._ -import scala.concurrent.Future -import akka.actor.ActorSystem -import akka.stream.ActorMaterializer -import akka.http.scaladsl.Http -import org.zalando.nakadi.client.scala.Connection -import java.net.InetSocketAddress -import scala.concurrent.Future -import akka.stream.scaladsl.Tcp -import akka.actor.ActorSystem -import akka.stream.scaladsl.Tcp.ServerBinding -import akka.stream.scaladsl.Tcp.IncomingConnection -import akka.util.ByteString - -object Server extends App { - private implicit val actorSystem = ActorSystem("Nakadi-Client-Connections") - private implicit val http = Http(actorSystem) - implicit val materializer = ActorMaterializer() - val server = new Server() - val client = new Client() - server.start() - -} - -class Server(implicit actorSystem: ActorSystem, m: ActorMaterializer) { - - def start() = { - val connections: Source[IncomingConnection, Future[ServerBinding]] = Tcp().bind("127.0.0.1", 8888) - - connections runForeach { connection => - println(s"New connection from: ${connection.remoteAddress}") - - val echo = Flow[ByteString] - .via(akka.stream.scaladsl.Framing.delimiter( - ByteString("\n"), - maximumFrameLength = 256, - allowTruncation = true)) - .map(_.utf8String) - .map(_ + "!!!\n") - .map { x => - println(s"$x") - ByteString(x) - } - - connection.handleWith(echo) - } - } - -} \ No newline at end of file diff --git a/it/src/test/scala/org/zalando/nakadi/client/logic/ReactiveStreamsSupport.scala b/it/src/test/scala/org/zalando/nakadi/client/logic/ReactiveStreamsSupport.scala deleted file mode 100644 index fb8832a..0000000 --- a/it/src/test/scala/org/zalando/nakadi/client/logic/ReactiveStreamsSupport.scala +++ /dev/null @@ -1,16 +0,0 @@ -package org.zalando.nakadi.client.logic - -import akka.actor.ActorSystem -import akka.util.Timeout -import scala.concurrent.duration._ -import akka.stream.ActorMaterializer - -trait ReactiveStreamsSupport extends Logging { - implicit def system: ActorSystem - - implicit val dispatcher = system.dispatcher - - implicit val timeout = Timeout(5.seconds) - - implicit val materializer = ActorMaterializer() -} diff --git a/it/src/test/scala/org/zalando/nakadi/client/logic/Receiver.scala b/it/src/test/scala/org/zalando/nakadi/client/logic/Receiver.scala deleted file mode 100644 index 82e035f..0000000 --- a/it/src/test/scala/org/zalando/nakadi/client/logic/Receiver.scala +++ /dev/null @@ -1,56 +0,0 @@ -package org.zalando.nakadi.client.logic -// -//import java.net.InetSocketAddress -//import akka.actor.ActorSystem -//import akka.stream.scaladsl.GraphDSL.Implicits._ -//import akka.stream.scaladsl._ -//import akka.util.ByteString -//import scala.concurrent.{Future, Promise} -//import akka.event.Logging -//import org.zalando.nakadi.client.Connection -//import org.zalando.nakadi.client.logic.ReactiveStreamsSupport -// -//class Receiver(connection: Connection)(implicit val system: ActorSystem) extends ReactiveStreamsSupport { -// def run(): Future[Unit] = { -// val completionPromise = Promise[Unit]() -// -// val serverConnection = connection.connection() -// -// -// val receiverFlow = GraphDSL.create() { implicit d => -// val in = Source. UndefinedSource[ByteString] -// val out = UndefinedSink[ByteString] -// -// val split = Broadcast[ByteString] -// -// val mainFlow = Flow[ByteString] -// .mapConcat(reconcileFrames.apply) -// .map(MessageData.decodeFromString) -// .map { md => -// logger.debug(s"Receiver: received msg: $md") -// createFrame(md.id) -// } -// -// in ~> split -// -// split ~> mainFlow ~> out -// split ~> OnCompleteSink[ByteString] { t => completionPromise.complete(t); () } -// -// (in, out) -// } -// -// val materializedMap = serverConnection.handleWith(receiverFlow) -// val connectFuture = serverConnection.localAddress(materializedMap) -// -// connectFuture.onSuccess { case _ => logger.debug(s"Receiver: connected to broker") } -// connectFuture.onFailure { case e: Exception => logger.error("Receiver: failed to connect to broker", e) } -// -// completionPromise.future -// } -//} -// -//object SimpleReceiver extends App with SimpleServerSupport with Logging { -// val receiver: Receiver = new Receiver(receiveServerAddress) -// import receiver.system.dispatcher -// receiver.run().onComplete(result => logger.info("Receiver: completed with result " + result)) -//} \ No newline at end of file diff --git a/it/src/test/scala/org/zalando/nakadi/client/logic/ReceiverGraph.scala b/it/src/test/scala/org/zalando/nakadi/client/logic/ReceiverGraph.scala deleted file mode 100644 index f47c30d..0000000 --- a/it/src/test/scala/org/zalando/nakadi/client/logic/ReceiverGraph.scala +++ /dev/null @@ -1,56 +0,0 @@ -package org.zalando.nakadi.client.logic - -import scala.concurrent.Future - -import org.zalando.nakadi.client.scala.Connection - -import akka.actor.ActorSystem -import akka.http.scaladsl.Http -import akka.http.scaladsl.model.HttpHeader -import akka.http.scaladsl.model.HttpRequest -import akka.http.scaladsl.model.HttpResponse -import akka.stream.ActorMaterializer -import akka.stream.ClosedShape -import akka.stream.FlowShape -import akka.stream.Inlet -import akka.stream.Outlet -import akka.stream.scaladsl.Flow -import akka.stream.scaladsl.GraphDSL -import akka.stream.scaladsl.RunnableGraph -import akka.stream.scaladsl.Sink -import akka.stream.scaladsl.Source -import akka.util.ByteString - -class ReceiverGraph[T](eventName: String, connection: Connection, request: HttpRequest, headers: List[HttpHeader]) // -(implicit system: ActorSystem, m: ActorMaterializer) { - import GraphDSL.Implicits._ - - def listen() = { - - val g: RunnableGraph[_] = RunnableGraph.fromGraph(GraphDSL.create() { implicit builder => - - val flow: Flow[HttpRequest, HttpResponse, Future[Http.OutgoingConnection]] = connection.requestFlow() - - // Source - val out: Outlet[HttpResponse] = builder.add(Source.single(request).via(flow)).out - val transform2ByteString: FlowShape[HttpResponse, ByteString] = builder.add(null) - val transform2String: FlowShape[ByteString, String] = builder.add(null) - val deserialize2Object: FlowShape[String, T] = builder.add(null) - val filterMessagesWithEvents: FlowShape[T, T] = builder.add(null) - val publishEvent2listener: Inlet[Any] = builder.add(Sink.ignore).in - - - // Graph - out ~> transform2ByteString ~> transform2String ~> - deserialize2Object ~> filterMessagesWithEvents ~> publishEvent2listener - - ClosedShape // - - }) - - g.run() - - } - - -} \ No newline at end of file diff --git a/it/src/test/scala/org/zalando/nakadi/client/logic/package.scala b/it/src/test/scala/org/zalando/nakadi/client/logic/package.scala deleted file mode 100644 index 60125dd..0000000 --- a/it/src/test/scala/org/zalando/nakadi/client/logic/package.scala +++ /dev/null @@ -1,7 +0,0 @@ -package org.zalando.nakadi.client - -import com.typesafe.scalalogging.StrictLogging -package object logic { - type Logging = StrictLogging - -} \ No newline at end of file diff --git a/it/src/test/scala/org/zalando/nakadi/client/scala/SimpleEventIntegrationTest.scala b/it/src/test/scala/org/zalando/nakadi/client/scala/SimpleEventIntegrationTest.scala new file mode 100644 index 0000000..0bfc1f5 --- /dev/null +++ b/it/src/test/scala/org/zalando/nakadi/client/scala/SimpleEventIntegrationTest.scala @@ -0,0 +1,132 @@ +package org.zalando.nakadi.client + +import org.scalatest.{ Matchers, WordSpec } +import org.zalando.nakadi.client.scala.model._ +import com.fasterxml.jackson.core.`type`.TypeReference +import org.zalando.nakadi.client.scala.ClientFactory +import org.zalando.nakadi.client.scala.EventTypesActions +import org.zalando.nakadi.client.scala.EventActions +import org.zalando.nakadi.client.scala.ModelFactory +import org.zalando.nakadi.client.scala.test.factory.EventGenerator +import scala.util.Random + +class EventTypeTest extends WordSpec with Matchers with ModelFactory { + + val generator = new EventGenerator { + def uniqueEventId: String = Random.alphanumeric + def newEvent(): Event = new MySimpleEvent(uniqueEventId) + def eventType: EventType = new EventType(name, // + owningApplication, // + EventTypeCategory.UNDEFINED, None, Nil, // + Some(PartitionStrategy.RANDOM), Option(eventTypeSchema), // + None, Option(paritionKeyFields), None) + } + + import ClientFactory._ + import JacksonJsonMarshaller._ + val eventAction = new EventActions(client) + val eventTypeAction = new EventTypesActions(client) + "POST/PUT/GET/DELETE single EventType " in { + val eventTypeName = "test-client-integration-event-1936085527-148383828851369665" + //Create event + val eventType = createEventType(eventTypeName) + val creationResult = eventTypeAction.create(eventType) + creationResult.isDefined shouldBe false + + //Check the created EventType + checkEventTypeExists(eventType) + + case class MyEventExample(orderNumber: String) extends Event + implicit def problemTR: TypeReference[MyEventExample] = new TypeReference[MyEventExample] {} + val events = for { + a <- 0 to 12 + } yield MyEventExample("order-" + a) + // eventAction.create("test-client-integration-event-1936085527-148383828851369665", List(MyEventExample("test-1"))) + eventAction.publish("test-client-integration-event-1936085527-148383828851369665", events) + + //TODO: Enable this when PUT is supported. + // Update the event + // val updatedEvent = eventType.copy(owningApplication = "laas-team-2") + // events.update(updatedEvent) + + //Check the EventType has bee updated + // checkEventTypeExists(updatedEvent) + // checkEventTypeDoesNotExist(eventType) + + //Delete the created Event + val deletedEvent = eventTypeAction.delete(eventType.name) + deletedEvent.isEmpty shouldBe true + + //Is it really deleted? + checkEventTypeDoesNotExist(eventType) + } + + "POST/GET/DELETE multiple EventTypes " in { + val eventTypeName1 = "test-client-integration-event-1936085527-1" + val eventTypeName2 = "test-client-integration-event-1936085527-2" + //Create 2 EventTypes + val eventType1 = createEventType(eventTypeName1) + val eventType2 = createEventType(eventTypeName2) + + eventTypeAction.create(eventType1) + checkEventTypeExists(eventType1) + + eventTypeAction.create(eventType2) + checkEventTypeExists(eventType2) + + //Get all EventTypes again + //TODO: Enable when Nakadi has no erranous eventType + // val Right(Some(allEvents)) = events.getAll() + // allEvents should contain(eventType1) + // allEvents should contain(eventType2) + + //Delete the 2 EventTypes + eventTypeAction.delete(eventType1.name) + eventTypeAction.delete(eventType2.name) + + //Check if the're really deleted + checkEventTypeDoesNotExist(eventType1) + checkEventTypeDoesNotExist(eventType2) + + //Get all should not contain the deleted events + val Right(Some(updatedEvents)) = eventTypeAction.getAll() + + updatedEvents shouldNot contain(eventType1) + updatedEvents shouldNot contain(eventType2) + + } + + //TODO: Enable when implemented + "UpdateEventTypes" in { + val eventTypeName1 = "test-client-integration-event-1936085527-3" + //Create 2 EventTypes + val eventType = createEventType(eventTypeName1) + + eventTypeAction.create(eventType) + checkEventTypeExists(eventType) + + //Update the event + val updatedEvent = eventType.copy(owningApplication = "laas-team-2") + eventTypeAction.update(updatedEvent) + + //Check the EventType has bee updated + // checkEventTypeExists(updatedEvent) + // checkEventTypeDoesNotExist(eventType) + + } + + def checkEventTypeDoesNotExist(eventType: EventType) = { + val requestedEvent = eventTypeAction.get(eventType.name) + println(requestedEvent) + requestedEvent.isRight shouldBe true + val Right(result) = requestedEvent + result shouldBe None + } + + def checkEventTypeExists(eventType: EventType) = { + val Right(Some(createdEvent)) = eventTypeAction.get(eventType.name) + createdEvent shouldBe eventType + } + +} + From 82d4dd94cfc57e3a61a8bc53541164c7e2de0fe3 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Thu, 9 Jun 2016 18:18:19 +0200 Subject: [PATCH 075/183] techjira:LAAS-60 Model is up to date + Scala Integration tests --- .../nakadi/client/scala/model/Model.scala | 2 +- .../model/ScalaJacksonJsonMarshaller.scala | 2 +- .../examples/java/EventListenerExample.java | 6 +- .../examples/scala/EventCreationExample.scala | 11 +- .../scala/test/factory/ClientActions.scala | 1 - .../scala/test/factory/EventGenerator.scala | 49 ++++--- ...est.scala => EventIntegrationHelper.scala} | 9 +- .../test/factory/events/MySimpleEvent.scala | 49 ++++++- .../scala/SimpleEventIntegrationTest.scala | 132 ------------------ .../nakadi/client/scala/SimpleEventTest.scala | 44 ++++++ 10 files changed, 141 insertions(+), 164 deletions(-) rename it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/{EventIntegrationTest.scala => EventIntegrationHelper.scala} (81%) delete mode 100644 it/src/test/scala/org/zalando/nakadi/client/scala/SimpleEventIntegrationTest.scala create mode 100644 it/src/test/scala/org/zalando/nakadi/client/scala/SimpleEventTest.scala diff --git a/api/src/main/scala/org/zalando/nakadi/client/scala/model/Model.scala b/api/src/main/scala/org/zalando/nakadi/client/scala/model/Model.scala index 41759c8..a6ccb70 100644 --- a/api/src/main/scala/org/zalando/nakadi/client/scala/model/Model.scala +++ b/api/src/main/scala/org/zalando/nakadi/client/scala/model/Model.scala @@ -5,7 +5,7 @@ import scala.collection.JavaConversions._ import com.fasterxml.jackson.module.scala.JsonScalaEnumeration import com.fasterxml.jackson.core.`type`.TypeReference -// Updated untill commit 57cace +// Updated untill commit 57cace5 diff --git a/client/src/main/scala/org/zalando/nakadi/client/scala/model/ScalaJacksonJsonMarshaller.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/model/ScalaJacksonJsonMarshaller.scala index ef2f346..498affc 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/scala/model/ScalaJacksonJsonMarshaller.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/scala/model/ScalaJacksonJsonMarshaller.scala @@ -68,7 +68,7 @@ object JacksonJsonMarshaller { .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) .setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE) - .setSerializationInclusion(JsonInclude.Include.NON_NULL) + .setSerializationInclusion(JsonInclude.Include.NON_EMPTY) .addHandler(new DeserializationProblemHandler() { override def handleUnknownProperty(ctxt: DeserializationContext, jp: JsonParser, deserializer: JsonDeserializer[_], diff --git a/it/src/main/java/org/zalando/nakadi/client/examples/java/EventListenerExample.java b/it/src/main/java/org/zalando/nakadi/client/examples/java/EventListenerExample.java index b9168d6..a414042 100644 --- a/it/src/main/java/org/zalando/nakadi/client/examples/java/EventListenerExample.java +++ b/it/src/main/java/org/zalando/nakadi/client/examples/java/EventListenerExample.java @@ -30,8 +30,10 @@ public static void main(String[] args) throws InterruptedException, */ Listener listener = new EventCounterListener("Java-Test"); - StreamParameters params = new StreamParameters(Optional.of(new Cursor( - "0", "BEGIN")), Optional.of(100),// batchLimit, + StreamParameters params = new StreamParameters( +// Optional.of(new Cursor("0", "BEGIN")), + Optional.empty(), + Optional.of(100),// batchLimit, Optional.empty(),// streamLimit, Optional.empty(),// batchFlushTimeout, Optional.empty(),// streamTimeout, diff --git a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala index 21a8dfb..d9a068f 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala @@ -49,9 +49,10 @@ object EventCreationExample extends App { val owner = "team-laas" val category = EventTypeCategory.UNDEFINED // We want just to pass data without through Nakadi, simple schema-validation is enough! - val validationStrategies = None // Validation strategies are not defined yet! + val validationStrategies = Nil // Validation strategies are not defined yet! val enrichmentStrategies = Nil val partitionStrategy = PartitionStrategy.RANDOM + val dataKeyFields = Nil def paritionKeyFields() = List("date", "topic") val eventType = new EventType(eventTypeName, @@ -60,9 +61,9 @@ object EventCreationExample extends App { validationStrategies, enrichmentStrategies, Some(partitionStrategy), - Some(eventTypeSchema), - None, - Option(paritionKeyFields()), + eventTypeSchema, + dataKeyFields, + paritionKeyFields(), None) //You need to import the default Serializer if you don't sepecify your own! @@ -71,7 +72,7 @@ object EventCreationExample extends App { client.createEventType(eventType) Thread.sleep(3000) // 4. Publish the EventType - +// System.exit(0) var counter = 0 for (n <- 1 to 5000) { val event = new MeetingsEvent("2016-04-28T13:28:15+00:00", "Hackaton") diff --git a/it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/ClientActions.scala b/it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/ClientActions.scala index 6aa23c9..0afd8e8 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/ClientActions.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/ClientActions.scala @@ -12,7 +12,6 @@ case class ClientActions(client: Client) { executeCall(client.publishEvents[T](name, event)) } - import JacksonJsonMarshaller._ def createEventType(event: EventType) = { executeCall(client.createEventType(event)) } diff --git a/it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/EventGenerator.scala b/it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/EventGenerator.scala index bf44c5f..26c5ec1 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/EventGenerator.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/EventGenerator.scala @@ -11,42 +11,57 @@ import org.zalando.nakadi.client.scala.model.EventTypeSchema import org.zalando.nakadi.client.scala.model.SchemaType trait EventGenerator { + + //################################################# + // ABSTRACT METHODS + //################################################## + + /** + * Should generate an eventTypeName once, + * this value should be cached(calculated only once) + * because it will be called often. + */ + def eventTypeId: String + /** * Should generate an uniqueId that can be used for uniqueness of the Event, - * this function MUST HAVE side effects. */ def newId(): String /** * Should generate an new Event. - * This function MUST HAVE side effects. */ def newEvent(): Event - /** - * Returns the EventType. - */ - def eventType: EventType = new EventType(name, // - owner, // - category, // - validationStrategies, // - enrichmentStrategies, // - partitionStrategy, // - schemaType, // - dataKeyFields, // - partitionKeyFields, // - statistics) - /** * Returns the EventTypeName. */ - def name: String + def eventTypeName: String /** * Returns the schema definition of the Event. */ def schemaDefinition: String + /** + * Returns the EventType. + */ + def eventType: EventType = + new EventType(eventTypeName, // + owner, // + category, // + validationStrategies, // + enrichmentStrategies, // + partitionStrategy, // + schemaType, // + dataKeyFields, // + partitionKeyFields, // + statistics) + + //#################################################################### + // METHODS WITH DEFAULTS + //#################################################################### + /** * Returns the owningApplication value. Default = "Nakadi-klients(integration-test-suite)" */ diff --git a/it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/EventIntegrationTest.scala b/it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/EventIntegrationHelper.scala similarity index 81% rename from it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/EventIntegrationTest.scala rename to it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/EventIntegrationHelper.scala index 18a8136..4f1ac1f 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/EventIntegrationTest.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/EventIntegrationHelper.scala @@ -11,11 +11,11 @@ import org.zalando.nakadi.client.scala.model._ import org.zalando.nakadi.client.scala.model._ import org.zalando.nakadi.client.scala.model._ import org.zalando.nakadi.client.scala.model.Event +import org.slf4j.LoggerFactory -class EventIntegrationTest(generator: EventGenerator, client: Client) extends WordSpec with Matchers { - +class EventIntegrationHelper(generator: EventGenerator, client: Client) extends WordSpec with Matchers { + private val log = LoggerFactory.getLogger(this.getClass) val actions = ClientActions(client) - //fixed Variables that should not be generated twice val eventType = generator.eventType def createEventType(): EventType = { @@ -27,9 +27,10 @@ class EventIntegrationTest(generator: EventGenerator, client: Client) extends Wo def publishEvents(nrOfEvents: Int): Seq[Event] = { val events = for { - a <- 0 to nrOfEvents + a <- 1 to nrOfEvents } yield generator.newEvent() failIfClientError(actions.publish(eventType.name, events)) + log.info(s"EVENTS published: $events") events } diff --git a/it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/events/MySimpleEvent.scala b/it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/events/MySimpleEvent.scala index 6d6c49a..3bd5126 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/events/MySimpleEvent.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/events/MySimpleEvent.scala @@ -3,12 +3,59 @@ package org.zalando.nakadi.client.scala.test.factory.events import org.zalando.nakadi.client.scala.model._ import com.fasterxml.jackson.core.`type`.TypeReference import org.zalando.nakadi.client.scala.model.EventStreamBatch +import org.zalando.nakadi.client.scala.test.factory.EventGenerator +import scala.util.Random +import org.zalando.nakadi.client.scala.Listener +import org.zalando.nakadi.client.scala.ClientError +import org.slf4j.LoggerFactory +import org.slf4j.Logger object MySimpleEvent { - def schemaDefinition() = """{ "properties": { "order_number": { "type": "string" } } }""" + + trait DefaultMySimpleEventGenerator extends EventGenerator { + + def newId: String = Random.alphanumeric.take(12).mkString + + def newEvent(): Event = new MySimpleEvent(newId) + + lazy val eventTypeName: String = eventTypeId + newId + + def schemaDefinition: String = """{ "properties": { "order_number": { "type": "string" } } }""" + } + implicit def myEventExampleTR: TypeReference[EventStreamBatch[MySimpleEvent]] = new TypeReference[EventStreamBatch[MySimpleEvent]] {} } case class MySimpleEvent(orderNumber: String) extends Event +class SimpleEventListener extends Listener[MySimpleEvent] { + private val log = LoggerFactory.getLogger(this.getClass) + var receivedEvents = List[MySimpleEvent]() + def id = "SimpleEventListener" + def onError(sourceUrl: String, error: Option[ClientError]): Unit = { + log.error(s"Error $sourceUrl $error") + } + + def onReceive(sourceUrl: String, cursor: Cursor, events: Seq[MySimpleEvent]): Unit = { + receivedEvents = receivedEvents ++ events + log.info(s"Received ${events.size.toLong}") + + } + def onSubscribed(endpoint: String, cursor: Option[Cursor]): Unit = { + log.info(s"Endpoint $endpoint - cursor $cursor") + + } + + def waitToReceive(nrOfEvents: Int): Seq[MySimpleEvent] = { + while (nrOfEvents > receivedEvents.size) { + Thread.sleep(2000) // Wait 2 seconds + } + log.info("############") + log.info(s"Waited to receive $nrOfEvents events, actual size =${receivedEvents.size}") + log.info("############") + receivedEvents + } + +} + \ No newline at end of file diff --git a/it/src/test/scala/org/zalando/nakadi/client/scala/SimpleEventIntegrationTest.scala b/it/src/test/scala/org/zalando/nakadi/client/scala/SimpleEventIntegrationTest.scala deleted file mode 100644 index 0bfc1f5..0000000 --- a/it/src/test/scala/org/zalando/nakadi/client/scala/SimpleEventIntegrationTest.scala +++ /dev/null @@ -1,132 +0,0 @@ -package org.zalando.nakadi.client - -import org.scalatest.{ Matchers, WordSpec } -import org.zalando.nakadi.client.scala.model._ -import com.fasterxml.jackson.core.`type`.TypeReference -import org.zalando.nakadi.client.scala.ClientFactory -import org.zalando.nakadi.client.scala.EventTypesActions -import org.zalando.nakadi.client.scala.EventActions -import org.zalando.nakadi.client.scala.ModelFactory -import org.zalando.nakadi.client.scala.test.factory.EventGenerator -import scala.util.Random - -class EventTypeTest extends WordSpec with Matchers with ModelFactory { - - val generator = new EventGenerator { - def uniqueEventId: String = Random.alphanumeric - def newEvent(): Event = new MySimpleEvent(uniqueEventId) - def eventType: EventType = new EventType(name, // - owningApplication, // - EventTypeCategory.UNDEFINED, None, Nil, // - Some(PartitionStrategy.RANDOM), Option(eventTypeSchema), // - None, Option(paritionKeyFields), None) - } - - import ClientFactory._ - import JacksonJsonMarshaller._ - val eventAction = new EventActions(client) - val eventTypeAction = new EventTypesActions(client) - "POST/PUT/GET/DELETE single EventType " in { - val eventTypeName = "test-client-integration-event-1936085527-148383828851369665" - //Create event - val eventType = createEventType(eventTypeName) - val creationResult = eventTypeAction.create(eventType) - creationResult.isDefined shouldBe false - - //Check the created EventType - checkEventTypeExists(eventType) - - case class MyEventExample(orderNumber: String) extends Event - implicit def problemTR: TypeReference[MyEventExample] = new TypeReference[MyEventExample] {} - val events = for { - a <- 0 to 12 - } yield MyEventExample("order-" + a) - // eventAction.create("test-client-integration-event-1936085527-148383828851369665", List(MyEventExample("test-1"))) - eventAction.publish("test-client-integration-event-1936085527-148383828851369665", events) - - //TODO: Enable this when PUT is supported. - // Update the event - // val updatedEvent = eventType.copy(owningApplication = "laas-team-2") - // events.update(updatedEvent) - - //Check the EventType has bee updated - // checkEventTypeExists(updatedEvent) - // checkEventTypeDoesNotExist(eventType) - - //Delete the created Event - val deletedEvent = eventTypeAction.delete(eventType.name) - deletedEvent.isEmpty shouldBe true - - //Is it really deleted? - checkEventTypeDoesNotExist(eventType) - } - - "POST/GET/DELETE multiple EventTypes " in { - val eventTypeName1 = "test-client-integration-event-1936085527-1" - val eventTypeName2 = "test-client-integration-event-1936085527-2" - //Create 2 EventTypes - val eventType1 = createEventType(eventTypeName1) - val eventType2 = createEventType(eventTypeName2) - - eventTypeAction.create(eventType1) - checkEventTypeExists(eventType1) - - eventTypeAction.create(eventType2) - checkEventTypeExists(eventType2) - - //Get all EventTypes again - //TODO: Enable when Nakadi has no erranous eventType - // val Right(Some(allEvents)) = events.getAll() - // allEvents should contain(eventType1) - // allEvents should contain(eventType2) - - //Delete the 2 EventTypes - eventTypeAction.delete(eventType1.name) - eventTypeAction.delete(eventType2.name) - - //Check if the're really deleted - checkEventTypeDoesNotExist(eventType1) - checkEventTypeDoesNotExist(eventType2) - - //Get all should not contain the deleted events - val Right(Some(updatedEvents)) = eventTypeAction.getAll() - - updatedEvents shouldNot contain(eventType1) - updatedEvents shouldNot contain(eventType2) - - } - - //TODO: Enable when implemented - "UpdateEventTypes" in { - val eventTypeName1 = "test-client-integration-event-1936085527-3" - //Create 2 EventTypes - val eventType = createEventType(eventTypeName1) - - eventTypeAction.create(eventType) - checkEventTypeExists(eventType) - - //Update the event - val updatedEvent = eventType.copy(owningApplication = "laas-team-2") - eventTypeAction.update(updatedEvent) - - //Check the EventType has bee updated - // checkEventTypeExists(updatedEvent) - // checkEventTypeDoesNotExist(eventType) - - } - - def checkEventTypeDoesNotExist(eventType: EventType) = { - val requestedEvent = eventTypeAction.get(eventType.name) - println(requestedEvent) - requestedEvent.isRight shouldBe true - val Right(result) = requestedEvent - result shouldBe None - } - - def checkEventTypeExists(eventType: EventType) = { - val Right(Some(createdEvent)) = eventTypeAction.get(eventType.name) - createdEvent shouldBe eventType - } - -} - diff --git a/it/src/test/scala/org/zalando/nakadi/client/scala/SimpleEventTest.scala b/it/src/test/scala/org/zalando/nakadi/client/scala/SimpleEventTest.scala new file mode 100644 index 0000000..a908ced --- /dev/null +++ b/it/src/test/scala/org/zalando/nakadi/client/scala/SimpleEventTest.scala @@ -0,0 +1,44 @@ +package org.zalando.nakadi.client.scala + +import org.scalatest.Matchers +import org.scalatest.WordSpec +import org.zalando.nakadi.client.scala.test.factory.EventIntegrationHelper +import org.zalando.nakadi.client.scala.test.factory.events.SimpleEventListener +import org.zalando.nakadi.client.scala.model.JacksonJsonMarshaller +import org.zalando.nakadi.client.scala.test.factory.events.MySimpleEvent +import org.zalando.nakadi.client.scala.model.Cursor + +class SimpleEventTest extends WordSpec with Matchers { + + import org.scalatest.Matchers._ + + import ClientFactory._ + import JacksonJsonMarshaller._ + import MySimpleEvent._ + + val client = ClientFactory.getScalaClient() + + val eventGenerator = new DefaultMySimpleEventGenerator() { + def eventTypeId = "SimpleEventIntegrationTest" + } + + "Create and Receive nr of SimpleEvents" in { + + val it = new EventIntegrationHelper(eventGenerator, client) + val listener = new SimpleEventListener() + val nrOfEvents = 4500 + val cursor = Some(Cursor("0", "BEGIN")) + + it.createEventType() + val events = it.publishEvents(nrOfEvents) + + client.subscribe(eventGenerator.eventTypeName, StreamParameters(cursor = cursor), listener) + + val receivedEvents = listener.waitToReceive(nrOfEvents) + + receivedEvents.size shouldBe events.size + receivedEvents shouldBe events + + } +} + From 5de42919d7933287c875804231cf90bfb7d1b8ad Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Fri, 10 Jun 2016 09:34:56 +0200 Subject: [PATCH 076/183] techjira:LAAS-60 Model is up to date + Scala Integration tests --- .../client/scala/test/factory/EventIntegrationHelper.scala | 2 ++ .../scala/org/zalando/nakadi/client/scala/SimpleEventTest.scala | 2 -- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/EventIntegrationHelper.scala b/it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/EventIntegrationHelper.scala index 4f1ac1f..fc0de8d 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/EventIntegrationHelper.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/EventIntegrationHelper.scala @@ -24,6 +24,8 @@ class EventIntegrationHelper(generator: EventGenerator, client: Client) extends } def deleteEventType() = failIfClientError(actions.deleteEventType(eventType.name)) + + def publishEvents(nrOfEvents: Int): Seq[Event] = { val events = for { diff --git a/it/src/test/scala/org/zalando/nakadi/client/scala/SimpleEventTest.scala b/it/src/test/scala/org/zalando/nakadi/client/scala/SimpleEventTest.scala index a908ced..269cad7 100644 --- a/it/src/test/scala/org/zalando/nakadi/client/scala/SimpleEventTest.scala +++ b/it/src/test/scala/org/zalando/nakadi/client/scala/SimpleEventTest.scala @@ -17,7 +17,6 @@ class SimpleEventTest extends WordSpec with Matchers { import MySimpleEvent._ val client = ClientFactory.getScalaClient() - val eventGenerator = new DefaultMySimpleEventGenerator() { def eventTypeId = "SimpleEventIntegrationTest" } @@ -33,7 +32,6 @@ class SimpleEventTest extends WordSpec with Matchers { val events = it.publishEvents(nrOfEvents) client.subscribe(eventGenerator.eventTypeName, StreamParameters(cursor = cursor), listener) - val receivedEvents = listener.waitToReceive(nrOfEvents) receivedEvents.size shouldBe events.size From 2cf83b99bb0b51836027492b3ef2c3add1ce9658 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Fri, 10 Jun 2016 09:51:08 +0200 Subject: [PATCH 077/183] techjira:LAAS-60 Development release 2.0.0-pre-alpha.11 --- .../org/zalando/nakadi/client/EventTypesIntegrationTest.scala | 4 ++-- project/Build.scala | 2 +- project/Dependencies.scala | 3 ++- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/it/src/test/scala/org/zalando/nakadi/client/EventTypesIntegrationTest.scala b/it/src/test/scala/org/zalando/nakadi/client/EventTypesIntegrationTest.scala index 13b24d2..640e322 100644 --- a/it/src/test/scala/org/zalando/nakadi/client/EventTypesIntegrationTest.scala +++ b/it/src/test/scala/org/zalando/nakadi/client/EventTypesIntegrationTest.scala @@ -14,7 +14,7 @@ import JacksonJsonMarshaller._ val eventAction = new EventActions(client) val eventTypeAction = new EventTypesActions(client) "POST/PUT/GET/DELETE single EventType " in { - val eventTypeName="test-client-integration-event-1936085527-148383828851369665" + val eventTypeName="test-client-integration-event-" //Create event val eventType = createEventType(eventTypeName) val creationResult = eventTypeAction.create(eventType) @@ -29,7 +29,7 @@ import JacksonJsonMarshaller._ a <- 0 to 12 } yield MyEventExample("order-"+a) // eventAction.create("test-client-integration-event-1936085527-148383828851369665", List(MyEventExample("test-1"))) - eventAction.publish("test-client-integration-event-1936085527-148383828851369665", events) + eventAction.publish(eventTypeName, events) //TODO: Enable this when PUT is supported. // Update the event diff --git a/project/Build.scala b/project/Build.scala index 5e7ec43..6d32ef9 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -57,7 +57,7 @@ lazy val client = withDefaults( project.settings( name := projectName, organization := "org.zalando.nakadi.client", - version := "2.0.0-pre-alpha.10", + version := "2.0.0-pre-alpha.11", crossPaths := false, scalaVersion := "2.11.8", publishTo := whereToPublishTo(isSnapshot.value), diff --git a/project/Dependencies.scala b/project/Dependencies.scala index 17b4454..cac4a08 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -32,7 +32,8 @@ object Dependencies { val itDeps = clientDeps ++ { Seq( "org.zalando.stups" % "tokens" % "0.9.9", - "org.apache.httpcomponents" % "httpclient" % "4.5.2" + "org.apache.httpcomponents" % "httpclient" % "4.5.2", + "org.scalatest" %% "scalatest" % "2.2.6" ) } } From 1f6794f7e757a4f06aa1db791cd4158f64bfca85 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Mon, 13 Jun 2016 09:46:21 +0200 Subject: [PATCH 078/183] techjira:LAAS-60 Integration tests creation and receiving of events using SCALA API. Fixes #41, #44 --- .../scala/model/EmptyListToOptional.scala | 24 ++++++++++ .../nakadi/client/scala/model/Model.scala | 2 +- .../nakadi/client/scala/ClientImpl.scala | 19 +++++--- .../model/ScalaJacksonJsonMarshaller.scala | 16 ++++++- .../scala/model/ScalaJacksonJsonModule.scala | 27 +++++++++++ .../examples/scala/EventCreationExample.scala | 8 ++-- .../nakadi/client/scala/TestFactory.scala | 1 - .../scala/test/factory/EventGenerator.scala | 6 ++- .../test/factory/EventIntegrationHelper.scala | 18 ++++++-- .../nakadi/client/scala/SimpleEventTest.scala | 45 +++++++++++++++---- 10 files changed, 138 insertions(+), 28 deletions(-) create mode 100644 api/src/main/scala/org/zalando/nakadi/client/scala/model/EmptyListToOptional.scala create mode 100644 client/src/test/scala/org/zalando/nakadi/client/scala/model/ScalaJacksonJsonModule.scala diff --git a/api/src/main/scala/org/zalando/nakadi/client/scala/model/EmptyListToOptional.scala b/api/src/main/scala/org/zalando/nakadi/client/scala/model/EmptyListToOptional.scala new file mode 100644 index 0000000..f64dad3 --- /dev/null +++ b/api/src/main/scala/org/zalando/nakadi/client/scala/model/EmptyListToOptional.scala @@ -0,0 +1,24 @@ +package org.zalando.nakadi.client.scala.model + +import com.fasterxml.jackson.databind.JsonDeserializer +import com.fasterxml.jackson.core.JsonParser +import com.fasterxml.jackson.databind.DeserializationContext + +class EmptyListToOptional extends JsonDeserializer[Option[Seq[AnyRef]]] { + + def deserialize(p: JsonParser, context: DeserializationContext) = { + + p.getValueAsString match { + case v => + println("#######") + println("#######") + println("#######") + println(s"Value $v") + println("#######") + println("#######") + println("#######") + None + } + + } +} \ No newline at end of file diff --git a/api/src/main/scala/org/zalando/nakadi/client/scala/model/Model.scala b/api/src/main/scala/org/zalando/nakadi/client/scala/model/Model.scala index a6ccb70..98eb061 100644 --- a/api/src/main/scala/org/zalando/nakadi/client/scala/model/Model.scala +++ b/api/src/main/scala/org/zalando/nakadi/client/scala/model/Model.scala @@ -293,6 +293,6 @@ class BatchItemStepType extends TypeReference[BatchItemStep.type] case object SchemaType extends Enumeration { type SchemaType = Value - val JSON = Value("JSON_SCHEMA") + val JSON = Value("json_schema") } class SchemaTypeType extends TypeReference[SchemaType.type] diff --git a/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala index 9eacd64..378645b 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala @@ -134,15 +134,16 @@ class ClientImpl(connection: Connection, subscriber: SubscriptionHandler, charSe private def logFutureEither[A, T](future: Future[Either[ClientError, T]]): Future[Either[ClientError, T]] = { future recover { case e: Throwable => - logger.error("A unexpected error occured:", e.getMessage) - Left(ClientError("Error: " + e.getMessage, None)) + val msg = s"A unexpected error occured: ${e.getMessage}" + logger.error(msg) + Left(ClientError(msg, None)) } } private def logFutureOption(future: Future[Option[ClientError]]): Future[Option[ClientError]] = { future recover { case e: Throwable => - logger.error("A unexpected error occured", e) - Option(ClientError("Error: " + e.getMessage, None)) + val msg = s"A unexpected error occured: ${e.getMessage}" + Option(ClientError(msg, None)) } } @@ -151,7 +152,11 @@ class ClientImpl(connection: Connection, subscriber: SubscriptionHandler, charSe response match { case HttpResponse(status, headers, entity, protocol) if (status.isSuccess()) => try { - Unmarshal(entity).to[String].map(body => Right(Some(deserializer.from(body)))) + Unmarshal(entity).to[String].map { + body => + logger.debug(s"Payload: $body") + Right(Some(deserializer.from(body))) + } } catch { case e: Throwable => val msg = "Failed to deserialise the content with error: %s".format(e.getMessage) @@ -159,8 +164,8 @@ class ClientImpl(connection: Connection, subscriber: SubscriptionHandler, charSe Future.successful(Left(ClientError(msg, Some(status.intValue())))) } case HttpResponse(StatusCodes.NotFound, headers, entity, protocol) => - val msg = "Not found!" - Future.successful(Left(ClientError(msg, Some(StatusCodes.NotFound.intValue)))) + logger.info(s"Received: httpStatus - Not found ${StatusCodes.NotFound}") + Future.successful(Right(None)) case HttpResponse(status, headers, entity, protocol) if (status.isRedirection()) => val msg = "Not implemented: http-status (" + status.intValue() + "}) and reason:" + status.reason() logger.info(msg) diff --git a/client/src/main/scala/org/zalando/nakadi/client/scala/model/ScalaJacksonJsonMarshaller.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/model/ScalaJacksonJsonMarshaller.scala index 498affc..9cfd58f 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/scala/model/ScalaJacksonJsonMarshaller.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/scala/model/ScalaJacksonJsonMarshaller.scala @@ -17,6 +17,10 @@ import com.fasterxml.jackson.databind.SerializationFeature import com.fasterxml.jackson.databind.deser.DeserializationProblemHandler import com.fasterxml.jackson.module.scala.DefaultScalaModule import com.typesafe.scalalogging.Logger +import com.fasterxml.jackson.module.scala.IteratorModule +import com.fasterxml.jackson.module.scala.OptionModule +import com.fasterxml.jackson.module.scala.SeqModule +import com.fasterxml.jackson.module.scala.MapModule object JacksonJsonMarshaller { val logger = Logger(LoggerFactory.getLogger(this.getClass)) @@ -63,9 +67,19 @@ object JacksonJsonMarshaller { def from(from: String): T = defaultObjectMapper.readValue[T](from, expectedType) } - lazy val defaultObjectMapper: ObjectMapper = new ObjectMapper() // + + lazy val defaultObjectMapper: ObjectMapper = new ObjectMapper(){ + private val module = new OptionModule + with MapModule + with SeqModule + with IteratorModule + + this.registerModule(module) + + } // .registerModule(new DefaultScalaModule) .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) + .configure(DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT, true) .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) .setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE) .setSerializationInclusion(JsonInclude.Include.NON_EMPTY) diff --git a/client/src/test/scala/org/zalando/nakadi/client/scala/model/ScalaJacksonJsonModule.scala b/client/src/test/scala/org/zalando/nakadi/client/scala/model/ScalaJacksonJsonModule.scala new file mode 100644 index 0000000..d3ca937 --- /dev/null +++ b/client/src/test/scala/org/zalando/nakadi/client/scala/model/ScalaJacksonJsonModule.scala @@ -0,0 +1,27 @@ +package org.zalando.nakadi.client.scala.model + +import org.scalatest.Matchers +import org.scalatest.WordSpec +import com.fasterxml.jackson.annotation.JsonProperty +import com.fasterxml.jackson.annotation.JsonIgnore +import com.fasterxml.jackson.databind.DeserializationFeature + +case class Example private (enrichment_strategies: Seq[String]) + +class ScalaJacksonJsonModuleTest extends WordSpec with Matchers { + val emptyJsonList = """{"enrichment_strategies": []}""" + val emptyJson = """{}""" + val mapper = JacksonJsonMarshaller.defaultObjectMapper + + "Deserialize from empty array to Nil " in { + val actual = mapper.readValue(emptyJsonList, classOf[Example]) + println(actual) + actual shouldBe Example(Nil) + } + "Deserialize from null to null" in { + val actual = mapper.readValue(emptyJson, classOf[Example]) + println(actual) + actual shouldBe Example(null) + } + +} \ No newline at end of file diff --git a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala index d9a068f..45f981f 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala @@ -51,19 +51,19 @@ object EventCreationExample extends App { val category = EventTypeCategory.UNDEFINED // We want just to pass data without through Nakadi, simple schema-validation is enough! val validationStrategies = Nil // Validation strategies are not defined yet! val enrichmentStrategies = Nil - val partitionStrategy = PartitionStrategy.RANDOM + val partitionStrategy = Some(PartitionStrategy.RANDOM) val dataKeyFields = Nil - def paritionKeyFields() = List("date", "topic") + val paritionKeyFields = List("date", "topic") val eventType = new EventType(eventTypeName, owner, category, validationStrategies, enrichmentStrategies, - Some(partitionStrategy), + partitionStrategy, eventTypeSchema, dataKeyFields, - paritionKeyFields(), + paritionKeyFields, None) //You need to import the default Serializer if you don't sepecify your own! diff --git a/it/src/main/scala/org/zalando/nakadi/client/scala/TestFactory.scala b/it/src/main/scala/org/zalando/nakadi/client/scala/TestFactory.scala index bd11d7e..b2978ef 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/scala/TestFactory.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/scala/TestFactory.scala @@ -27,7 +27,6 @@ case class EventActions(client: Client)extends TestUtil { } case class EventTypesActions(client: Client)extends TestUtil { - import JacksonJsonMarshaller._ def create(event: EventType) = { executeCall(client.createEventType(event)) } diff --git a/it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/EventGenerator.scala b/it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/EventGenerator.scala index 26c5ec1..074d772 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/EventGenerator.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/EventGenerator.scala @@ -79,7 +79,7 @@ trait EventGenerator { /** * Returns the enrichmentStrategies value. Default = Nil */ - def enrichmentStrategies: Seq[EventEnrichmentStrategy.Value] = Nil + def enrichmentStrategies: Seq[EventEnrichmentStrategy.Value] =Nil /** * Returns the partitionStrategy value. Default = Random @@ -97,7 +97,7 @@ trait EventGenerator { def dataKeyFields: Seq[String] = Nil /** - * Returns the partitionKeyFields value. Default = Nil + * Returns the partitionKeyFields value. Default =Nil */ def partitionKeyFields: Seq[String] = Nil @@ -106,4 +106,6 @@ trait EventGenerator { */ def statistics: Option[EventTypeStatistics] = None + + } \ No newline at end of file diff --git a/it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/EventIntegrationHelper.scala b/it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/EventIntegrationHelper.scala index fc0de8d..3813207 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/EventIntegrationHelper.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/EventIntegrationHelper.scala @@ -12,6 +12,9 @@ import org.zalando.nakadi.client.scala.model._ import org.zalando.nakadi.client.scala.model._ import org.zalando.nakadi.client.scala.model.Event import org.slf4j.LoggerFactory +import scala.concurrent.Await +import scala.concurrent.duration.DurationInt +import scala.concurrent.Future class EventIntegrationHelper(generator: EventGenerator, client: Client) extends WordSpec with Matchers { private val log = LoggerFactory.getLogger(this.getClass) @@ -19,13 +22,18 @@ class EventIntegrationHelper(generator: EventGenerator, client: Client) extends val eventType = generator.eventType def createEventType(): EventType = { - failIfClientError(actions.createEventType(eventType)) + failIfClientError(executeCall(client.createEventType(eventType))) eventType } + def getEventType(eventTypeName: String = eventType.name): Option[EventType] = executeCall(client.getEventType(eventType.name)) match { + case Left(clientError) => + failIfClientError(Option(clientError)) + None + case Right(result) => result + } + def deleteEventType() = failIfClientError(actions.deleteEventType(eventType.name)) - - def publishEvents(nrOfEvents: Int): Seq[Event] = { val events = for { @@ -43,6 +51,10 @@ class EventIntegrationHelper(generator: EventGenerator, client: Client) extends case _ => } + def executeCall[T](call: => Future[T]): T = { + Await.result(call, 10.second) + } + } \ No newline at end of file diff --git a/it/src/test/scala/org/zalando/nakadi/client/scala/SimpleEventTest.scala b/it/src/test/scala/org/zalando/nakadi/client/scala/SimpleEventTest.scala index 269cad7..7fb83dc 100644 --- a/it/src/test/scala/org/zalando/nakadi/client/scala/SimpleEventTest.scala +++ b/it/src/test/scala/org/zalando/nakadi/client/scala/SimpleEventTest.scala @@ -2,11 +2,11 @@ package org.zalando.nakadi.client.scala import org.scalatest.Matchers import org.scalatest.WordSpec -import org.zalando.nakadi.client.scala.test.factory.EventIntegrationHelper -import org.zalando.nakadi.client.scala.test.factory.events.SimpleEventListener +import org.zalando.nakadi.client.scala.model.Cursor import org.zalando.nakadi.client.scala.model.JacksonJsonMarshaller +import org.zalando.nakadi.client.scala.test.factory.EventIntegrationHelper import org.zalando.nakadi.client.scala.test.factory.events.MySimpleEvent -import org.zalando.nakadi.client.scala.model.Cursor +import org.zalando.nakadi.client.scala.test.factory.events.SimpleEventListener class SimpleEventTest extends WordSpec with Matchers { @@ -16,16 +16,19 @@ class SimpleEventTest extends WordSpec with Matchers { import JacksonJsonMarshaller._ import MySimpleEvent._ + val client = ClientFactory.getScalaClient() - val eventGenerator = new DefaultMySimpleEventGenerator() { - def eventTypeId = "SimpleEventIntegrationTest" - } + val nrOfEvents = 45 + val listener = new SimpleEventListener() + + - "Create and Receive nr of SimpleEvents" in { + "Validate Published nr of SimpleEvents" in { + val eventGenerator = new DefaultMySimpleEventGenerator() { + def eventTypeId = s"SimpleEventIntegrationTest-Validate-Published-Events-$nrOfEvents" + } val it = new EventIntegrationHelper(eventGenerator, client) - val listener = new SimpleEventListener() - val nrOfEvents = 4500 val cursor = Some(Cursor("0", "BEGIN")) it.createEventType() @@ -38,5 +41,29 @@ class SimpleEventTest extends WordSpec with Matchers { receivedEvents shouldBe events } + "Validate created EventType" in { + val eventGenerator = new DefaultMySimpleEventGenerator() { + def eventTypeId = s"SimpleEventIntegrationTest-Validate-Created-Events-$nrOfEvents" + } + val it = new EventIntegrationHelper(eventGenerator, client) + it.createEventType() + val optionalOfCreatedEventType = it.getEventType() + optionalOfCreatedEventType.isDefined shouldBe true + val Some(eventType)= optionalOfCreatedEventType + eventType.category shouldBe it.eventType.category + eventType.dataKeyFields shouldBe null + eventType.name shouldBe it.eventType.name + eventType.owningApplication shouldBe it.eventType.owningApplication + eventType.partitionStrategy shouldBe it.eventType.partitionStrategy + eventType.schema shouldBe it.eventType.schema + eventType.statistics shouldBe it.eventType.statistics + eventType.validationStrategies shouldBe null + eventType.enrichmentStrategies shouldBe it.eventType.enrichmentStrategies + eventType.partitionKeyFields shouldBe it.eventType.partitionKeyFields + + } + + + } From 6216746848805b5973a195a34c23e87c6b099c2b Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Mon, 13 Jun 2016 10:08:30 +0200 Subject: [PATCH 079/183] techjira:LAAS-60 Fixes error in deserialization of json enums written in lower_case. Fixes #59. --- .../client/java/enumerator/SchemaType.java | 2 +- .../nakadi/client/scala/SimpleEventTest.scala | 58 ++++++++++--------- 2 files changed, 32 insertions(+), 28 deletions(-) diff --git a/api/src/main/java/org/zalando/nakadi/client/java/enumerator/SchemaType.java b/api/src/main/java/org/zalando/nakadi/client/java/enumerator/SchemaType.java index 7cbee9c..9bfc46b 100644 --- a/api/src/main/java/org/zalando/nakadi/client/java/enumerator/SchemaType.java +++ b/api/src/main/java/org/zalando/nakadi/client/java/enumerator/SchemaType.java @@ -5,7 +5,7 @@ import com.fasterxml.jackson.annotation.JsonValue; public enum SchemaType { - JSON("JSON_SCHEMA"); + JSON("json_schema"); private final String schema; diff --git a/it/src/test/scala/org/zalando/nakadi/client/scala/SimpleEventTest.scala b/it/src/test/scala/org/zalando/nakadi/client/scala/SimpleEventTest.scala index 7fb83dc..a70fd29 100644 --- a/it/src/test/scala/org/zalando/nakadi/client/scala/SimpleEventTest.scala +++ b/it/src/test/scala/org/zalando/nakadi/client/scala/SimpleEventTest.scala @@ -16,18 +16,24 @@ class SimpleEventTest extends WordSpec with Matchers { import JacksonJsonMarshaller._ import MySimpleEvent._ - val client = ClientFactory.getScalaClient() val nrOfEvents = 45 val listener = new SimpleEventListener() - - + + "404 should be handled graciously, by retuning None" in { + val it = new EventIntegrationHelper(null, client) + it.getEventType("non-existing-event-type-name") match { + case Some(_) => fail("Should not fail, because eventType was not created yet!!") + case None => + } + + } "Validate Published nr of SimpleEvents" in { - val eventGenerator = new DefaultMySimpleEventGenerator() { - def eventTypeId = s"SimpleEventIntegrationTest-Validate-Published-Events-$nrOfEvents" - } + val eventGenerator = new DefaultMySimpleEventGenerator() { + def eventTypeId = s"SimpleEventIntegrationTest-Validate-Published-Events-$nrOfEvents" + } val it = new EventIntegrationHelper(eventGenerator, client) val cursor = Some(Cursor("0", "BEGIN")) @@ -43,27 +49,25 @@ class SimpleEventTest extends WordSpec with Matchers { } "Validate created EventType" in { val eventGenerator = new DefaultMySimpleEventGenerator() { - def eventTypeId = s"SimpleEventIntegrationTest-Validate-Created-Events-$nrOfEvents" - } - val it = new EventIntegrationHelper(eventGenerator, client) - it.createEventType() - val optionalOfCreatedEventType = it.getEventType() - optionalOfCreatedEventType.isDefined shouldBe true - val Some(eventType)= optionalOfCreatedEventType - eventType.category shouldBe it.eventType.category - eventType.dataKeyFields shouldBe null - eventType.name shouldBe it.eventType.name - eventType.owningApplication shouldBe it.eventType.owningApplication - eventType.partitionStrategy shouldBe it.eventType.partitionStrategy - eventType.schema shouldBe it.eventType.schema - eventType.statistics shouldBe it.eventType.statistics - eventType.validationStrategies shouldBe null - eventType.enrichmentStrategies shouldBe it.eventType.enrichmentStrategies - eventType.partitionKeyFields shouldBe it.eventType.partitionKeyFields - + def eventTypeId = s"SimpleEventIntegrationTest-Validate-Created-Events-$nrOfEvents" + } + val it = new EventIntegrationHelper(eventGenerator, client) + it.createEventType() + val optionalOfCreatedEventType = it.getEventType() + optionalOfCreatedEventType.isDefined shouldBe true + val Some(eventType) = optionalOfCreatedEventType + eventType.category shouldBe it.eventType.category + eventType.dataKeyFields shouldBe null + eventType.name shouldBe it.eventType.name + eventType.owningApplication shouldBe it.eventType.owningApplication + eventType.partitionStrategy shouldBe it.eventType.partitionStrategy + eventType.schema shouldBe it.eventType.schema + eventType.statistics shouldBe it.eventType.statistics + eventType.validationStrategies shouldBe null + eventType.enrichmentStrategies shouldBe it.eventType.enrichmentStrategies + eventType.partitionKeyFields shouldBe it.eventType.partitionKeyFields + } - - - + } From 9668869c6a1ef2968eb0b828b160aeab18816cf1 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Mon, 13 Jun 2016 10:15:10 +0200 Subject: [PATCH 080/183] techjira:LAAS-60 Development release 2.0.0-pre-alpha.12 --- project/Build.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/Build.scala b/project/Build.scala index 6d32ef9..dd723c0 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -57,7 +57,7 @@ lazy val client = withDefaults( project.settings( name := projectName, organization := "org.zalando.nakadi.client", - version := "2.0.0-pre-alpha.11", + version := "2.0.0-pre-alpha.12", crossPaths := false, scalaVersion := "2.11.8", publishTo := whereToPublishTo(isSnapshot.value), From eeabac76b127af94f46a93f26d1a06e8993243a4 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Mon, 13 Jun 2016 11:41:20 +0200 Subject: [PATCH 081/183] techjira:LAAS-60 Development release 2.0.0-pre-alpha.13 --- project/Build.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/Build.scala b/project/Build.scala index dd723c0..f01e7c5 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -57,7 +57,7 @@ lazy val client = withDefaults( project.settings( name := projectName, organization := "org.zalando.nakadi.client", - version := "2.0.0-pre-alpha.12", + version := "2.0.0-pre-alpha.13", crossPaths := false, scalaVersion := "2.11.8", publishTo := whereToPublishTo(isSnapshot.value), From 2aea8639a844c1c7ac6b8375e39035fb173948af Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Mon, 13 Jun 2016 15:16:13 +0200 Subject: [PATCH 082/183] techjira:LAAS-60 Fixing the Exceptions by returning the appropriate Result(empty optional) in the java client. Fixes 44 --- .../client/java/JavaClientHandler.scala | 4 +- .../nakadi/client/java/ClientTest.scala | 41 +++++++++++++++++++ .../test/factory/events/MySimpleEvent.java | 17 ++++++++ 3 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 client/src/test/scala/org/zalando/nakadi/client/java/ClientTest.scala create mode 100644 it/src/main/java/org/zalando/nakadi/client/java/test/factory/events/MySimpleEvent.java diff --git a/client/src/main/scala/org/zalando/nakadi/client/java/JavaClientHandler.scala b/client/src/main/scala/org/zalando/nakadi/client/java/JavaClientHandler.scala index cef47e3..d8f0902 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/java/JavaClientHandler.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/java/JavaClientHandler.scala @@ -36,6 +36,7 @@ import akka.http.scaladsl.unmarshalling.Unmarshal import org.zalando.nakadi.client.handler.SubscriptionHandlerImpl import org.zalando.nakadi.client.handler.SubscriptionHandler import org.zalando.nakadi.client.utils.GeneralConversions +import akka.http.scaladsl.model.StatusCodes /** * Handler for mapping(Java<->Scala) and handling http calls and listener subscriptions for the Java API. @@ -65,7 +66,8 @@ class JavaClientHandlerImpl(val connection: Connection, subscriber: Subscription case Success(result) => result.map(Optional.of(_)) case Failure(error) => throw new RuntimeException(error.getMessage) } - + case HttpResponse(StatusCodes.NotFound, headers, entity, protocol) => + Future.successful(Optional.empty()) case HttpResponse(status, headers, entity, protocol) if (status.isFailure()) => throw new RuntimeException(status.reason()) diff --git a/client/src/test/scala/org/zalando/nakadi/client/java/ClientTest.scala b/client/src/test/scala/org/zalando/nakadi/client/java/ClientTest.scala new file mode 100644 index 0000000..06ea8d9 --- /dev/null +++ b/client/src/test/scala/org/zalando/nakadi/client/java/ClientTest.scala @@ -0,0 +1,41 @@ +package org.zalando.nakadi.client.java + +import org.scalatest.BeforeAndAfter +import org.scalatest.Matchers +import org.scalatest.WordSpec +import org.zalando.nakadi.client.handler.SubscriptionHandler +import akka.http.scaladsl.model.HttpResponse +import org.zalando.nakadi.client.scala.Connection +import org.scalatest.mock.MockitoSugar._ +import scala.concurrent.Future +import org.mockito.Mockito.reset +import org.mockito.Mockito.when +import org.mockito.Matchers.anyString +import java.util.Optional +import akka.http.scaladsl.model.StatusCodes +import akka.http.scaladsl.model.HttpProtocols +import akka.http.scaladsl.model.HttpEntity + +class ClientTest extends WordSpec with Matchers with BeforeAndAfter { + val connection = mock[Connection] + val subscriptionHandler = mock[SubscriptionHandler] + val clientHandler = new JavaClientHandlerImpl(connection, subscriptionHandler) + val client = new ClientImpl(clientHandler) + + val eventTypeName = "EventTypeName" + + before { + reset(connection, subscriptionHandler) + } + + + + + "Should return Empty Optional when endpoint returns a 404" in { + val res = HttpResponse.apply(StatusCodes.NotFound, Nil, HttpEntity.Empty, HttpProtocols.`HTTP/1.1`) + val futureResponse = Future.successful(res) + when(connection.get(anyString)).thenReturn(futureResponse) + val result = client.getEventType(eventTypeName).get + result shouldBe Optional.empty() + } +} \ No newline at end of file diff --git a/it/src/main/java/org/zalando/nakadi/client/java/test/factory/events/MySimpleEvent.java b/it/src/main/java/org/zalando/nakadi/client/java/test/factory/events/MySimpleEvent.java new file mode 100644 index 0000000..75d7fe4 --- /dev/null +++ b/it/src/main/java/org/zalando/nakadi/client/java/test/factory/events/MySimpleEvent.java @@ -0,0 +1,17 @@ +package org.zalando.nakadi.client.java.test.factory.events; + +import org.zalando.nakadi.client.java.model.Event; + +public class MySimpleEvent implements Event { + + private String orderNumber; + + public MySimpleEvent(String orderNumber) { + this.orderNumber = orderNumber; + } + + public String getOrderNumber() { + return orderNumber; + } + +} From 7f5a9f6f4cab21a4417f372eb97b240410d8a997 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Mon, 13 Jun 2016 15:26:38 +0200 Subject: [PATCH 083/183] techjira:LAAS-60 Fixing the Exceptions by returning the appropriate Result(empty optional) in the java client. Fixes 44 --- ...{ClientTest.scala => JavaClientTest.scala} | 22 ++++++++++--------- ...ClientTest.scala => ScalaClientTest.scala} | 22 ++++++++----------- 2 files changed, 21 insertions(+), 23 deletions(-) rename client/src/test/scala/org/zalando/nakadi/client/java/{ClientTest.scala => JavaClientTest.scala} (61%) rename client/src/test/scala/org/zalando/nakadi/client/scala/{ClientTest.scala => ScalaClientTest.scala} (76%) diff --git a/client/src/test/scala/org/zalando/nakadi/client/java/ClientTest.scala b/client/src/test/scala/org/zalando/nakadi/client/java/JavaClientTest.scala similarity index 61% rename from client/src/test/scala/org/zalando/nakadi/client/java/ClientTest.scala rename to client/src/test/scala/org/zalando/nakadi/client/java/JavaClientTest.scala index 06ea8d9..3bc1e97 100644 --- a/client/src/test/scala/org/zalando/nakadi/client/java/ClientTest.scala +++ b/client/src/test/scala/org/zalando/nakadi/client/java/JavaClientTest.scala @@ -15,27 +15,29 @@ import java.util.Optional import akka.http.scaladsl.model.StatusCodes import akka.http.scaladsl.model.HttpProtocols import akka.http.scaladsl.model.HttpEntity +import akka.http.scaladsl.model.ContentTypes -class ClientTest extends WordSpec with Matchers with BeforeAndAfter { +class JavaClientTest extends WordSpec with Matchers with BeforeAndAfter { val connection = mock[Connection] val subscriptionHandler = mock[SubscriptionHandler] val clientHandler = new JavaClientHandlerImpl(connection, subscriptionHandler) val client = new ClientImpl(clientHandler) - val eventTypeName = "EventTypeName" before { reset(connection, subscriptionHandler) } - - + "Java Client" should { - "Should return Empty Optional when endpoint returns a 404" in { - val res = HttpResponse.apply(StatusCodes.NotFound, Nil, HttpEntity.Empty, HttpProtocols.`HTTP/1.1`) - val futureResponse = Future.successful(res) - when(connection.get(anyString)).thenReturn(futureResponse) - val result = client.getEventType(eventTypeName).get - result shouldBe Optional.empty() + "map a 404 to an Empty Optional" in { + val headers = Nil + val entity = HttpEntity(ContentTypes.`application/json`, "{}") + val response = new HttpResponse(StatusCodes.NotFound, headers, entity, HttpProtocols.`HTTP/1.1`) + val futureResponse = Future.successful(response) + when(connection.get(anyString)).thenReturn(futureResponse) + val result = client.getEventType(eventTypeName).get + result shouldBe Optional.empty() + } } } \ No newline at end of file diff --git a/client/src/test/scala/org/zalando/nakadi/client/scala/ClientTest.scala b/client/src/test/scala/org/zalando/nakadi/client/scala/ScalaClientTest.scala similarity index 76% rename from client/src/test/scala/org/zalando/nakadi/client/scala/ClientTest.scala rename to client/src/test/scala/org/zalando/nakadi/client/scala/ScalaClientTest.scala index 4219eb1..68b7a3a 100644 --- a/client/src/test/scala/org/zalando/nakadi/client/scala/ClientTest.scala +++ b/client/src/test/scala/org/zalando/nakadi/client/scala/ScalaClientTest.scala @@ -26,38 +26,34 @@ import org.zalando.nakadi.client.utils.Uri import org.zalando.nakadi.client.handler.SubscriptionHandlerImpl import org.zalando.nakadi.client.handler.SubscriptionHandler -class ClientTest extends WordSpec with Matchers with MockitoSugar with BeforeAndAfter { +class ScalaClientTest extends WordSpec with Matchers with MockitoSugar with BeforeAndAfter { import JacksonJsonMarshaller._ import Uri._ private var connection: Connection = mock[Connection] private var subscriber: SubscriptionHandler = mock[SubscriptionHandler] private val client: Client = new ClientImpl(connection, subscriber) + val eventTypeName = "EventTypeName" before { reset(connection) } - "ClientImpl " should { + "Scala Client " should { - "return a None when receiving a 404" in { - val headers = null - val entity = HttpEntity(ContentTypes.`application/json`, "abc") + "map a 404 to a None" in { + val headers = Nil + val entity = HttpEntity(ContentTypes.`application/json`, "{}") val response = new HttpResponse(StatusCodes.NotFound, headers, entity, HttpProtocols.`HTTP/1.1`) val futureResponse = Future.successful(response) - // when(connection.get(URI_PARTITIONING_STRATEGIES)).thenReturn(futureResponse) - // val result=Await.result(client.partitionStrategies(),500.seconds) - // result.isLeft shouldBe true - // val Left(clientError) = result - // clientError.status shouldBe None + when(connection.get(anyString)).thenReturn(futureResponse) + val result = Await.result(client.getEventType(eventTypeName),5.seconds) + result shouldBe Right(None) } "marshall an object when receiving http a 200 with valid payload" in { - //TODO : implement } "catch the marshalling exception when receiving a 200 with invalid payload" in { - //TODO : implement } "return a ClientError receiving a redirection (300-399)" in { - //TODO : implement } } From 20e2bbfe85f4793e6cdf9a1dbfd1fde813ced1e4 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Tue, 14 Jun 2016 12:00:56 +0200 Subject: [PATCH 084/183] techjira:LAAS-60 fixes #58 --- .../zalando/nakadi/client/java/Client.java | 8 +- .../zalando/nakadi/client/scala/Client.scala | 4 +- .../nakadi/client/java/ClientImpl.java | 8 +- .../client/java/JavaClientHandler.scala | 22 +++--- .../nakadi/client/scala/ClientImpl.scala | 9 +-- .../nakadi/client/java/JavaClientTest.scala | 79 ++++++++++++++++++- .../nakadi/client/scala/ScalaClientTest.scala | 62 +++++++++++++-- 7 files changed, 153 insertions(+), 39 deletions(-) diff --git a/api/src/main/java/org/zalando/nakadi/client/java/Client.java b/api/src/main/java/org/zalando/nakadi/client/java/Client.java index 5a1e950..b87f826 100644 --- a/api/src/main/java/org/zalando/nakadi/client/java/Client.java +++ b/api/src/main/java/org/zalando/nakadi/client/java/Client.java @@ -141,17 +141,17 @@ public interface Client { * @param eventTypeName The unique name (id) of the EventType target * @param parameters Parameters for customizing the details of the streaming. * @param listener Listener to pass the event to when it is received. - * @return Void in case of success + * @return ClientError in case of failure and Empty Optional in case of success. */ - void subscribe(String eventTypeName, StreamParameters parameters, Listener listener, Deserializer> deserializer); + Optional subscribe(String eventTypeName, StreamParameters parameters, Listener listener, Deserializer> deserializer); /** * Registers the subscription of a listener to start streaming events from a partition in non-blocking fashion. * @param eventTypeName The unique name (id) of the EventType target * @param parameters Parameters for customizing the details of the streaming. * @param typeRef TypeReference for unmarshalling with the Jackson ObjectMapper. - * @return Void in case of success + * @return ClientError in case of failure and Empty Optional in case of success. */ - void subscribe(String eventTypeName, StreamParameters parameters, Listener listener, TypeReference> typeRef); + Optional subscribe(String eventTypeName, StreamParameters parameters, Listener listener, TypeReference> typeRef); /** * Removes the subscription of a listener, to stop streaming events from a partition. * @param eventTypeName The unique name (id) of the EventType target diff --git a/api/src/main/scala/org/zalando/nakadi/client/scala/Client.scala b/api/src/main/scala/org/zalando/nakadi/client/scala/Client.scala index babae17..ca83efc 100644 --- a/api/src/main/scala/org/zalando/nakadi/client/scala/Client.scala +++ b/api/src/main/scala/org/zalando/nakadi/client/scala/Client.scala @@ -160,7 +160,7 @@ trait Client { * @param listener - Listener to pass the event to when it is received. * @param des - Json Marshaller(implicit) to deserialize the event to Json. */ - def subscribe[T <: Event](eventTypeName: String, parameters: StreamParameters, listener: Listener[T])(implicit des: Deserializer[EventStreamBatch[T]]): Future[Option[ClientError]] + def subscribe[T <: Event](eventTypeName: String, parameters: StreamParameters, listener: Listener[T])(implicit des: Deserializer[EventStreamBatch[T]]): Option[ClientError] /** * Registers the subscription of a listener to start streaming events from a partition in non-blocking fashion. * @@ -169,7 +169,7 @@ trait Client { * @param listener - Listener to pass the event to when it is received. * @param typeRef - TypeReference/Helper for using with the Jackson-Objectmapper to deserializing the event to json. */ - def subscribe[T <: Event](eventTypeName: String, parameters: StreamParameters, listener: Listener[T], typeRef: TypeReference[EventStreamBatch[T]]): Future[Option[ClientError]] + def subscribe[T <: Event](eventTypeName: String, parameters: StreamParameters, listener: Listener[T], typeRef: TypeReference[EventStreamBatch[T]]): Option[ClientError] /** * Removes the subscription of a listener, to stop streaming events from a partition. * diff --git a/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java b/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java index 9da723d..206eb82 100644 --- a/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java +++ b/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java @@ -122,14 +122,14 @@ public Future stop() { } @Override - public void subscribe(String eventTypeName, StreamParameters parameters, Listener listener, Deserializer> deserializer) { - handler.subscribe(eventTypeName,Uri.getEventStreamingUri(eventTypeName), parameters, listener, deserializer); + public Optional subscribe(String eventTypeName, StreamParameters parameters, Listener listener, Deserializer> deserializer) { + return handler.subscribe(eventTypeName,Uri.getEventStreamingUri(eventTypeName), parameters, listener, deserializer); } @Override - public void subscribe(String eventTypeName, StreamParameters parameters, Listener listener, + public Optional subscribe(String eventTypeName, StreamParameters parameters, Listener listener, TypeReference> typeRef) { - handler.subscribe(eventTypeName,Uri.getEventStreamingUri(eventTypeName), parameters, listener, SerializationUtils.withCustomDeserializer(typeRef)); + return handler.subscribe(eventTypeName,Uri.getEventStreamingUri(eventTypeName), parameters, listener, SerializationUtils.withCustomDeserializer(typeRef)); } @Override diff --git a/client/src/main/scala/org/zalando/nakadi/client/java/JavaClientHandler.scala b/client/src/main/scala/org/zalando/nakadi/client/java/JavaClientHandler.scala index d8f0902..38682e1 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/java/JavaClientHandler.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/java/JavaClientHandler.scala @@ -46,8 +46,8 @@ trait JavaClientHandler { def get[T](endpoint: String, des: Deserializer[T]): java.util.concurrent.Future[Optional[T]] def get[T](endpoint: String, headers: Seq[HttpHeader], des: Deserializer[T]): java.util.concurrent.Future[Optional[T]] def post[T](endpoint: String, model: T)(implicit serializer: Serializer[T]): java.util.concurrent.Future[Void] - def subscribe[T <: JEvent](eventTypeName: String, endpoint: String, parameters: JStreamParameters, listener: JListener[T])(implicit des: Deserializer[JEventStreamBatch[T]]) - def unsubscribe[T <: JEvent](eventTypeName: String, partition: Optional[String], listener: JListener[T]) + def subscribe[T <: JEvent](eventTypeName: String, endpoint: String, parameters: JStreamParameters, listener: JListener[T])(implicit des: Deserializer[JEventStreamBatch[T]]): Optional[ClientError] + def unsubscribe[T <: JEvent](eventTypeName: String, partition: Optional[String], listener: JListener[T]) } @@ -110,16 +110,14 @@ class JavaClientHandlerImpl(val connection: Connection, subscriber: Subscription } } - def subscribe[T <: JEvent](eventTypeName: String, endpoint: String, parameters: JStreamParameters, listener: JListener[T])(implicit des: Deserializer[JEventStreamBatch[T]]) = - FutureConversions.fromFuture2FutureVoid { - (Future { - import ModelConverter._ - val params: Option[ScalaStreamParameters] = toScalaStreamParameters(parameters) - val eventHandler: EventHandler = new EventHandlerImpl[T, EmptyScalaEvent](Left((des, listener))) - val finalUrl = withUrl(endpoint, params) - subscriber.subscribe(eventTypeName, endpoint, getCursor(params), eventHandler) - }) - } + def subscribe[T <: JEvent](eventTypeName: String, endpoint: String, parameters: JStreamParameters, listener: JListener[T])(implicit des: Deserializer[JEventStreamBatch[T]]) = { + import ModelConverter._ + val params: Option[ScalaStreamParameters] = toScalaStreamParameters(parameters) + val eventHandler: EventHandler = new EventHandlerImpl[T, EmptyScalaEvent](Left((des, listener))) + val finalUrl = withUrl(endpoint, params) + val res = subscriber.subscribe(eventTypeName, finalUrl, getCursor(params), eventHandler) + toJavaClientError(res) + } def unsubscribe[T <: JEvent](eventTypeName: String, partition: Optional[String], listener: JListener[T]) = { subscriber.unsubscribe(eventTypeName, toOption(partition), listener.getId) diff --git a/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala index 378645b..5f65833 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala @@ -98,20 +98,20 @@ class ClientImpl(connection: Connection, subscriber: SubscriptionHandler, charSe None } - def subscribe[T <: Event](eventTypeName: String, parameters: StreamParameters, listener: Listener[T], typeRef: TypeReference[EventStreamBatch[T]]): Future[Option[ClientError]] = { + def subscribe[T <: Event](eventTypeName: String, parameters: StreamParameters, listener: Listener[T], typeRef: TypeReference[EventStreamBatch[T]]): Option[ClientError] = { subscribe(eventTypeName, parameters, listener)(deserializer(typeRef)) } - def subscribe[T <: Event](eventTypeName: String, params: StreamParameters, listener: Listener[T])(implicit des: Deserializer[EventStreamBatch[T]]): Future[Option[ClientError]] = + def subscribe[T <: Event](eventTypeName: String, params: StreamParameters, listener: Listener[T])(implicit des: Deserializer[EventStreamBatch[T]]): Option[ClientError] = (eventTypeName, params, listener) match { case (_, _, listener) if listener == null => logger.info("listener is null") - Future.successful(Option(ClientError("Listener may not be empty(null)!", None))) + Some(ClientError("Listener may not be empty(null)!", None)) case (eventType, _, _) if Option(eventType).isEmpty || eventType == "" => logger.info("eventType is null") - Future.successful(Option(ClientError("Eventype may not be empty(null)!", None))) + Some(ClientError("Eventype may not be empty(null)!", None)) case (eventType, StreamParameters(cursor, _, _, _, _, _, _), listener) if Option(eventType).isDefined => val url = URI_EVENTS_OF_EVENT_TYPE.format(eventType) @@ -119,7 +119,6 @@ class ClientImpl(connection: Connection, subscriber: SubscriptionHandler, charSe val finalUrl = withUrl(url, Some(params)) val eventHandler: EventHandler = new EventHandlerImpl[EmptyJavaEvent, T](Right((des, listener))) subscriber.subscribe(eventTypeName, finalUrl, cursor, eventHandler) - Future.successful(None) } def unsubscribe[T <: Event](eventTypeName: String, partition: Option[String], listener: Listener[T]): Future[Option[ClientError]] = { diff --git a/client/src/test/scala/org/zalando/nakadi/client/java/JavaClientTest.scala b/client/src/test/scala/org/zalando/nakadi/client/java/JavaClientTest.scala index 3bc1e97..fd5ae91 100644 --- a/client/src/test/scala/org/zalando/nakadi/client/java/JavaClientTest.scala +++ b/client/src/test/scala/org/zalando/nakadi/client/java/JavaClientTest.scala @@ -10,19 +10,35 @@ import org.scalatest.mock.MockitoSugar._ import scala.concurrent.Future import org.mockito.Mockito.reset import org.mockito.Mockito.when +import org.mockito.Matchers.any +import org.mockito.Matchers.anyString +import org.mockito.Matchers.anyObject +import org.mockito.Mockito.reset +import org.mockito.Mockito.times +import org.mockito.Mockito.verify +import org.mockito.Mockito.when import org.mockito.Matchers.anyString import java.util.Optional import akka.http.scaladsl.model.StatusCodes import akka.http.scaladsl.model.HttpProtocols import akka.http.scaladsl.model.HttpEntity import akka.http.scaladsl.model.ContentTypes +import org.zalando.nakadi.client.Deserializer +import org.zalando.nakadi.client.java.model._ +import org.zalando.nakadi.client.scala.model.{ Cursor => SCursor } +import org.zalando.nakadi.client.java._ +import org.mockito.ArgumentCaptor +import org.zalando.nakadi.client.scala.EventHandler +import org.scalatest.mock.MockitoSugar -class JavaClientTest extends WordSpec with Matchers with BeforeAndAfter { +class JavaClientTest extends WordSpec with Matchers with BeforeAndAfter with MockitoSugar { val connection = mock[Connection] val subscriptionHandler = mock[SubscriptionHandler] val clientHandler = new JavaClientHandlerImpl(connection, subscriptionHandler) val client = new ClientImpl(clientHandler) val eventTypeName = "EventTypeName" + val listener = mock[Listener[Event]] + val deserialzer = mock[Deserializer[EventStreamBatch[Event]]] before { reset(connection, subscriptionHandler) @@ -30,14 +46,69 @@ class JavaClientTest extends WordSpec with Matchers with BeforeAndAfter { "Java Client" should { - "map a 404 to an Empty Optional" in { + "Map a 404 to an empty optional" in { val headers = Nil val entity = HttpEntity(ContentTypes.`application/json`, "{}") val response = new HttpResponse(StatusCodes.NotFound, headers, entity, HttpProtocols.`HTTP/1.1`) val futureResponse = Future.successful(response) when(connection.get(anyString)).thenReturn(futureResponse) - val result = client.getEventType(eventTypeName).get - result shouldBe Optional.empty() + + //For an event + val result1 = client.getEventType(eventTypeName).get + result1 shouldBe Optional.empty() + + //For enrichment strategies + val result2 = client.getEnrichmentStrategies().get + result2 shouldBe Optional.empty() + + } + + "Pass the streamParameters correctly to the subscriptionHandler" in { + val partition = "partition" + val offset = "81237" + + val cursor = Optional.of(new Cursor(partition, offset)) + val batchLimit: Optional[Integer] = Optional.of(7162); + val streamLimit: Optional[Integer] = Optional.of(817625); + val batchFlushTimeout: Optional[Integer] = Optional.of(92837); + val streamTimeout: Optional[Integer] = Optional.of(22871); + val streamKeepAliveLimit: Optional[Integer] = Optional.of(9928378); + val flowId: Optional[String] = Optional.of("X-Flow-ID"); + + val parameters = new StreamParameters( + cursor, + batchLimit, + streamLimit, + batchFlushTimeout, + streamTimeout, + streamKeepAliveLimit, + flowId) + + when(subscriptionHandler.subscribe(anyString, anyString, any[Option[SCursor]], any[EventHandler])).thenReturn(None) + + client.subscribe[Event](eventTypeName, parameters, listener, deserialzer) shouldBe None + + val eventTypeNameCap = ArgumentCaptor.forClass(classOf[String]) + val endpointCap = ArgumentCaptor.forClass(classOf[String]) + val cursorCap = ArgumentCaptor.forClass(classOf[Option[SCursor]]) + val eventHandlerCap = ArgumentCaptor.forClass(classOf[EventHandler]) + + verify(subscriptionHandler, times(1)).subscribe( + eventTypeNameCap.capture(), + endpointCap.capture(), + cursorCap.capture(), + eventHandlerCap.capture()) + + eventTypeNameCap.getValue shouldBe eventTypeName + cursorCap.getValue shouldBe Some(SCursor(partition, offset)) + val urlResult = "/event-types/EventTypeName/events?batch_limit=%s&stream_limit=%s&batch_flush_timeout=%s&stream_timeout=%s&stream_keep_alive_limit=%s" + .format(batchLimit.get, + streamLimit.get, + batchFlushTimeout.get, + streamTimeout.get, + streamKeepAliveLimit.get) + endpointCap.getValue shouldBe urlResult + } } } \ No newline at end of file diff --git a/client/src/test/scala/org/zalando/nakadi/client/scala/ScalaClientTest.scala b/client/src/test/scala/org/zalando/nakadi/client/scala/ScalaClientTest.scala index 68b7a3a..658e432 100644 --- a/client/src/test/scala/org/zalando/nakadi/client/scala/ScalaClientTest.scala +++ b/client/src/test/scala/org/zalando/nakadi/client/scala/ScalaClientTest.scala @@ -22,16 +22,23 @@ import akka.http.scaladsl.model.HttpProtocols import akka.http.scaladsl.model.HttpEntity import akka.http.scaladsl.model.ContentTypes import org.zalando.nakadi.client.scala.model.JacksonJsonMarshaller +import org.zalando.nakadi.client.scala.model._ +import org.zalando.nakadi.client.scala._ import org.zalando.nakadi.client.utils.Uri import org.zalando.nakadi.client.handler.SubscriptionHandlerImpl import org.zalando.nakadi.client.handler.SubscriptionHandler +import org.mockito.ArgumentCaptor +import org.zalando.nakadi.client.Deserializer class ScalaClientTest extends WordSpec with Matchers with MockitoSugar with BeforeAndAfter { import JacksonJsonMarshaller._ import Uri._ private var connection: Connection = mock[Connection] - private var subscriber: SubscriptionHandler = mock[SubscriptionHandler] - private val client: Client = new ClientImpl(connection, subscriber) + private var subscriptionHandler: SubscriptionHandler = mock[SubscriptionHandler] + private val client: Client = new ClientImpl(connection, subscriptionHandler) + private val listener = mock[Listener[Event]] + private val deserialzer = mock[Deserializer[EventStreamBatch[Event]]] + val eventTypeName = "EventTypeName" before { reset(connection) @@ -44,17 +51,56 @@ class ScalaClientTest extends WordSpec with Matchers with MockitoSugar with Befo val response = new HttpResponse(StatusCodes.NotFound, headers, entity, HttpProtocols.`HTTP/1.1`) val futureResponse = Future.successful(response) when(connection.get(anyString)).thenReturn(futureResponse) - val result = Await.result(client.getEventType(eventTypeName),5.seconds) + val result = Await.result(client.getEventType(eventTypeName), 5.seconds) result shouldBe Right(None) } - "marshall an object when receiving http a 200 with valid payload" in { - } + "Pass the streamParameters correctly to the subscriptionHandler" in { + val partition = "partition" + val offset = "81237" - "catch the marshalling exception when receiving a 200 with invalid payload" in { - } - "return a ClientError receiving a redirection (300-399)" in { + val cursor = Some(Cursor(partition, offset)) + val batchLimit:Option[Integer] = Some(7162); + val streamLimit:Option[Integer] = Some(817625); + val batchFlushTimeout:Option[Integer] = Some(92837); + val streamTimeout:Option[Integer] = Some(22871); + val streamKeepAliveLimit:Option[Integer] = Some(9928378); + val flowId = Some("X-Flow-ID"); + + when(subscriptionHandler.subscribe(anyString, anyString, any[Option[Cursor]], any[EventHandler])).thenReturn(None) + val eventTypeNameCap = ArgumentCaptor.forClass(classOf[String]) + val endpointCap = ArgumentCaptor.forClass(classOf[String]) + val cursorCap = ArgumentCaptor.forClass(classOf[Option[Cursor]]) + val eventHandlerCap = ArgumentCaptor.forClass(classOf[EventHandler]) + + val parameters = StreamParameters( + cursor, + batchLimit, + streamLimit, + batchFlushTimeout, + streamTimeout, + streamKeepAliveLimit, + flowId) + + client.subscribe[Event](eventTypeName, parameters, listener)(deserialzer) shouldBe None + + verify(subscriptionHandler, times(1)).subscribe( + eventTypeNameCap.capture(), + endpointCap.capture(), + cursorCap.capture(), + eventHandlerCap.capture()) + + eventTypeNameCap.getValue shouldBe eventTypeName + cursorCap.getValue shouldBe Some(Cursor(partition, offset)) + val urlResult = "/event-types/EventTypeName/events?batch_limit=%s&stream_limit=%s&batch_flush_timeout=%s&stream_timeout=%s&stream_keep_alive_limit=%s" + .format(batchLimit.get, + streamLimit.get, + batchFlushTimeout.get, + streamTimeout.get, + streamKeepAliveLimit.get) + endpointCap.getValue shouldBe urlResult } + } } \ No newline at end of file From 6333c0ea1cf858211b6bc38a35018c07aa24df03 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Tue, 14 Jun 2016 14:15:55 +0200 Subject: [PATCH 085/183] techjira:LAAS-60 Development release 2.0.0-pre-alpha.14 --- .../nakadi/client/scala/SimpleEventTest.scala | 13 +++++-------- project/Build.scala | 2 +- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/it/src/test/scala/org/zalando/nakadi/client/scala/SimpleEventTest.scala b/it/src/test/scala/org/zalando/nakadi/client/scala/SimpleEventTest.scala index a70fd29..f60fc13 100644 --- a/it/src/test/scala/org/zalando/nakadi/client/scala/SimpleEventTest.scala +++ b/it/src/test/scala/org/zalando/nakadi/client/scala/SimpleEventTest.scala @@ -11,7 +11,6 @@ import org.zalando.nakadi.client.scala.test.factory.events.SimpleEventListener class SimpleEventTest extends WordSpec with Matchers { import org.scalatest.Matchers._ - import ClientFactory._ import JacksonJsonMarshaller._ import MySimpleEvent._ @@ -21,32 +20,31 @@ class SimpleEventTest extends WordSpec with Matchers { val listener = new SimpleEventListener() "404 should be handled graciously, by retuning None" in { - val it = new EventIntegrationHelper(null, client) + val eventGenerator = new DefaultMySimpleEventGenerator() { + def eventTypeId = s"SimpleEventIntegrationTest-Handle-404-Graciously" + } + val it = new EventIntegrationHelper(eventGenerator, client) it.getEventType("non-existing-event-type-name") match { case Some(_) => fail("Should not fail, because eventType was not created yet!!") case None => } - } "Validate Published nr of SimpleEvents" in { - val eventGenerator = new DefaultMySimpleEventGenerator() { def eventTypeId = s"SimpleEventIntegrationTest-Validate-Published-Events-$nrOfEvents" } val it = new EventIntegrationHelper(eventGenerator, client) val cursor = Some(Cursor("0", "BEGIN")) - it.createEventType() val events = it.publishEvents(nrOfEvents) - client.subscribe(eventGenerator.eventTypeName, StreamParameters(cursor = cursor), listener) val receivedEvents = listener.waitToReceive(nrOfEvents) - receivedEvents.size shouldBe events.size receivedEvents shouldBe events } + "Validate created EventType" in { val eventGenerator = new DefaultMySimpleEventGenerator() { def eventTypeId = s"SimpleEventIntegrationTest-Validate-Created-Events-$nrOfEvents" @@ -66,7 +64,6 @@ class SimpleEventTest extends WordSpec with Matchers { eventType.validationStrategies shouldBe null eventType.enrichmentStrategies shouldBe it.eventType.enrichmentStrategies eventType.partitionKeyFields shouldBe it.eventType.partitionKeyFields - } } diff --git a/project/Build.scala b/project/Build.scala index f01e7c5..8aa6d4d 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -57,7 +57,7 @@ lazy val client = withDefaults( project.settings( name := projectName, organization := "org.zalando.nakadi.client", - version := "2.0.0-pre-alpha.13", + version := "2.0.0-pre-alpha.14", crossPaths := false, scalaVersion := "2.11.8", publishTo := whereToPublishTo(isSnapshot.value), From 73c5d65ea3673b7218ba60a56011ab07c5eb6937 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Tue, 14 Jun 2016 18:18:11 +0200 Subject: [PATCH 086/183] techjira:LAAS-60 Added Integration tests for updates --- .../nakadi/client/scala/Connection.scala | 7 +- .../nakadi/client/java/JavaClientTest.scala | 2 +- examples/scalajs-play-core-react/.gitignore | 54 - .../scalajs-play-core-react/LICENSE-2.0.txt | 202 - examples/scalajs-play-core-react/README.md | 30 - examples/scalajs-play-core-react/activator | 334 - .../activator-launch-1.3.7.jar | Bin 1213545 -> 0 bytes .../scalajs-play-core-react/client/.gitignore | 1 - .../client/src/main/scala/SPA.scala | 13 - .../src/main/scala/component/Menu.scala | 13 - .../core/material/MaterialComponent.scala | 44 - .../main/scala/core/material/package.scala | 16 - .../main/scala/core/reactive/RxObserver.scala | 24 - .../scala/core/service/AbstractClient.scala | 31 - .../client/src/main/scala/layout/Layout.scala | 13 - .../src/main/scala/layout/MainLayout.scala | 10 - .../scala/layout/ReactLayoutWithMenu.scala | 58 - .../src/main/scala/pages/MyScreenPage.scala | 34 - .../main/scala/router/ApplicationRouter.scala | 31 - .../src/main/scala/service/SampleClient.scala | 6 - .../project/Build.scala | 58 - .../project/Settings.scala | 129 - .../project/build.properties | 4 - .../project/plugins.sbt | 13 - .../server/src/main/assets/main.less | 0 .../src/main/assets/material/material.css | 9751 ----------------- .../src/main/assets/material/material.js | 3865 ------- .../src/main/assets/material/material.min.css | 9 - .../main/assets/material/material.min.css.map | 1 - .../src/main/assets/material/material.min.js | 10 - .../main/assets/material/material.min.js.map | 1 - .../src/main/resources/application.conf | 4 - .../main/scala/GlobalApplicationLoader.scala | 35 - .../scala/controllers/ApiController.scala | 20 - .../scala/controllers/DemoController.scala | 11 - .../scala/controllers/ServiceController.scala | 38 - .../src/main/scala/modules/Controllers.scala | 13 - .../src/main/scala/modules/Service.scala | 8 - .../main/scala/service/SampleApiImpl.scala | 10 - .../src/main/twirl/views/index.scala.html | 20 - .../shared/.js/.gitignore | 22 - .../src/main/scala/demo/SampleApi.scala | 5 - .../src/main/scala/demo/SampleApi.sjsir | Bin 323 -> 0 bytes .../nakadi/client/scala/ClientFactory.scala | 8 +- .../test/factory/EventIntegrationHelper.scala | 34 +- .../client/ClientSubscriptionTest.scala | 56 - .../client/EventTypesIntegrationTest.scala | 119 - .../nakadi/client/KlientIntegrationTest.scala | 84 - .../nakadi/client/scala/SimpleEventTest.scala | 47 +- 49 files changed, 77 insertions(+), 15221 deletions(-) delete mode 100644 examples/scalajs-play-core-react/.gitignore delete mode 100644 examples/scalajs-play-core-react/LICENSE-2.0.txt delete mode 100644 examples/scalajs-play-core-react/README.md delete mode 100755 examples/scalajs-play-core-react/activator delete mode 100644 examples/scalajs-play-core-react/activator-launch-1.3.7.jar delete mode 100644 examples/scalajs-play-core-react/client/.gitignore delete mode 100644 examples/scalajs-play-core-react/client/src/main/scala/SPA.scala delete mode 100644 examples/scalajs-play-core-react/client/src/main/scala/component/Menu.scala delete mode 100644 examples/scalajs-play-core-react/client/src/main/scala/core/material/MaterialComponent.scala delete mode 100644 examples/scalajs-play-core-react/client/src/main/scala/core/material/package.scala delete mode 100644 examples/scalajs-play-core-react/client/src/main/scala/core/reactive/RxObserver.scala delete mode 100644 examples/scalajs-play-core-react/client/src/main/scala/core/service/AbstractClient.scala delete mode 100644 examples/scalajs-play-core-react/client/src/main/scala/layout/Layout.scala delete mode 100644 examples/scalajs-play-core-react/client/src/main/scala/layout/MainLayout.scala delete mode 100644 examples/scalajs-play-core-react/client/src/main/scala/layout/ReactLayoutWithMenu.scala delete mode 100644 examples/scalajs-play-core-react/client/src/main/scala/pages/MyScreenPage.scala delete mode 100644 examples/scalajs-play-core-react/client/src/main/scala/router/ApplicationRouter.scala delete mode 100644 examples/scalajs-play-core-react/client/src/main/scala/service/SampleClient.scala delete mode 100644 examples/scalajs-play-core-react/project/Build.scala delete mode 100644 examples/scalajs-play-core-react/project/Settings.scala delete mode 100644 examples/scalajs-play-core-react/project/build.properties delete mode 100644 examples/scalajs-play-core-react/project/plugins.sbt delete mode 100644 examples/scalajs-play-core-react/server/src/main/assets/main.less delete mode 100644 examples/scalajs-play-core-react/server/src/main/assets/material/material.css delete mode 100644 examples/scalajs-play-core-react/server/src/main/assets/material/material.js delete mode 100644 examples/scalajs-play-core-react/server/src/main/assets/material/material.min.css delete mode 100644 examples/scalajs-play-core-react/server/src/main/assets/material/material.min.css.map delete mode 100644 examples/scalajs-play-core-react/server/src/main/assets/material/material.min.js delete mode 100644 examples/scalajs-play-core-react/server/src/main/assets/material/material.min.js.map delete mode 100644 examples/scalajs-play-core-react/server/src/main/resources/application.conf delete mode 100644 examples/scalajs-play-core-react/server/src/main/scala/GlobalApplicationLoader.scala delete mode 100644 examples/scalajs-play-core-react/server/src/main/scala/controllers/ApiController.scala delete mode 100644 examples/scalajs-play-core-react/server/src/main/scala/controllers/DemoController.scala delete mode 100644 examples/scalajs-play-core-react/server/src/main/scala/controllers/ServiceController.scala delete mode 100644 examples/scalajs-play-core-react/server/src/main/scala/modules/Controllers.scala delete mode 100644 examples/scalajs-play-core-react/server/src/main/scala/modules/Service.scala delete mode 100644 examples/scalajs-play-core-react/server/src/main/scala/service/SampleApiImpl.scala delete mode 100644 examples/scalajs-play-core-react/server/src/main/twirl/views/index.scala.html delete mode 100644 examples/scalajs-play-core-react/shared/.js/.gitignore delete mode 100644 examples/scalajs-play-core-react/shared/src/main/scala/demo/SampleApi.scala delete mode 100644 examples/scalajs-play-core-react/shared/src/main/scala/demo/SampleApi.sjsir delete mode 100644 it/src/test/scala/org/zalando/nakadi/client/ClientSubscriptionTest.scala delete mode 100644 it/src/test/scala/org/zalando/nakadi/client/EventTypesIntegrationTest.scala delete mode 100644 it/src/test/scala/org/zalando/nakadi/client/KlientIntegrationTest.scala diff --git a/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala index 1bd1ed9..1af65a7 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala @@ -128,13 +128,14 @@ sealed class ConnectionImpl(val host: String, val port: Int, val tokenProvider: } def put[T](endpoint: String, model: T)(implicit serializer: Serializer[T]): Future[HttpResponse] = { - logger.info("Get: {}", endpoint) - executeCall(withHttpRequest(endpoint, HttpMethods.GET, Nil, tokenProvider, None)) + val entity = serializer.to(model) + logger.info("Put: {} - Data: {}", endpoint, entity) + executeCall(withHttpRequest(endpoint, HttpMethods.PUT, Nil, tokenProvider, None)) } def post[T](endpoint: String, model: T)(implicit serializer: Serializer[T]): Future[HttpResponse] = { val entity = serializer.to(model) - logger.info("POST URL:{} - DATA:{}", endpoint, entity) + logger.info("Post:{} - Data:{}", endpoint, entity) executeCall(withHttpRequestAndPayload(endpoint, entity, HttpMethods.POST, tokenProvider)) } diff --git a/client/src/test/scala/org/zalando/nakadi/client/java/JavaClientTest.scala b/client/src/test/scala/org/zalando/nakadi/client/java/JavaClientTest.scala index fd5ae91..015f0b8 100644 --- a/client/src/test/scala/org/zalando/nakadi/client/java/JavaClientTest.scala +++ b/client/src/test/scala/org/zalando/nakadi/client/java/JavaClientTest.scala @@ -86,7 +86,7 @@ class JavaClientTest extends WordSpec with Matchers with BeforeAndAfter with Moc when(subscriptionHandler.subscribe(anyString, anyString, any[Option[SCursor]], any[EventHandler])).thenReturn(None) - client.subscribe[Event](eventTypeName, parameters, listener, deserialzer) shouldBe None + client.subscribe[Event](eventTypeName, parameters, listener, deserialzer) shouldBe Optional.empty() val eventTypeNameCap = ArgumentCaptor.forClass(classOf[String]) val endpointCap = ArgumentCaptor.forClass(classOf[String]) diff --git a/examples/scalajs-play-core-react/.gitignore b/examples/scalajs-play-core-react/.gitignore deleted file mode 100644 index 5f3abf9..0000000 --- a/examples/scalajs-play-core-react/.gitignore +++ /dev/null @@ -1,54 +0,0 @@ -# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm - -*.iml - -## Directory-based project format: -.idea/ -# if you remove the above rule, at least ignore the following: - -# User-specific stuff: -# .idea/workspace.xml -# .idea/tasks.xml -# .idea/dictionaries -# .idea/shelf - -# Sensitive or high-churn files: -# .idea/dataSources.ids -# .idea/dataSources.xml -# .idea/sqlDataSources.xml -# .idea/dynamic.xml -# .idea/uiDesigner.xml - -# Gradle: -# .idea/gradle.xml -# .idea/libraries - -# Mongo Explorer plugin: -# .idea/mongoSettings.xml - -## File-based project format: -*.ipr -*.iws - -## Plugin-specific files: - -# IntelliJ -/out/ - -# mpeltonen/sbt-idea plugin -.idea_modules/ - -# JIRA plugin -atlassian-ide-plugin.xml - -# Crashlytics plugin (for Android Studio and IntelliJ) -com_crashlytics_export_strings.xml -crashlytics.properties -crashlytics-build.properties -fabric.properties -target/ -lib_managed/ -src_managed/ -project/boot/ -.history -.cache diff --git a/examples/scalajs-play-core-react/LICENSE-2.0.txt b/examples/scalajs-play-core-react/LICENSE-2.0.txt deleted file mode 100644 index d645695..0000000 --- a/examples/scalajs-play-core-react/LICENSE-2.0.txt +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/examples/scalajs-play-core-react/README.md b/examples/scalajs-play-core-react/README.md deleted file mode 100644 index e09bba0..0000000 --- a/examples/scalajs-play-core-react/README.md +++ /dev/null @@ -1,30 +0,0 @@ -# ScalaJS and Play base project with React and MDL - -This branch using [ScalaJS - React](https://github.com/japgolly/scalajs-react) with [MDL](http://www.getmdl.io/) in the client side - -This project using [Macwire](https://github.com/adamw/macwire) for dependency injection and ignore Play layout. - -The frontend-backend communication is based is [autowire](https://github.com/lihaoyi/autowire) and [boopickle](https://github.com/ochrons/boopickle) - -## The application structure - -The application have three parts. - -* shared -* server -* client - -#### Shared - -The shared directory contains the frontend and backend shared codes. We put here the communication objects and the API traits. - -#### Server - -This is the play server directory. The application override the default play application loader because it's using MacWire for dependency injection and [Play SIRD](https://www.playframework.com/documentation/2.4.x/ScalaSirdRouter) for the routing. - -##### Depth - -The application loading started in the `GlobalApplicationLoader` class. The `BuiltInComponentFromContextWithPlayWorkaround` class is a workaround to Play, because Play doesn't handle well the big file upload, when you not using guice for dependency injection. -The Routing is in the `ApplicationComponents` class, this file depend to the `Controller` and `Service` trait which contains the dependent classes. - -The `ApiController` methods will handle the different API calls. Each api entry point must exists in the `ApplicationComponents` router (See sampleApi call) diff --git a/examples/scalajs-play-core-react/activator b/examples/scalajs-play-core-react/activator deleted file mode 100755 index df609ab..0000000 --- a/examples/scalajs-play-core-react/activator +++ /dev/null @@ -1,334 +0,0 @@ -#!/usr/bin/env bash - -### ------------------------------- ### -### Helper methods for BASH scripts ### -### ------------------------------- ### - -realpath () { -( - TARGET_FILE="$1" - - cd "$(dirname "$TARGET_FILE")" - TARGET_FILE=$(basename "$TARGET_FILE") - - COUNT=0 - while [ -L "$TARGET_FILE" -a $COUNT -lt 100 ] - do - TARGET_FILE=$(readlink "$TARGET_FILE") - cd "$(dirname "$TARGET_FILE")" - TARGET_FILE=$(basename "$TARGET_FILE") - COUNT=$(($COUNT + 1)) - done - - if [ "$TARGET_FILE" == "." -o "$TARGET_FILE" == ".." ]; then - cd "$TARGET_FILE" - TARGET_FILEPATH= - else - TARGET_FILEPATH=/$TARGET_FILE - fi - - # make sure we grab the actual windows path, instead of cygwin's path. - if ! is_cygwin; then - echo "$(pwd -P)/$TARGET_FILE" - else - echo $(cygwinpath "$(pwd -P)/$TARGET_FILE") - fi -) -} - -# TODO - Do we need to detect msys? - -# Uses uname to detect if we're in the odd cygwin environment. -is_cygwin() { - local os=$(uname -s) - case "$os" in - CYGWIN*) return 0 ;; - *) return 1 ;; - esac -} - -# This can fix cygwin style /cygdrive paths so we get the -# windows style paths. -cygwinpath() { - local file="$1" - if is_cygwin; then - echo $(cygpath -w $file) - else - echo $file - fi -} - -# Make something URI friendly -make_url() { - url="$1" - local nospaces=${url// /%20} - if is_cygwin; then - echo "/${nospaces//\\//}" - else - echo "$nospaces" - fi -} - -# Detect if we should use JAVA_HOME or just try PATH. -get_java_cmd() { - if [[ -n "$JAVA_HOME" ]] && [[ -x "$JAVA_HOME/bin/java" ]]; then - echo "$JAVA_HOME/bin/java" - else - echo "java" - fi -} - -echoerr () { - echo 1>&2 "$@" -} -vlog () { - [[ $verbose || $debug ]] && echoerr "$@" -} -dlog () { - [[ $debug ]] && echoerr "$@" -} -execRunner () { - # print the arguments one to a line, quoting any containing spaces - [[ $verbose || $debug ]] && echo "# Executing command line:" && { - for arg; do - if printf "%s\n" "$arg" | grep -q ' '; then - printf "\"%s\"\n" "$arg" - else - printf "%s\n" "$arg" - fi - done - echo "" - } - - exec "$@" -} -addJava () { - dlog "[addJava] arg = '$1'" - java_args=( "${java_args[@]}" "$1" ) -} -addApp () { - dlog "[addApp] arg = '$1'" - sbt_commands=( "${app_commands[@]}" "$1" ) -} -addResidual () { - dlog "[residual] arg = '$1'" - residual_args=( "${residual_args[@]}" "$1" ) -} -addDebugger () { - addJava "-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=$1" -} -addConfigOpts () { - dlog "[addConfigOpts] arg = '$*'" - for item in $* - do - addJava "$item" - done -} -# a ham-fisted attempt to move some memory settings in concert -# so they need not be messed around with individually. -get_mem_opts () { - local mem=${1:-1024} - local meta=$(( $mem / 4 )) - (( $meta > 256 )) || meta=256 - (( $meta < 1024 )) || meta=1024 - - # default is to set memory options but this can be overridden by code section below - memopts="-Xms${mem}m -Xmx${mem}m" - if [[ "${java_version}" > "1.8" ]]; then - extmemopts="-XX:MetaspaceSize=64m -XX:MaxMetaspaceSize=${meta}m" - else - extmemopts="-XX:PermSize=64m -XX:MaxPermSize=${meta}m" - fi - - if [[ "${java_opts}" == *-Xmx* ]] || [[ "${java_opts}" == *-Xms* ]] || [[ "${java_opts}" == *-XX:MaxPermSize* ]] || [[ "${java_opts}" == *-XX:ReservedCodeCacheSize* ]] || [[ "${java_opts}" == *-XX:MaxMetaspaceSize* ]]; then - # if we detect any of these settings in ${java_opts} we need to NOT output our settings. - # The reason is the Xms/Xmx, if they don't line up, cause errors. - memopts="" - extmemopts="" - fi - - echo "${memopts} ${extmemopts}" -} -require_arg () { - local type="$1" - local opt="$2" - local arg="$3" - if [[ -z "$arg" ]] || [[ "${arg:0:1}" == "-" ]]; then - die "$opt requires <$type> argument" - fi -} -is_function_defined() { - declare -f "$1" > /dev/null -} - -# If we're *not* running in a terminal, and we don't have any arguments, then we need to add the 'ui' parameter -detect_terminal_for_ui() { - [[ ! -t 0 ]] && [[ "${#residual_args}" == "0" ]] && { - addResidual "ui" - } - # SPECIAL TEST FOR MAC - [[ "$(uname)" == "Darwin" ]] && [[ "$HOME" == "$PWD" ]] && [[ "${#residual_args}" == "0" ]] && { - echo "Detected MAC OSX launched script...." - echo "Swapping to UI" - addResidual "ui" - } -} - -# Processes incoming arguments and places them in appropriate global variables. called by the run method. -process_args () { - while [[ $# -gt 0 ]]; do - case "$1" in - -h|-help) usage; exit 1 ;; - -v|-verbose) verbose=1 && shift ;; - -d|-debug) debug=1 && shift ;; - -mem) require_arg integer "$1" "$2" && app_mem="$2" && shift 2 ;; - -jvm-debug) - if echo "$2" | grep -E ^[0-9]+$ > /dev/null; then - addDebugger "$2" && shift - else - addDebugger 9999 - fi - shift ;; - -java-home) require_arg path "$1" "$2" && java_cmd="$2/bin/java" && shift 2 ;; - -D*) addJava "$1" && shift ;; - -J*) addJava "${1:2}" && shift ;; - *) addResidual "$1" && shift ;; - esac - done - - is_function_defined process_my_args && { - myargs=("${residual_args[@]}") - residual_args=() - process_my_args "${myargs[@]}" - } -} - -# Actually runs the script. -run() { - # TODO - check for sane environment - - # process the combined args, then reset "$@" to the residuals - process_args "$@" - detect_terminal_for_ui - set -- "${residual_args[@]}" - argumentCount=$# - - #check for jline terminal fixes on cygwin - if is_cygwin; then - stty -icanon min 1 -echo > /dev/null 2>&1 - addJava "-Djline.terminal=jline.UnixTerminal" - addJava "-Dsbt.cygwin=true" - fi - - # run sbt - execRunner "$java_cmd" \ - "-Dactivator.home=$(make_url "$activator_home")" \ - $(get_mem_opts $app_mem) \ - ${java_opts[@]} \ - ${java_args[@]} \ - -jar "$app_launcher" \ - "${app_commands[@]}" \ - "${residual_args[@]}" - - local exit_code=$? - if is_cygwin; then - stty icanon echo > /dev/null 2>&1 - fi - exit $exit_code -} - -# Loads a configuration file full of default command line options for this script. -loadConfigFile() { - cat "$1" | sed '/^\#/d' -} - -### ------------------------------- ### -### Start of customized settings ### -### ------------------------------- ### -usage() { - cat < [options] - - Command: - ui Start the Activator UI - new [name] [template-id] Create a new project with [name] using template [template-id] - list-templates Print all available template names - -h | -help Print this message - - Options: - -v | -verbose Make this runner chattier - -d | -debug Set sbt log level to debug - -mem Set memory options (default: $sbt_mem, which is $(get_mem_opts $sbt_mem)) - -jvm-debug Turn on JVM debugging, open at the given port. - - # java version (default: java from PATH, currently $(java -version 2>&1 | grep version)) - -java-home Alternate JAVA_HOME - - # jvm options and output control - -Dkey=val Pass -Dkey=val directly to the java runtime - -J-X Pass option -X directly to the java runtime - (-J is stripped) - - # environment variables (read from context) - JAVA_OPTS Environment variable, if unset uses "" - SBT_OPTS Environment variable, if unset uses "" - ACTIVATOR_OPTS Environment variable, if unset uses "" - -In the case of duplicated or conflicting options, the order above -shows precedence: environment variables lowest, command line options highest. -EOM -} - -### ------------------------------- ### -### Main script ### -### ------------------------------- ### - -declare -a residual_args -declare -a java_args -declare -a app_commands -declare -r real_script_path="$(realpath "$0")" -declare -r activator_home="$(realpath "$(dirname "$real_script_path")")" -declare -r app_version="1.3.7" - -declare -r app_launcher="${activator_home}/activator-launch-${app_version}.jar" -declare -r script_name=activator -java_cmd=$(get_java_cmd) -declare -r java_opts=( "${ACTIVATOR_OPTS[@]}" "${SBT_OPTS[@]}" "${JAVA_OPTS[@]}" "${java_opts[@]}" ) -userhome="$HOME" -if is_cygwin; then - # cygwin sets home to something f-d up, set to real windows homedir - userhome="$USERPROFILE" -fi -declare -r activator_user_home_dir="${userhome}/.activator" -declare -r java_opts_config_home="${activator_user_home_dir}/activatorconfig.txt" -declare -r java_opts_config_version="${activator_user_home_dir}/${app_version}/activatorconfig.txt" - -# Now check to see if it's a good enough version -declare -r java_version=$("$java_cmd" -version 2>&1 | awk -F '"' '/version/ {print $2}') -if [[ "$java_version" == "" ]]; then - echo - echo No java installations was detected. - echo Please go to http://www.java.com/getjava/ and download - echo - exit 1 -elif [[ ! "$java_version" > "1.6" ]]; then - echo - echo The java installation you have is not up to date - echo Activator requires at least version 1.6+, you have - echo version $java_version - echo - echo Please go to http://www.java.com/getjava/ and download - echo a valid Java Runtime and install before running Activator. - echo - exit 1 -fi - -# if configuration files exist, prepend their contents to the java args so it can be processed by this runner -# a "versioned" config trumps one on the top level -if [[ -f "$java_opts_config_version" ]]; then - addConfigOpts $(loadConfigFile "$java_opts_config_version") -elif [[ -f "$java_opts_config_home" ]]; then - addConfigOpts $(loadConfigFile "$java_opts_config_home") -fi - -run "$@" diff --git a/examples/scalajs-play-core-react/activator-launch-1.3.7.jar b/examples/scalajs-play-core-react/activator-launch-1.3.7.jar deleted file mode 100644 index c37f87925a03a91e48d553edfa786c771da9b388..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1213545 zcmbSy1yCGX(k|}q?(Xh^0Kwf|1|NdECOCnh!QI{62@+g_1b4UK?vLDickk~1Z`k+V zR8ciWb)VDS=j*SJ^eD=JL%@N6z<_|@TH+m+fRzMe1hgWR; z-HfI#rZ!Hm=#l?s#y|B;Y+Y@vYyl>(3_$kk01l>(wpK2$h3a(!988@YfG@VZV!-cW z2DEW>0$5qSqKW?-)2(eyoULA)lInFu6H`ZH2cVsk?Q080^!gaU|DTxuW%!HVPR@?6 zIR3kk**P0o0Ua$~(fnP=98B$O9bUs3@o(JyrRWHJ3Ds)}_&a<5JGuyeJMDkP)$d$& zGzB;qzZRq4&FN_B%sjG1ZC@J4t!-D|HDLoX#5vm`<}&;iExlgTwzV;{0vf-D*8h;4|1j7J;AHCfn(lu9 z>pyjEjo-iG@qfsXe`s0*oL&mrYYvnCK3p~cYg5OUiuf9(z2dPQz`^nVcj~`%t=V32 z`#)5Wf4KdZx|{WD=z7KU|4K`*%>94GOw_-R`oHL#0smji$v*<{FM~KcycUF4@ctJ| zz6Ku3*WCD{f;%w(mmGfuyf0L1ZvHBQUQvG8_BaAwJ`x0 zy}DZep)UNP{IbYSuR4kIzk~dT;_D0hYt!p!^cvwn{gN;SSOH#%5$Zo>jBTx~OpTph z59u$S|8vNH5jVEAd0C9tEcuUW=B75L4nX5qjQbVQ|1}%X+S=LazbwUnobSJ@A^yvJ z)^>o`Jop z7kV*2TvyCG9Iq|VLNky@~{ z0^Z)$+tbzYa*gqW-k6HU(BUG(*!mcPIzVpCHr!KB(zHT58MTlR;Zn5$ zk#?}#>9B>g8~H>$K?}FhSmToBHBDw?1q^z*kCh%R_yKdSgK&zlSo>=vn>;_mrk?X3 zTT>nBgVG2fteWf4uv~OJ`WHx5-ddZImrGrQpcw~4Qyj)cQA)vT`{1r>G52kF3= z*djs^mIC2O%ixIdl$$#yR~w8qI;$sY%l29+o-=0;0DTT_-A!)h4142|_1q||1I6QO zof7QKrs5Lz>mmkc$~7AGAlF(aXmnZpPp+g;iS$6&Xlz9zWcgnErfP8jsTK(qOlh3gOF76V+ZOM+3QM`tT~e3z zO=g0mYz(2?c)IX) z;E1M8)Ois!b^1kwb(q(|kTQu(7rI9)Q8Ed;QnA-#KDTRBo|99k7+)m}z-bKbLszc* zqgkME^GARzZnPpk?6pzU3oCk&6Y^E$sFvCxhkf`&)JwM*x)|70xw7Yw;0wz0-UkmP z2{`3XrD25O5A;fhfyCnC@&ZF$9&zV#2WW1^`b&}?$jlCN#9~y++B!qGq~1MXM1spt z#au9dtGaEyWJzv2mTaxSQ?FC*dr)nw#h|so<`XX|wM2@7UZQ4Ogvy58$c-s$Mv$KY zc~>7#i5Wsx5
        EuG~YTNE?1LKb_E_i=ioj~7LSYk?VWj9RxaPYD@=>lC)fZYxPL z%4C+S7Vg7G>02_HP*A0nj%hg`Yi=75egQX#W#MtL?AR@tqiIm>8t z%y*{qL>t~Y9f_QY>%g-!(<%Mo^&~TWteJb5p-M^4D4%H%9PHh|a4VXKE36skcY?j6 zItGSVo8lS+iWUW(c_yUyE$eg27*+Hu;7>Aqpk(_{*xYYvg|*eI$uJ$DxeKSaP>?uX zRW(JVi=&@lU4+yH&FG`wZ;3Or;Uv8iHnXn9w_9{@Qr!af#h*+}_D2S|6>${x83ycW zi3)Tn=~fJlhar7wh`T};$&mX=qsX*%QBVFeSn(ZkG~l~9nctKZ^T9D$KH z&H^s|9p9DH7kxfO9**Z5K@VDCd_z|h7n4Mi-nq}EaN2J`T`%agmI1%ob_fsPmJx!= zuGm?xkSwuVXgIy`)e5=n@0se`pEIM9sUe8NRF#Uxl_Ux98U42Y2^)krKn1M(U`fVYt=KBKypgx(t=V#^I%970B{(OWTu-(wxSuw2H7 z*`b%DA(o!Ok99lPrjQd2#~cuR$$b4H@Uvyc&$ZZ(LG_>k{I{8(wjcuwBe|Tz1&~91 zEI~fpegsttBb;SLg%(h{DvdIvN=1Rf)Xa^a~Y zgB>Wtx3BU4=`61;H>aiZHe+m@hS1U={RaC8ZQgK_)&Lds$tU`qy24Ndb^y42@IeV< z;dct#;?xzR$a0uE8>V>9JE1EPq$oIJK0ebi9d%(F$|qTp9sKLhs+D97^}S6ACM*Kt zK=Dn52V4PWG4gAhAK*lFN;*tnhEktx!Hv}C@a)v!=wskS7Y`i-t5tt9-XeyAb zvOW3HI5a92;@p>5bZbIdh~njnDWR$O5HN(7`izR%oMWC{yrOlt}xTG861Y?5$g z>a2&3hTXMEaCQpsMW6b41Zz zz=n6VXySRcN~Hw+nN#_3Co7@{#qUbk6^DB9O|z9}KqJls4Z6-XBd%yw>fWeneT^D8 zg(j+-<8+sbD+nGTG1CH%O(Ix!2ZGF0)ycRX34=PEyGnF4EZjJ0UWyQYB+2NmBSO(l zLLz>Sd6e*=S`ZAQDy{%kat!+@Qjy)Ml)TL44~r>EKlO{Rwhv=dHB%#xXNQ*vUykib zJdNjX-$Xf>-*3*v33nvP@>Vh2(xYQ)E5HiIVqPZF?*7oKA)}E>JvTEFC`4sD5#ELA zNtoqCcUU1pY=gZl=<<&FL$R=CgPz$8mbwaMK1MiBa3Dr(F9 z4dGi@J?hlXFHLfoC#vmLJw+0qK!I_;e-2-eZFAAclkI08Wsj$u&Ez`IGh z-K0e5*;AgUSr&yQom5VxeY#Ke6P4+(9pkI?j*xJtJN;_C(W&Coe1Il%951}Xv?ewq z);YcCqVE1SkCjSX+ypsbh#)rxEOAbqLb*N>fF3>i5qeZEMjffAV;oyxea`5z<>}dql0(eW>*M+WMR%vb%Kl!MSaF1{zDh_i!DF z;A-u0U4HRHV@sQsfn(j)LKl=R*4t+yt|_O3x7_TDnDX6_0_JkIoD+_Gc8KGFk%|wL z-$u6c1gxEqcuurM^pYi3xtc4fug>K*uQzVYZ6 z0ycVq-$p7TF=%a1gnG=_6%XF-ySnT0;PrvULw*n%$<%KMPY(Y+1f)s5-hn@DZDapL z`mG0V$0gkHbj^afBn?TuSDX(-ka@Ek=}sV2KyMQMZuo}fkn6T)k8xSA&c=p#LMg9! z8160plDoNCW3mDQQ+gXZvi4dPem7_fXa4*FRR-Y)zig{J{q;u?;wJ@W=j+ zd$Od(&@U@7JX zrYM0oSgz{zDZMX~=n)CSwfOL4>I(;oaSfNSD}XCJLj={Rdd^FyMuUJ{6<-VzL<^*o@_eZUL6qU8v|vGvcem95d7R5#wD)D<5Qubp{en`4NBrGvyJUq^p?;{COl9Z zr5@z5^H>1O>nU z6O=72=DY2m2?pkZ;qKDoezHG^JQYA zz`i}rA!_IEA9Qiy{@k>Vw26N254GI-o3Z+3POBKv51xfbe*uZL6#8J;C=}(%{-H zB->J6oC(u)X6T!HwFeOo|8$9l(ytR6f|A9h=E*?{(O*&Mm`;3eMz^-EX~-_fVIXwd zA8rK3+?H;+Vl!FARdadBoFC0ZC)s#$=K|lxi^5!gZ*#aKYP`4feneBvr!?pq%UCCR zN@K@}3I8F#V%H_(|KMAIbMq|u75tQ{rYtVG5XCRm0`VIHLD@nCirrh-Oa`Yu+pAlk z)cV2Eh3+}_GpM2rG)!&ndXV+YW34^|2*|I8;s1P4hyMGZUZ*1GxWI+dcCPW0@f4wi z&ju-N8zMUDQxF*@NNtXeAzHPfOaK(fq0$F$>?ncy6i4JQBu3YV_nKH&ZVuZ3>{a8B zklZsfAD>(tANSb%CzU+wraF2BgX0_`6=`>VFa_t`7ZI1w{R`|8e|-<0nF$X~%e0-W`OPgYo}#V55BU%XBPj%0)HZZkAWT6+NacGVaUU<`Fo8N6)x1vG9Rt0J_HIu zf+=AMtdP@yJ+Uwq6S!->?T9djUHgu}aaN;mZ)P*s7&vd@IVSwxO)ysYj=5pS7*QF)2bu*@IV4q zA>;)z5qmQS^4Pwu3S;5ktA3CSfH$(6kQ9e(r<7B2rFS`u6l=pX zSNIsZWM~FL8H#)&j|0u0F(aO^2n$O35vD?Im?&b>ax1MC*ydsV&UDljPa*fY@LGN3 z#{}p@D1a;?nWS!nbxwYi?={J3cb2rP>zn+F+eApM#XC`o%_NC8Qx(1uF2R>A3!Gi4J^LbsF$Zk z=8G{}KV0N-1G9oVs169NtyZo#ZEpZ%i^CU2R^tPHGKOMNxN1& zJKe(MkBFS4ap2Dpj%ax34TD1y3%=yEC7sC;_UY?b7OFEdwkJx#;fqdrjwWw6>$a>J zm+aBj#AQM`2?bjSl6kl5p9_G>4fZlQo<VEmWVZdTmEP+^oIR^BVjtrr$HQ&4=7(1Z^ip!g!pFt(xJO*ZVu#qub_I+J2}C}75Ski zN2+sNnd|-0*T#Z2dzLTTJj3OGF3!$@WTW>?`@?=T!$p1Mq6RMG=JPP{Oo}K4|Cq#J z@f-8A`4$kzOf?|85khhk#xzT~Ngep+glyP(vg#h2oMY*-j4&*Nn#xs(UAXVP4wEcN z4gcZr^Shu?HfG7Rg)}JkATF%lW&_rs+6s=^PPc_C^tSu-Z4BQE)6}U)!GghARUGtQ z7nTGy*#@v!Auu2W)@S5FbDW(5_^d4N9BOopvd{G9y-2L+3E&J+!G!z^=<6dVSPel} zFckDmD8w^6)$$2C4WdQiI@{JTX1N>p4x%FY8Y0of33t&_@;tUCk0fyVV@5I_JyK_$ z`arQ#I+_Z4#-8l)I@OB}Xcd~9Gs++~b?cCiE0X0HQ(wM$sKEJvs8vhDmzz-(w_3p| zEq#YUQJuo8h_4%yoV;Y<8v4ML<018)=^XQ^I*=xCnTk#(t7P_lh*=nytYtJeWlSw_ zGrxC%Zjd%$P=;!{H8F>u5ACB5jxxP^e8vyZt(ZngXzqF%_jtUwx}+4}omEfm2LTus zYg+|e0@$Y=UmYzB9r#-e>7F$FDIjY6%I4lsEIkCH;|MQHwnwOv8gRnr1=?^F+&43k z&B@x|`pi_{iJO*vkqTdDr%N;|VkKEc>EdC=qx_WK{(VOOx(=6DP<^@V4p$v0|qn z`USl(WCBN*zF(f*-BZ0>2XD25lxU&l{;`ZeZRz+u{G_Ow?OT2gQ8T0w5A!!6OHZ36 zEqc%z2?@tJc8@6Kw#=Ydz0W>B+-zM?DYmobS4%iQc2t#WFXY$Z^KjrF0E)kGTf2%x z+*N^Km(@j`CT@j>!-Tw_tgjC&bG;rrWGJjFLUdX2*ibNR4V0U5X>Wex^vR>NS#L!h znXhk0q+&U<%e5m^3k#CD42XB1ksTE5!d7@cFLSFxzJ$LC`D7QGPL*!wU!EnvB3@bB zUZxAKu_1G28{Zc){;au0=yrdu=#GI-r=u}*q{-##W=inP%SwlBUim{J>dk$coJU6JB&cwVccev8to{uIU>U zgNO*W(2yzSgD}~6&=tz)XQmFdXqWJ5H_0I+@ zCOZdPyO)@b?QRY%^D;93%irJW`#&2fe>K?rdKY{7WBT>* z^)D9msL0u-vb=1F>3J?H>3izAlvG1>j^11wWep|*sw`Qm5eM_vznWW3uW}T}>L?s| zN8Y;Gn!z3Aj%70qI)EO;;+C8(5YM>b*?T~TB(`aN_WKNkTfy@zlLkWV+oJP~gJMpU zh`AX@6ZU|pHIcvj>>m4OHd4&x1GABEZGpl`lEE(eqkaVrQ=J`?jGV#5L7$SS_69+d zgs4tj+87cTAP6>k+&EO@N1*Bicii+F&EoX2Ov3(>Qg}Fe5pc!1h`s3$F%w2+7$GNzosDtT6mR`CmtQrJAXSLm&;`ZB(-3d z$gG?!hDX5OhJdfO4eh`}!WR?JBcWTRlitKtnG%M6mLw-M&;ovGu9IRp|8CpLjj#D( zKL+H=CdjGt80NtTvnL>dDRl^mfihaj1T$ zCwan=&w;*O81a|UcJ*o0*Ch(N;$6(>iuxLz5tH&;t)Cz$UBNh%)>6 zgENp2V(C-GZSu;Yb}RTVgxpRmFD!wAfb_j|3jB?b|M`~rt3BxNcTrh@vyHI@1qHz7 zr5DQChJuyJ*b3n27_O}M9$gsYO^i+Tp-aiath#ZXTFX9_Qo@B8E7_;n0uIJp7{P&` zE@3JSHA80gs?Ywk>ikGBUy+#m-Q^C#Aewe~~3 z*7&~R&EdTWR;i91cFVqy-#c3UtL^Cvn;U&fm@GA&>=EIa%JDV3#ezaJu(UT3;Dy_2 z`jFic?WT|$vfrQ{aCNIHejGg^NgS3Ff0{Eqx3yH*QNCPwMlW(jf{u;{FmJ=Ij81|# z(L3xn;2@v_V)OZao{02REQlLzMXM?cnN_CexA=7uZXF>aWIW}O$L(U>NB6tB#VQIr zS0npR%6t~U!k1qRynG;trg#DdU7X{f!berW*%b`O|ejzL=6yHwOGRay#vzpt+ zJqtr*uvbQjY8zebipXc1d|7IC$ZfTez%lQ*R`@xXggn%RPI96TK(kRGf^l7`j+HvpLT~L`3;>1U|t92aq75tI!i8ch? zj}}lFYvpKP7!nra01mg@zd%B69cVcH0uqoH8v8v+eswPX4HA(TB6&${Q@dZyB7XwJ zI!ZyN`z=b(%7wy2?s#uDj34yrM809BMvs4X!Fp%twVdvtbTj=x{)I8EDg^Qe5TRI} z5M|`6Gc)rm?t8cU+sh|V!?57NOa5qSh*4nc_cKt-%jU7))Max_2jpnG5`SeIY? zjIHFp5h7ehPi4=GdqZ8zG~!PYpfRK>sAcM8ubfMU9;fTYW)xOk*zyDk-p>%#;&JoNvl*G;|=(D&H!>GiPZR!VqLG|I~1mp?@OdJ z`|X5fV_xN*K2t@(MD)-7%pSufh~-Nb7olbx9-;@P-CZt+~V^`|0W>JZ3LJ z{Cxnq{w;u_ws!7+=NQ1w&dQyF?SKA*{m;lcE9)uzO1;T=rj}BXk~7M|8L&JU`kPOz zO0faCwa9rV0xbw8{Vw@)X`HJSU|{1nhF;qesK;QP%3FYKv(cuV(I)om{kt7zkoSd} ziS;bTfN4K-z;c&gI9U4pZZ|r<7y@5^xAQK~<~pP_lfKbn4}DluVprj3UIF8N4J=n& zD!MxIr?`9N`xG`bo#L3}{18an3iFFWgXWZGX#65~n#8Z@?{o?8!dE2&%)CzxK4snL z(*XrpFt{$-4RK1PDEEQMu_n74Y4T1YxvI-_-4+d> zk3QfIHc%y##S45{v9VSVbh=`dlpPkWNh%(bEGt~^Ctt?BqgA7XCdl9jd8N9vDcEf^KgF9?8!`57*CR{ny9 zuVAksLgs%U!p00}?(6{gl~Rg-^i}?i2Xj*=VLLm;f9@Ur6)UV9f5IeB<)1KN{2YI$ zL=Totec?suXp9fO8WWImIHyEtl-Ujy}^mV6Rzk=>qj1O33=n^X@~my3Mk zV7tBSIofo6w0tZi^Z`myVx)^4K9aFi)6Fu=D$6J<**6fsce9$LF-dGNk#^WLT70>e zkzt-@JpvILo+~}mFehLE0!ST(83Qqt=WyP5OgjTFx@8l%9_4B*g^4VdGAI~>=x0*& z5NeqBLm=C>2w!pB`MQ;SyF}bw;9cdDIY2^+sX6>qvT5!b_;%@$2ToJ3dp>UkXFqla+RJe&c$-#TV(i9grHS+Z`W`r>)wVu5~U z$@xYuMEJAC7m@{7Xwh~4v3WNS&{W^(7uS&B=J<7-$!aEHj?=5%;=MJUe#44G}Sqm?tPZp^Oquv9!en}r;g{Q&h{qb z4mMq!n!CT-+E0$X3Y5O2M=SGRQjyItu6GF?u5p|_b`D+jtp^_Z63=7V+d?eA73uc9 z-RB$c0X(ou%^A(23G6kaQ(h(Sy`uxYfROn6$6!ryu5U*B# zzJEiYimAg(6W^cp-_+r6@Uj2>^-rSuYfE6QB=?K)y%#DhO6x5j%iU<_?=ZF7)B?!d zLg79LN2WARGw$W*rWie{c~oVRq(3UH=b{SC4h@eka37Bxd3k^J1}V(4FdXQp_QeD* zftr?Okxi29;)w1t!E%?CAWi5Yt46qW=&?B$Y=|A39?8=)myD-xKuFobxo))7!j{)* zY}UU;X0-|@jQ~;(48PQJV)k752ma2enmn`}Kp$!+r>lTSeEGXEY~h@R%iN*5i>VLa zw}{MHDjrzudWym zXD{D{1IU^D6@hc8>fL*LJ}q|~sy4X>raWFhdFLIx40KLYxeA7$QDdcbZ}$aN^ZdVV zSoA7ms^#wO;G)%Ly!(I!pO|GCU#!=q5)03tVK`EwVKTZBZFO{n2oOM*cT^cxgrZl@ zoqBXBSE0h#J0zvj?^T44#@+t-2ccwzM5UO&5DNCIghKjn%c*8(^3sp~_uBnM6W%I% zDwA(9-Yis%q2-ogh@q;|U4efh!O9C`|8(*L%bbqyq+TuoPc|lz3gJfK0kX9?XSFZv zj)MJ($h&TtBdrR|n5wDeda=g!`*Pdu>HQ=&h@<8g7J7humo@A;+L0uUq-K4HH=ps^BmLNDb4) zv&ELiQ530^a&oJnEZO?nZr7%4$DAxxLLGKIu^>aL`v3sjB)U$;A$frK2xJ`2$@WB7VkB#2)&se3x&pTe%@XeQ;M?& zHs&$~i_(N8SHrbuRp%G8PoWE?u=lJLdjVmWR&w|}^!Zg~oVqFe$-rD-#U{`iLD~Ir zO&rKMf>M%PUKYwZBEzNV+<4bITxR1iXTUP4+%$^YwOD=O{e@Y-QP)-}ImoWrNP#dW z_pyhryj+Nl)1S%MSphSiI)d~=b@KDuCq~xcL>JLDER76Uv4tfK)k!Sr8WoH@Chg)f ztq36fP<@WIBeO&`xkeG3&PMaqgwS9k@_hN4g@>IiF)NNM&2oa zi;t9);neWbgq~>91x><3y5w&nwcPY%O~F4?w=L%hk)bFULTv=(58R7AE=%OZ4Sf`C zd_X#zEQ;j$uIT>xNtB`hTQsE)A|jxDV-XaMJWCXzU4%mwhL~h#jUpeLT#$NV>zxNr zv>|CKTnsjY&!fHk$x3aHQG(UH5GCCM#T&Jg(=3m4K?Wv>El>+loihAt`-lO#*h0 zU|v^#SNx@wj~^buDkHrk%n7U`kot&%2ToISTKve43i~(O;UG;h2{2&~(2k@V8$t<+ z@hoSfP87RU1STp!6Cy%6AtJ)c35<<(QHjo3Tu2y^$#Qc<>V4lcEw5q}0HlkD;6rT= z;)io15E`fReNS$n_~<%#{f>o0p)A^%x~(_+Ghir@inzd+ak8Z?5Oq4iu2AAM5WjIi zCa6p4W)v`X@3)RE0kAtln$(osUX~;4)?PtOjrq}#dLyezR7DTZiTgu1vA3R_+eGml zhCA^d_~?Nil(maDlwk5r@TnaGIQp|Qf%pc4;+e?3cmB3Fs?yH$ykhT94}_^<0-h>* zfol~mJx0e~U{qtfF_HEL1(pk&O5P*DiZUNFD5S~c*h3G(q(C?=DC*qyDMzIs1pWK? zc;12XTLY;II)xx6R*mw0c(U*4)llDPzWn6WnOUW-;5)3KA*6v0%viN`7Gu_3zj7_z zvO}aol+$3UBCFHkp2t)dQsL|70z^|(3Tt+60R63-mkFfm=I(-&&*AnHC?A>$5OhBN z@kU4{N&K+*!kdh*o^5_Lng4yZQ8xX@&(0|RI@!GJzkj@dq^69pc|lC~EygLhma{7+ z_uVSIp{!y?^V@Hcs|&UKtEF}RkD8YSC-7H2*c{P?A>-I`4@#@nWg=Xyi%FiIN4z`B zd&iH=DBkGqxOLtbbXxol&aP^IE?1bQ>k#WVZ1!y$^N*q{5+A4NKAmm zP6l~aS&rnRg;^O@PsT5LO+_MedWI}`BLxUChz-b2At?GIlP%zKPtd zk5J3badr1L8Anzv{L+FjZDh}y;H<%x>MVwL#~kn6SX4hDNg*A~ZEOJHm)e}${H6WO z8>SNzKLEzG&6B=Saowc%(JP4 z6*gMDS6V2E8BGzBwQP?Be_v2vep00KyD@+e!en<|chK`qH=4~jKdXnDw(`|GCw4<) zG|pmKC9d?3kT)8!SBlFc==ce&N+8Q9zEV4B(?R#(+ekLL&q(L&bO^*98eAW;&a9rO zi^0Q2Kl-(S+aMX@$$+P_#G5Uvn1|$o(qr#$koE9tLQ_PKJDhdX5TKB-yGG*P<9<<3 zc+?0C(c-IdX{ig2o|e#T>5+VP^Tc4@)HZ+mqolf6i^5O5K%?)~k}CXf(D)6Zyfn)H z%`*x%qQCA-UVb}ZLBaMXfC81}Y|&X!yyfSuY67_A;A78}8s@vH;xx5wAV{x{)zWaz4oqQS>VE~pG<6<|X-$-RjYE=Wn7g@P%VUJ$+1?s}! zr5On7HKC>~P7%2~sc6=LY+0E-=|J`9{_}}y)iWB@z(dy^%zRb#O#!|smwg}K1H1r@ z975}j5XCM*O3l#APv}z z7*{HGv+l^83-fJ2Zwk`fGr}=`vXEZbYW_X`C|Kkuf9ZE6PsiAmg=eseYfO6LXO*vd zRe{>SO~fav8i`RT zm-dp=Ub*&8Et+{0G&xF~lvI*bp}ZH8P6aYT>}?CQd#&)U4BXy$d4pF5ehJiqcf{Ie zWYE+cCJWR>k}`2rMciQx@*;{!>^FwWK^Ty}`~oOjuf0WV@GjTBDq6rwpDTI_#Hs&? zmLe`QMP{^XpRu67djIWW?um`_9!5UbOwR!FoYbJezJAp z@_1L)9g_{?gQq#pv8OE@V-xf7{eAp>W<+c>A>gef!(0>3>Qfe5qT+Vw zm}J~RA-UW!KyvIEEs!jqRYcFXS*`0!ti<$6XGBKSoLlc0IH*aAQ4pJAj-=3Id@N0$ zMe`Nd!71^+YAa;V%db(OVJcAL74=>gj^)*bqy4vq``3$53XZ?G5C7Ol|8*;)GGD&L zq+Cp5q@$|-6S^|q(5oYnhFD=?I@hvm<7e}a&O`Z{U-vS~{#?=TyxC7BaF6Rb{gi74 z((diIQ~6zwBQr9-g3P|v6V(zG6IBzH>n;v*MvM>g#Lcy*1V?nl)fJJ4?kIO9{U zO6XMsKX0_o^m$*^WMhcifUww;?aR4nBALRbtl!U~R^Do;NxKmIfY{(gz<_<#AST?0=J4C;>ipIzYBd_RP4KFxznX0V(`fLw4?1Nk~Rsv#F~R0Q7o2^zLv z5zeVpK7xi-OwCJ}lz{7gAeKpQ@eKB|?9Ut{z>@j;M-3r@Gi3sKft3EM+Y5^S0IPqm zB!86&)q90`MT{q=ObtslFZ%Y5EUb4Wp0+&KCshoz$q0Juk$EoK8agVWO!p;b@o(9_ zSPLCT&b!z^LUKj~9ABg#r=NSLE({$Dy_AW@$OZxho^%I+4Y$$g4b3&T&Z@zx-m1p*J~OS`N80N^NY*WV2QFISFy}B?J(AiVPSY?5V|fue&vak5nCUR--(MpAIYd|c zUYG99a@^TlZE`5WUb0Y;QpAqTr^U- zgw}HeK53gw?!Wjmy?3eADxFU2D}f8kXgt%YdU+j-%OeHOU4=4#aJr`pK-oqR@1^(O z0Q(O0C}fX=ngVU!_F_9->r5~5PE7iaHn82W-Lmt7VX3}d*TOm7J>HhflIkdxy?b+M z$H%l;+}iNdgsbWzL316dwkfMR3iu=CPN|YL^1tCjg55JV6)Q8cH0PO01lspcE2Sh}=4o0`l{p(C*jY>)(B8#475Ynlk?wDkQRKZi0)o8d^DPK5BMsdwjeQ+)e!P z@d~X2E&@l;c-HjBFY8-nU*-1`%I*dPIMJl6E=A*;2w`I$gtuMzpa|qxK4Fp|#-F55 zR++eE7AYFF;#YeP(B73wo~&8JerToiC1oJ>;ieSPiiPp1Cl|1ZO=0~O%o^ayMg)jU z;k4m0F*!lyCU!gz2@{xL6YOhYgKK0qhE8D%btBneU5-A*TuwOUT>0Rii-1Yo&+}Hg z!-*o9S2djmR*%&(#LpTiZmst=c5x&$n?5A8=)kgPT9#Z%1#xPEE+p27f>5r)M#n78 zs4aTec;*P!4doVEjvbwL$*NddOJjy#av@@EouR@tVK@O9Hr_$dH3; zIa-vU_C8nx-EOB?ze|;T7Pgw8dwQQvO-Q{&Ok^8I@8jiS0K{y!>2~%Ypif^pyaijNQc{T z;ogpt2Hp>~$HHQ*A90ccYJ4x9jORr(>cWqLD=NfVmyH9xNDN3gTFR4(T3l#x2XQByAAFsi*bfhhS^KDbsB z$=~EQ7jde>wcK#GPLPS_PQw|+_kblVd?V&p9dCr9~7ZdzMNwFR={|vkuX=1D5AK%TUGKE^CgBCpgckM z&47E5UEKluigJ_JQ33IR@dV#@1@1v@)fVg{_Dyhy5abi(iLGw|Wcq0iDo`jH^?U;> z^~#ui^yXBcbvn{z@vsl6buco`t&X^1ofFOyKwsEROQiz0A8q0wPs$7x0}%rc=hx5D zl$!~=0jC|M-2doKr3G0w{J!S^qqft;LCH5LMwS3lXYNsbSGHf6pLv0GgraZr5{@si zAuW%eB}?&xJtS$Xaux+6WD84?A@?X$B_uoDvX7^lz$eed#qXPKbnTxM*lsam6xeSW zV!pH93dArMCU%>vdLdpHxqY%G?z&g?BD7UHMQl~+;XZZ2T@gZ98 zF{JGQ*ETLQXgwa7C!;Fj*ywmt`tmT4=;6V~#%4eLi645VrjLL>DV^#{-pG8Txk#da zkzy)0E`ma(C^bhRQQU+j$#f&xZig0@acz`+%F7BCSv7UGsWwIyHySV`6~*#S8Chz; z?W3EqIyd^=*vOn(sk9W~0Og2Ow_rc72CI-W;u8?Ir5oS1vDq%i;~`bhI%-FcdrX&B z!Bws2{d7rQGM}keCfqX>`z#StFn_#E%hr6umk!CGnOw*BH$fvjBfyIQX!B}xd6TZ{ z)c#C)B=jKf+4Rx8u-cfV-RrkE!_QM4-SuS#cmpN_7x8wOjrK?m3X#Ab=f0@b3QG50zc|KRk6GFNb8VQ;ieQvVO(?U)um+X~#M*exytPd)=Icf_sVZ==a1b2<4nA(k}8;?{f{>rbPGz86&uWu-Uo3pbWp zHa|=hwIG^c-}3g9M?y{|X;8zfk7)5@$VeZ+*^`3RZ)Rmy61srH=>NFL;gm8lf>STb zmSHbgkV2t|$n>;!Ve%|Hk<}RFc`6?rHDQs$mFlb7%P|{1Dkh+|S1FQBN}iGj%fL9e zAt{I!ljR+28aJvWHGLdR*9OR)DU9Q4jCZCzz$uc3NpL0u=wQp#y%&(FWcLWSYE5Xd z+@cY${5C}8Fq-SknpRor7;7P|Fd=WL>=9I0g55vctLo4zMv+CQBAr&v2RN|rEgoVW zpnDq{2TIy*&2pvX-|>x-{HO9U|L~cnORR*yVtHfcft)7ovIVTKXfwJ(Yz~yVa0*_n z$ETv`_jdaBSh$X9hwVt4GlEI;wssx52L64)R6m2Dq0@%Bubnc-n|moH##J8Ow)s$Z z)lm-ZWyRCJS4rOvMW_i!aEN}_P5*|XVAP+stq8mO!R(qXrMb;!6&QTLL;A)#ufSnX zv8j)s(z#6jS$2`8p*U|JAbhiEr#j|%&j;NU&|KFClNE$3Jup57f;9 zk+zL`KS^Syaa&6~(}$l3G)Um-sG!wCLoI*oCz&&KJ3S}O75SfB6C zd0NAQEJzxq;5Sbnw4ADQ#H_*@Glgh&>=_C%Dup7k$E=B%8CS*9!SvgIkbNgBRU1}o z=SM3;luq9FD4Mc-L*P;FwxT`4r%}^WFeP@tnKiXzUmLrrdx}6=N;@7j8>84)7euYd z9bBgEe^B9$nw3a()>zZi;4{wqgDIOfpBA2{kptrbdyJb1L)aD+y4Szm5Yb>~oL|4g zPQ&#F`(lnemYoK}ep~3Y76)+Iw08USi)lc1deb`l`R z2KL|{yd=&5bU}G1c1{GCRjnV=L_ehH_Vdg*By|eu4X6e_D(QUV>i5GN@eZIG7gGF& z17=EGFXn`i*-SL$*H(XJTL0P*s+I zO-A4H>5)J-WNWsMZ{C_%ZEs^=SbMraLrEbd$OlpO&rXS01O4s(Py5qVJV5iLa7R(aJLQ=&#+b>g zd_FgS^aPmNRvQ&>Pd)Y<2j_o(?m7NPPw?L+rT=pn_6Bm0e$(@_`g?5Z9rr`>ZND5e zVKK7^w6=VHwPf&DVu-v|K`EeynSxX99+6oMraKgkmyy@~o-D_Jj_&o2Kyno*7(@); zdy;Bvf;jp#F!JFulm$1Y6YY`ObWKIR#z#a$s9-%HH4B0nkd4Vt0cV)JNRE{$0^5J+ zv23Q$+X7nY-eu&JS=I(cDbWkBZQ7+*ZuyD$uqIRzAMQ8<3Rbbt&iiLZj>;>RRNOCi zEUh!-owQ9dk`&9}yxwP0a9K0-Iy1govyT`Kl-xvuQ=9&H6nx2`;#mZxnc1&wpAO#U zi(pbMlQ9~ersJI;z4~6#tHt-S_E9?8kQ@+bx{jyp5u6D3*6ihJVe-VJ=S-dpQN|*C z?y)co&h>)T4|P_b)kpG#O2ewX$XO^5{D>IFzXRxL2&xEv^_POHqDk2SZUT3K+s)lf;n$1Yhf-}lc0U&O#(2ESkrqyqc zpc*&B80mwsV5ifI{s_zq#)9fG)#C$%WzA;n7ngOaV)$Ipg5*@WzxbN*H9AD^j_-&2 z*|uE=0OFkX=zHZn0<6n!Ovh^7oCqixaBYuC}Q|ADo)&cTXW}37)%B3u|rg zGiVAmHWV@Xg_`hP;yWYL2o)qZDw)n+EJ=X$uX8rQBq_549OV{zR{k**CJM3O)kvRU z>vW!>YEKfvk$Oxq@J<`*@O93|!{M+1oR}mLg*6hVFtS}f!Sf^$=&B6}^@+(N9*vr) zHHO-b?A)8F*G1DlX&ce8M(nzuc--fQq-{6sd}WSxKa|E&N)MP6r5cZxiCsEG_L;d& zoRn>Kg_^K^gMJJ(1{WG)OVrZpcVRDVe_b~i=rA^8k=j9353CvxC)F${5|4^g>&rq~ zY$A5w!X2W#gy`HP913Q-eJ7(Rgu)n=gmu(k_va*X3X+UT{H^z*`&TN* zZ%MlUEIR+_z5Wa5>)>o7VQpvicW_$EkJ#V{B3CfQAf!jV58=I3)F2Wsh`hj{P|y(< z|KNCW%-fdwEyviTVN@fwtCm5Oi}TJ)DM4qDESf?4NWoRL;b`HUpy?f={l-RNKv94- zv?t;%4dqrYJk$i}eU;QH36kdG%>_V~?}{m5R8Vsq+|MRvNh&4+kldQs`7*f>Mnrkc1-dOayaFc2$=h5 z?w%ndoSGHRNM3|LYMQ%CGnVD{**Zg@EpDU$3vdeSgEUr$~SVMz}kyk)`LwbVu(aWdO0Hp!tA zCTZH;Z~T==r#p@@HOFIvjd;e`IVtKpQZFkRV2=8v?IfRP1Z1b|iqgPikSned)Q+hB z*!hF6xbzn=LfaYNV9+8t2tDVn{{?Dh-#cSa^x6-x1Y z%g9Xsnvuo+Vby<5mj7ekH?>`72Sa1Ix4onzDbs(}8UKKEy`j9NKKw>`_i1Z4;TX=U z9N!U3%+gcc7oaO$3@rk*vF}-puBcVlum-+ww~rmL!(63}B_{x z7uk|Gp-7ZhG{ea*X>S)UvbB~V8>0yBu~|h2Y`4{LCsit4N1h2MXX`uZQrpKig6DP_ z-vA1Ik}^ayj^s{;0Q#4o#7f9}-&yCu2X9@&_Bb`O>tr5yh~fxI;SY^u^$#DfGL7DS z=2jMM4C=xo6}_H3DJdFpbA64y z>iklP>47m^d`<*@PDuE;8ge0El}~q4Cun!ZNkV@AZ};-iH6Xz^sM$m@`k6|o8 zU!7qL0wHjrv+mRs1Z<{71NNLHwdSboLB$Z(T$lj#8q{=6sCoVVBHVN~aJE%exdCTz z1X>na)v{YZHxgQ1h$od5tlnI_@T|y2?BRqDtvy&tA09iM60x9?7S3Z5?7YmKl}SO_ zc1w~!xrEEF2=bgrGX+LmGr@&yWZ_&z1r-`ogNLm`x?}I>si~r)oMoG|*5P@7ekNK^ zqb*w2mt{tG;nf`yza8gZ5{JPyv1W*k5%U%kl{I9CVhN(3P7v*sx-zut_Th0#*bU6B z9gmjnX|H>_NPQkOnrz#obRd%;eb1szwz`1Flmasx*8R;_Oy?FlyuFcO4h?1KBNOfO zbsBK*<>eJn2>+A0LT44N*JHRJ8*{u#CfS|eESwOa69qPt4UuMoCX@CVfPm6*eBUrb z4Uf=NBNT^via9huARy=yxzuZvKPA1I@=j+G*LXYSr(b%Y$`@31nfn5p9G!DI zC;Ls8y+4Jge>eL1<%Y+PrCFFg+b&t2j3;K4%E@3$kC%KDrQ}{pG|USyZ(#^*h?zt} z8|zXJH==>PKKi2)Kkmq6{*B(n`d7#s*+1M#&f&jdJ>LK*ZzQw-dduZxWP9FxOUfJ` zqfiBjy#F8<`eMUhH42t#Q{)#+|5GEaiNsR;>3m6-q%1l9ulFyM+x2!g%na9iW9_LA z56_QKcF`S}Uw&kP&qG4I@L90WeYRhMq)#COW{w4z-I66~JijAH zhYR79J9sl=QGa0x-gD;v0{9U&eO07IKFu3SN>b!)x}zQ!F3kur)6QCoTVA#iK1c0o z9C}2YEtxM;v^K9jU^$(zTvoV~Ur;St;G%U@UyB#JRVYp?d$sD|5Nep~$j9JJdo;I2 z6l^xF4?t*bMp?^;&#t8#Muf%AMXPZI3q}NeEkdN6CSmW%kDBCr4t%2>@+Zmq8Z}E>4l?* z1*NMkQu;%H35AOHy^M4A!?v-En5fL^7AER#B3+1ual}mT;h$}8uMSL{FK~QTJV~xW z%fZ)U_?Yj{E2(UiOFMaT+wbU6#7N8oZ@Z&K<4YfVetQNfiPeweOC<+vg{HG(d|S>G z=hnrTBo*#N)ltBMP+({K`;R0rr83vzB2#QWQO;X~=DF9CS}V8vy5N+G=%o_zQ^^zy zNW&JbDinH3KOunjtnuvX*=I&Q(2g%8>AfU}W86Y^cRD{F8xwg`W?pYPXNZVmuOG(l zJ~OXi>wN~xrjFev*g0w{A=;?w`9V~!H^$M&6Y(+8(%d-Hes{s@+dLF4zHi?BB~okK zX#Xw2?~g6R>LHH%_Mt8Q^<#7X<75A$i20v$0taJ#BNcrsXXC&9J5kwM5lt1{$Hrg1 zmR6G3!n&N^x!ipQn6rpk1__|UA!H8+osNv{fk|<+rH=hTyve>nx?ypKTVz&zo=<%# zY1}8bcN_VbnaO2(-m<^TwZH0kzW(~_lH#+zlBfKn-25A2(-0v7C(G^wVs^v!GqF$4kMFMF?V}jh^QZ)ibo#n>s$|}wp7>C9KJu&noQ{V^zs=nk zpB1F1zDi<#pzgQP06Giqp{|Eh;_bdN61NtpfgNz%!3)kZ?;;1%yIeZg84ubO?YJ56 zSEzz6#OD%-mMY1@Nc@1KXji0(G!&$LjMcLFxG=^AV|1&YTGScs=gqH zrx32SgN?{bQDHZ>V8xql8^#P9`*Oxn+o%70)cv&5e2-JiK2+cv#vb3Yt}j|0AeD|@{#pIegX$I?#3KvihoSLxI$D^= z(k<9{mEw?)_%)uftigfE>Sp*SBI#`YbT_Yv4sqXh>WQU@>%|&5o!W?fBqw3$+V{-p zM^dUgWR5!DTw5%ZVbcp`@;1Hfc72Am^{1CVIvWB^ z_3p24{c6;|?h1eF<^QK(QgnKYd}|SB>o)@RU$_D#by>7G++1)YpfX>M5{9~}#Q39F zcSmS#vwn9W2$Ep5zGEZtP?`lkeCqxP>Eb(`VLCD8jLliK2@b&dl$X6(5{!~D#W;M# zg*$JDAAmz&?GZO81)O9ICc&xNtPeistRf2GN8sSRBmZR9{Jq%xh9ZChKpE=~z4Ui^e+R|^DDL5b%doz66SwAT-{XxG=a*2Nq*@UZ? zs`9YQvW_n;%S2v)(E1Q5o_{ML(7(T;{=0}AJq(8}CT(r~YdP7RPE)zR#>ltiBzdP3 zgN@dnTZ9?c;aW355b<%Ku!~Vgo_T8Uhs91>c*8|^Ldh>8VP5HuJ(*Z%a)ky%d52O1 z{6Hug@tursbm7tTiqE7$l z?{l&Zc5u&2zC7Nl?_z?lOYgkvr*pXRrx#;FzhF6plUzYoX6hc~Ob%?RMGZ5`Z$_QW zdnU!sdL~a+Fyw2;sMHAiMm(0Hv(py@2e&9re-)Ss5@)Sx8I&>`>DwWE$l-dE@FPtAM zregKPaevBqfxTJ`Oh;#4UWu7{f$R3SQiv3yo8s=B4EdN&G_BJwdA&Ytl6*F7#_KVI z!4huni#1I#PBComx691^CTB#+v})A+T@E%t-xUaV3+qT23A`P=Fce97X7*ns6BgAF z712XE!Ptw9EU_~bhjojy$x5j5$RvfS(Ou6 z;s0S!{Bxkp;ZzUWOUAyF&FKDc1BJy8{&gf(D=C&e*l4uXD#y(IVLD9~1pnaad{;8* zf`z(Yh?VKBpZJ6J)1t>LTaQpa@uaQE(Gf549z6c=-MD+Qlg<2ghxBa$?yPq0?wk66 zT@ar0-#kmQF%-ZVtA^kommQaU-RKkbzup zo=#r8c?s2(;qz$(mQKB;OO;8Q%USSlf3?BGRi+R>nP62)w6dxxTQ@=FV*5+|pr#K0 z;rq&vg0v=$vZ)+Tgr6v`PU(a{Q+t21qnKF_XV^BLBqx-0C0gcG8I}FmkTEdDv^T@# z``#8Fv05cCu9;*m5qg78CU{5}_BiQGQ&woN?Ky`*#67S#yxbYvi z$c$1Pd|0G&gcH62M>ML90FFn}5of9Q^h1)Kg(YhDM`MVTwx5nHKAg!87;T~9MQoxI zVQDQD`}_fG(fekv`#<%_>ZSb&Pw0h=8duY)-n9S=lwI9oR!Sqh}=M*VEh^F zWKF-HLXZuRP8~7+rXaHSM3ZJ^*+tH@G$S@Y&WK;Y&;)SunO@#?h0DuDF8n5vZ1HHy z_tl!Or@Cj|Wo-{k?8?52-k=^ZkER_U96%nxj;_Pq=W@VJ<_zYdchs7ie5WW3_k-Ku zK*=CI8lv%7+#m#%$Jy>=Iycu6&B=;wG6tXQ7KbxD04h3n;hX&lhaH$FAfwL)yf~KK z4l&6u;_Xe#8joDVaO`mi)O_1zl&C;sbH55+&vd%#qT@+O(`L4eSCeJ7G>Ga`UsyLu zTk-NKyGzYc| zcU+RAM8W1thY~lfV3>I@th#K}jGHQ8()AjNy8hGhSq4Wws%|nF*$Wo7J-B)jzwpRo z^mJHB-6i1p6^m0S&H{3mN&*`&k!%E_!)|4_fX3jH8dK9gx%pK~MxDn>N9TFwr+npQ z9_6unIf%52doAox9AYz-KH-_b{!`;LP5Gw%o5s3Y@LAN*+NOK@D9=$g)X@%s5FQMi zh9(USZjmJryqEm3sdPnnq4|Af^Jjh`sCT!qtWHA7y7~4MW6kq-P%;r_BUj+$x*{Wv zCkikTz9JRK$G$O$!KI*jU%xn+jzA!zy9$k0h<)#DXYL^xtzyLJt?y@*63a)#^yF%g z`rYiVE#-(>fkBZDM(w2#`z1!${sAkRZY-tImk6_Gs4=B&QP@$BQ+aD>aUBu( zA@r@A9=oy*NGNI6X~XG2SXPo89bO?Ge6;Jp`{EmNzWhr_?wZf^v{S!e+*97`66K?- z3S=tcKIZ8Md6X>Z&GJLCpwh5fsu_EzOv-Tml4nXV2A7dwscIHMh@lBx>yoxstPV*? zNLyQY#gy@uGSdlVRk9zVjDi^ImG2JuGyGPUq}G_;!Y}Jz!;keJ;rBnOug;FfipGY{ z4#o<9-kf`zy8T7Xu~v|hMHfVVNy9e7g<90s_a=q|UsCRcr~>B4+i8&6Ly$COQy!^LcrBMEC}WDaJIx)N3DMn6%A4 zfEOJEBb*Y6tj8-98U{lWYABtLzfDd*kRnVc>Q5D1s?z32=s~P3bY7fVSGcBY45M*s z9sGShb5t^1W8i6H3YDkwv-6?lBqCK{KA-g z+cJpZo;T{IIry^{sh@=+T=heqCr)HjYg^AyVHvU{BOt;?wSz?EVHsYTX_n!}sN|Vv zlWRfGF}NSrI88zmPPvnyJ{^yy9u!V^pPdbZK=tIZ8^A=6&P><$=w2J7@g35Rd+JKoMGp5!{xytAd zX2=$(%S9tl1^Z{-Vs4Qo-RwFq?|kh8!=473Z{G9r3IV;k4+ebbs$x%A{9v_6+QK%F z;!q%qC-g{th&=iz13I_s2LO*iVCNIIVfLWjz8SD8OHTJsLBa+^#q{R4p+eBV4qg8_ z3IB`ZZu^#&Z5{p^y%ws5`Oe&8yi(f}Ys!n;fz4t=HWh5(M&~-!%TQY4fEh()(jqno zTT90!_UTE9CJ`OhRGpqVi?um_Xj^bLgnm=9SytzhY|VH2;q7vF8N=!+t5A{&YP9CxbuAM9GO0!ThMhw`8IangjL*eABiRFeu==)dHUV9{XRMLJk(84M7mGbZdavhj>dFMF8#65v2?HQVOLD_0kf>7xNMe#TVm}1H~8X5((w? zMxW&Da+90X+YK%H(T*?rp&HWmjwsZXv0c$0$<{E%BqfeeU&}v|2?`_xkI2#JvylqVRw#{h=XL0 z2@sWuj~-=)Hh@f@h8cB=te9gXH1Gox(KhXwsT)$SFyP$q{#|cAfWY{kv=@!JGnj$d zD{ONFpsRmR-TQ^PGn!$LGe*qFD>;)t3TgNrpPzUejGt&5yIr>*>@4w;w%0M`%O&sl zAmx@Yv*dksB#4)Ao0&$ZyUq9)dUpJ!<|@Uu>wQY(`nEl`??#7jU$Jp}@PN@T*lRGt z9R680;vBYFHV+}LN|9;NosZo42ry>4D8}gCZ3A!-%TorcLSF*iBqZ*I|wfvv!dZG03(Sa$6=U_s#cKt;oB3{){vqB=M_iOrQQ zHidh;V=${cCiHFbwXsVGZ((#Mt!0=G#D=Uf;z(9m7B@7RCQY*B|6Lx0S`Y(h)pV2)Z^0 z?Yx3mbE7xd@>VaJSTIGj;x#ToHt+*MR*Ez#&%R1`b76`K5tdQ~A;u3B#65@9dW#9y zY`nC0R5s|+%G<%Y36tbMjg*F3+{*=cxkT_KeI?7YPmw`c*O zUlH3RcUY%~bu?MIEOAUTJe#yqPdPD^>r@t-XFmPVXP~o=rk!5W65X~J*eJ5n{@L`d z!JyK;*x)Okv1I!UB`e9Q?5C4s%5kOLPgLtIViKioMa3?PN}saDFr+f^T*?-dm~i_# zuH+>P%hJ$YOulYIE!2!@FCOMD+lBTyol;)%7bnA;q@BxOq8orIgCNCwuMPZ zXvktG(W^7y=GMHwPRIn=Xy=F%Ckt?_R#spL6RZFX?Ool z_|d9>V`pN^$HV*yw{rR5i*FyZKUpNdA%mA2_PH}4P^KPL;?_wP1v(#oasGppj;JE> zFXps~Ts|$49ojT3JH;N|u2Aq~4_%=R+Otz_p`6fMGAo%|&+z89u3UgF^Q4@JEjHc| zq!T#~eIA$kIo6g$Oc#A8#5pmP)Zzs71ZZ%`qwy?^VQZW?Sn7a&yWhxF_M4(mMORIi z{`(d1ott2zoSHy0l#rxJkK9Ovp*7Sshatr0>p(noMoK&cC%q-VA8s&B8}g4{;YRDk zW0e=QGw*vzdMFI)LS=5C1^fDqJxO#-Tgo;1!Apv@;vf%FF5vK;>h2+0?k-Zz-t(aH zMF0|X(g|9tQNLE?Oymi!@Qr);UXB8tuj549vJ$9OJ;Z=EnZ2mx+BQ~xpT zLL7`KxbLInfm9M?=wLjJ6yi>HeV4z90gIwZTI!Cl$zwXV^|>T5aKPlM`_AKh&GUA3 zpz7u2Zldd*^cDz+1YrbO0_Fh5`hEq>1tJBE_C4hLs?YJC_rY!u66CPTrRfvl&6D(F|iq8V>V{B^V!Jn+HMW7#Awkr^ z?+JxFtm(HN<0rjJ+AkLUR9W7rJnh}ubi@Pw zP=$WqDrjbAC7XNJ^y#?hY_;fTM&e9KjH62DM=h&tSB;@B@;5B_*LtP7RD6~+w}K5L z6jVQEr)~Jlmn_?B0eB63=eCRWY8_!XfZA&9nW<|Hhxz3NhhveXTY42`kEFf~W1C~1 zs#A=QSI7cpSiO^6&-%zos(TBH@TkJ9hAoX{_RD}%Y%%$4I+`HD%1I9o`zSxY!b=r6 z$TC8Su~DYLy4EkMaMPT^={QREVGC+X)-v|20@^~WE(pCe9|i4QVB>J)NxvRBOp(sh zVHWyW$fAvx6{b5yVcjZ^9kc(~Uw+STcr`LI>20C0(onhYYCcL}uSW`OKc@dZ67-qr-jZ>v$l+fdiTC}$Z@`kiz?oi!ODB(&^ky&gc}?6ANn zIN=F@5dJYo7MySFqiJN<0Zs-Kc*3@&y46B&0l>Dq8bB>(aumvp=8N{qW)E^AU)KDC*@-qJfaC0B9=JIG+iLCow#ob^uln|34hC3 zM==j}vx|LM7tx7*T#ViB1s}Y?s6Yp{bisW#BSxRB=Pr1GA*;25-76Dux>)lCsy2pJ z)rLW2T*<~X3Y29kElT{0y?AzJ%uQ@8`YLQJJv7&)kq1q)#&IU`Io&FJyV#-DS~dPv zG+8vtUI-GdezX6CCCN-?Om)PH^Du6?vo>K%XwWz3)M&Ha5VE!0Qy`J#$;^U7UStZd zM*nB84ry>^;8ri=qC!y^SI;B?8=e~H>APMuTb7@HB(mb$Bs%Z6)jr$Xar^&${rpc= zl0QTx%uUSS5F>wPF>fVp#}y@HzJ(;(75P(cndw%W95N#36IL4>oaKT;)g%AzQkLdP zDh5#FS2PR|bM36e1I5F8X_FM*cftZiqaUBZzy-qe89j}^+gr)Ix2w9|ncbZ%(LDb= zFV}sq)#HJVg}y3hr`Syg>5Ac1uwe+f3q+UhLk4N2NaGSk4xse&*oy8BLRfLzT$<_E{W0Y@I1=dmX3x4`uoMHlFYAOXVTw1y^ zJPlJ}WOb~QbGG`is->So=-O6=t&uxyt<7>{+mO~x#kd4NOV_*MEY>tCG_={cr*<2j z{1g7ttyI8z$myOOYx6SSPx6$h@ZwkR+7biwFMb|*J=Wv8#p=pnns2isv#~5C4K4b! zD>%cZ0f8`AusU&;t))B**rM}*${+LT+)XQh>9n~Gz+vrP+RsY15v!wgdgUCAYaQ}n zBXy1kKG(MX(>Ax+Qx=2%qD-)s@U|!I@BmQmKGU%l0M?|-2CM8RI@94wPL;h^xc_yY$eH(effJPw=Jwb<3XrTS9(tMSGDbB^J3@7niApICq?&jv^zALTaw0ipvHCJI_Gg7a9Ig`bso*#C3WWfcJ&pR80 z^C!+v>tB&8;Zx6vs=gfPfTb3{jxxaTjkfpI>~p?=9ogc44@eRco7N%c@w0<2ru
      1. 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

        #59TW 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 z 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 z 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

        VG%S9+{qPcu~)l{^K6v+%=SXpf?M7X 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

        SO-_c*kpKZ0md*Y=kSQ1qKd**+D0ymy7RYkgsA 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

        !%tcKfxi%vJV`Lq2B++t0Whfvm`MH+ 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

        y12)gzKX&UM}meys0slCx|>? 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%0A8s!(|nU6ZQYdGx}UP%zXfXkf%K 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μ9^fNGx*#$Eq+Y1BHX{<|CL0P&`+c3wkyV)?8- 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

        MT*k}wz9>J0Fb8`Byp4L}n!?_7(q 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
          eY4|N6dYl5mUv^_ObW*y~XG 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

          PRE5=1nmi4iI3%XLl^dj447(X+Udf(1 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%0AeUw9fq%ox4nvp{wY{fNPKMEpx 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 Ld~B^qkoo)%4QZXo diff --git a/examples/scalajs-play-core-react/client/.gitignore b/examples/scalajs-play-core-react/client/.gitignore deleted file mode 100644 index ae3c172..0000000 --- a/examples/scalajs-play-core-react/client/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/bin/ diff --git a/examples/scalajs-play-core-react/client/src/main/scala/SPA.scala b/examples/scalajs-play-core-react/client/src/main/scala/SPA.scala deleted file mode 100644 index fa289f3..0000000 --- a/examples/scalajs-play-core-react/client/src/main/scala/SPA.scala +++ /dev/null @@ -1,13 +0,0 @@ -import org.scalajs.dom - -import scala.scalajs.js -import scala.scalajs.js.annotation.JSExport - - -object SPA extends js.JSApp { - @JSExport - override def main(): Unit = { - import router.ApplicationRouter._ - router() render dom.document.getElementById("container") - } -} diff --git a/examples/scalajs-play-core-react/client/src/main/scala/component/Menu.scala b/examples/scalajs-play-core-react/client/src/main/scala/component/Menu.scala deleted file mode 100644 index da34659..0000000 --- a/examples/scalajs-play-core-react/client/src/main/scala/component/Menu.scala +++ /dev/null @@ -1,13 +0,0 @@ -package component - -sealed trait MenuType - -case object Link extends MenuType - -case object Route extends MenuType - -case class Menu( - `type`: MenuType = Route, - text: String = "", - path: String = "") - diff --git a/examples/scalajs-play-core-react/client/src/main/scala/core/material/MaterialComponent.scala b/examples/scalajs-play-core-react/client/src/main/scala/core/material/MaterialComponent.scala deleted file mode 100644 index d65280d..0000000 --- a/examples/scalajs-play-core-react/client/src/main/scala/core/material/MaterialComponent.scala +++ /dev/null @@ -1,44 +0,0 @@ -package core.material - -import japgolly.scalajs.react._ -import japgolly.scalajs.react.vdom.all._ - -import scala.scalajs.js - -/** - * Created by Janos on 12/9/2015. - */ -object MaterialComponent { - - val rc = ReactComponentB[(ReactTag, Boolean)]("MaterialComponent") - .renderP(($, p) => { - p._1 - }) - .componentDidMount(afterMount) - .componentDidUpdate(afterUpdate) - .build - - def apply(props: (ReactTag, Boolean)): ReactComponentU[(ReactTag, Boolean), Unit, Unit, TopNode] = { - rc(props) - } - - private def upgrade(scope: CompScope.DuringCallbackM[(ReactTag, Boolean), Unit, Unit, TopNode]): Callback = { - js.Dynamic.global.window.componentHandler.upgradeElement(scope.getDOMNode()) - if (scope.props._2) { - val children = scope.getDOMNode().children - (0 until children.length).foreach(i => { - js.Dynamic.global.window.componentHandler.upgradeElement(children(i)) - } - ) - } - Callback.empty - } - - def afterMount(scope: CompScope.DuringCallbackM[(ReactTag, Boolean), Unit, Unit, TopNode]): Callback = { - upgrade(scope) - } - - def afterUpdate(scope: ComponentDidUpdate[(ReactTag, Boolean), Unit, Unit, TopNode]): Callback = { - upgrade(scope.$) - } -} diff --git a/examples/scalajs-play-core-react/client/src/main/scala/core/material/package.scala b/examples/scalajs-play-core-react/client/src/main/scala/core/material/package.scala deleted file mode 100644 index 282565d..0000000 --- a/examples/scalajs-play-core-react/client/src/main/scala/core/material/package.scala +++ /dev/null @@ -1,16 +0,0 @@ -package core - -import japgolly.scalajs.react.vdom.all._ -import japgolly.scalajs.react.{ReactComponentU, TopNode} - -package object material { - implicit class MaterialAble(val elem: ReactTag) extends AnyVal { - def material: ReactComponentU[(ReactTag, Boolean), Unit, Unit, TopNode] = { - MaterialComponent((elem, false)) - } - - def material(children: Boolean): ReactComponentU[(ReactTag, Boolean), Unit, Unit, TopNode] = { - MaterialComponent((elem, children)) - } - } -} diff --git a/examples/scalajs-play-core-react/client/src/main/scala/core/reactive/RxObserver.scala b/examples/scalajs-play-core-react/client/src/main/scala/core/reactive/RxObserver.scala deleted file mode 100644 index b252b93..0000000 --- a/examples/scalajs-play-core-react/client/src/main/scala/core/reactive/RxObserver.scala +++ /dev/null @@ -1,24 +0,0 @@ -package core.reactive - -import japgolly.scalajs.react.extra.OnUnmount -import japgolly.scalajs.react.{Callback, CallbackTo, CompState, TopNode} -import rx._ -import rx.ops._ - - -trait RxObserver extends OnUnmount { - def observe[S](rx: Rx[S])($: CompState.ReadCallbackWriteCallbackOps[S]) = - CallbackTo(rx foreach (r => $.setState(r).runNow())) >>= (obs => onUnmount(Callback(obs.kill()))) - - def observeT[R, S](rx: Rx[R])(xform: (R) => S)($: CompState.ReadCallbackWriteCallbackOps[S]) = - CallbackTo(rx foreach (r => $.setState(xform(r)).runNow())) >>= (obs => onUnmount(Callback(obs.kill()))) - - def observeCB[T](rx: Rx[T])(f: (T) => Callback) = - CallbackTo(rx foreach (r => f(r).runNow())) >>= (obs => onUnmount(Callback(obs.kill()))) - - def clearAllObservations = unmount -} - -object RxObserver { - def install[P, S, B <: RxObserver, N <: TopNode] = OnUnmount.install[P, S, B, N] -} diff --git a/examples/scalajs-play-core-react/client/src/main/scala/core/service/AbstractClient.scala b/examples/scalajs-play-core-react/client/src/main/scala/core/service/AbstractClient.scala deleted file mode 100644 index 9066b03..0000000 --- a/examples/scalajs-play-core-react/client/src/main/scala/core/service/AbstractClient.scala +++ /dev/null @@ -1,31 +0,0 @@ -package core.service - -import java.nio.ByteBuffer - -import boopickle.Default._ -import org.scalajs.dom - -import scala.concurrent.Future -import scala.scalajs.concurrent.JSExecutionContext.Implicits.runNow -import scala.scalajs.js.typedarray.{ArrayBuffer, TypedArrayBuffer} - - -/** - * Abstract Autowire client for api call - * - * It's send the API call into /api/{name} path and process the response - * - * @param name the api name in the server side (example: sample will call /api/sample) - */ -abstract class AbstractClient(name: String) extends autowire.Client[ByteBuffer, Pickler, Pickler] { - override def doCall(req: Request): Future[ByteBuffer] = dom.ext.Ajax.post( - url = s"/api/${name}/${req.path.mkString("/")}", - data = Pickle.intoBytes(req.args), - responseType = "arraybuffer", - headers = Map("Content-Type" -> "application/octet-stream") - ).map(r => TypedArrayBuffer.wrap(r.response.asInstanceOf[ArrayBuffer])) - - override def read[Result: Pickler](p: ByteBuffer) = Unpickle[Result].fromBytes(p) - - override def write[Result: Pickler](r: Result) = Pickle.intoBytes(r) -} diff --git a/examples/scalajs-play-core-react/client/src/main/scala/layout/Layout.scala b/examples/scalajs-play-core-react/client/src/main/scala/layout/Layout.scala deleted file mode 100644 index e156ab4..0000000 --- a/examples/scalajs-play-core-react/client/src/main/scala/layout/Layout.scala +++ /dev/null @@ -1,13 +0,0 @@ -package layout - - -import japgolly.scalajs.react.ReactElement -import japgolly.scalajs.react.extra.router.{Resolution, RouterCtl} -import router.ApplicationRouter.Loc - -/** - * Created by Janos on 12/9/2015. - */ -trait Layout { - def layout(c: RouterCtl[Loc], r: Resolution[Loc]): ReactElement -} diff --git a/examples/scalajs-play-core-react/client/src/main/scala/layout/MainLayout.scala b/examples/scalajs-play-core-react/client/src/main/scala/layout/MainLayout.scala deleted file mode 100644 index 621281f..0000000 --- a/examples/scalajs-play-core-react/client/src/main/scala/layout/MainLayout.scala +++ /dev/null @@ -1,10 +0,0 @@ -package layout - -import japgolly.scalajs.react.ReactElement -import japgolly.scalajs.react.extra.router.{Resolution, RouterCtl} -import japgolly.scalajs.react.vdom.prefix_<^._ -import router.ApplicationRouter.Loc - -object MainLayout extends Layout { - override def layout(c: RouterCtl[Loc], r: Resolution[Loc]): ReactElement = <.span(ReactLayoutWithMenu(HeaderConfig(c,"Hello World Title"))(r.render())) -} diff --git a/examples/scalajs-play-core-react/client/src/main/scala/layout/ReactLayoutWithMenu.scala b/examples/scalajs-play-core-react/client/src/main/scala/layout/ReactLayoutWithMenu.scala deleted file mode 100644 index 67b395c..0000000 --- a/examples/scalajs-play-core-react/client/src/main/scala/layout/ReactLayoutWithMenu.scala +++ /dev/null @@ -1,58 +0,0 @@ -package layout - -import component.{Menu, Route} -import core.material._ -import japgolly.scalajs.react.extra.OnUnmount -import japgolly.scalajs.react.extra.router.{Path, RouterCtl} -import japgolly.scalajs.react.vdom.TagMod -import japgolly.scalajs.react.vdom.prefix_<^._ -import japgolly.scalajs.react.{BackendScope, PropsChildren, ReactComponentB, ReactNode} -import router.ApplicationRouter.Loc - -case class HeaderConfig( - router: RouterCtl[Loc] = null, - title: String = "Sample Title", - menu: List[Menu] = List(), - topLinks: Option[TagMod] = None - ) - -object ReactLayoutWithMenu { - - class Backend($: BackendScope[HeaderConfig, Unit]) extends OnUnmount { - def render(P: HeaderConfig, C: PropsChildren) = { - <.div(^.cls := "mdl-layout__container")( - <.div(^.cls := "mdl-layout mdl-js-layout mdl-layout--fixed-drawer")( - <.header(^.cls := "mdl-layout__header--transparent")( - <.div(^.cls := "mdl-layout__header-row")( - <.span(^.cls := "mdl-layout-title")(P.title) - ), - P.topLinks.map(i => i) - ), - <.div(^.cls := "mdl-layout__drawer")( - <.span(^.cls := "mdl-layout-title")(P.title), - <.nav(^.cls := "mdl-navigation")( - P.menu.map(menu => menu.`type` match { - case Route if P.router != null => - <.a( - ^.cls := "mdl-navigation__link", - ^.onClick --> { - P.router.byPath.set(Path(menu.path)) - } - )(menu.text) - case _ => - <.a(^.cls := "mdl-navigation__link", ^.href := menu.path)(menu.text) - }) - ) - ), - <.main(^.cls := "mdl-layout__content")(C) - ) - ).material(true) - } - } - - val component = ReactComponentB[HeaderConfig]("Application-Header") - .renderBackend[Backend] - .build - - def apply(config: HeaderConfig)(nodes: ReactNode*) = component(config, nodes: _*) -} diff --git a/examples/scalajs-play-core-react/client/src/main/scala/pages/MyScreenPage.scala b/examples/scalajs-play-core-react/client/src/main/scala/pages/MyScreenPage.scala deleted file mode 100644 index 45f5681..0000000 --- a/examples/scalajs-play-core-react/client/src/main/scala/pages/MyScreenPage.scala +++ /dev/null @@ -1,34 +0,0 @@ -package pages - -import autowire._ -import boopickle.Default._ -import demo.SampleApi -import japgolly.scalajs.react.vdom.prefix_<^._ -import japgolly.scalajs.react.{BackendScope, Callback, ReactComponentB} -import service.SampleClient -import scala.scalajs.concurrent.JSExecutionContext.Implicits.runNow -/** - * Main Screen component - */ -object MyScreenPage { - - case class State(message: String) - - class Backend($: BackendScope[Unit, State]) { - def init() = { - Callback { - SampleClient[SampleApi].echo("Hi there") - .call() - .map(response => $.modState(_.copy(message = response)).runNow()) - } - } - - def render() = <.div($.state.runNow().message) - } - - val component = ReactComponentB[Unit]("MyScreenPage") - .initialState(State("")) - .renderBackend[Backend] - .componentDidMount(_.backend.init()) - .buildU -} diff --git a/examples/scalajs-play-core-react/client/src/main/scala/router/ApplicationRouter.scala b/examples/scalajs-play-core-react/client/src/main/scala/router/ApplicationRouter.scala deleted file mode 100644 index 67ed85b..0000000 --- a/examples/scalajs-play-core-react/client/src/main/scala/router/ApplicationRouter.scala +++ /dev/null @@ -1,31 +0,0 @@ -package router - -import japgolly.scalajs.react.extra.router._ -import layout.MainLayout -import pages.MyScreenPage - -/** - * Created by Janos on 12/9/2015. - */ -object ApplicationRouter { - - sealed trait Loc - - case object MainScreen extends Loc - - case object SubScreen extends Loc - - lazy val routerConfig: RouterConfig[Loc] = RouterConfigDsl[Loc].buildConfig(dsl => { - import dsl._ - ( - emptyRule - | staticRoute(root, MainScreen) ~> render(MyScreenPage.component()) - ) - .notFound(redirectToPage(MainScreen)(Redirect.Replace)) - .renderWith(MainLayout.layout) - .logToConsole - }) - - val baseUrl = BaseUrl.fromWindowOrigin_/ - val router = Router(baseUrl, routerConfig) -} diff --git a/examples/scalajs-play-core-react/client/src/main/scala/service/SampleClient.scala b/examples/scalajs-play-core-react/client/src/main/scala/service/SampleClient.scala deleted file mode 100644 index e7ecfc4..0000000 --- a/examples/scalajs-play-core-react/client/src/main/scala/service/SampleClient.scala +++ /dev/null @@ -1,6 +0,0 @@ -package service - -import boopickle.Default._ -import core.service.AbstractClient - -object SampleClient extends AbstractClient("sample") diff --git a/examples/scalajs-play-core-react/project/Build.scala b/examples/scalajs-play-core-react/project/Build.scala deleted file mode 100644 index 9decb1a..0000000 --- a/examples/scalajs-play-core-react/project/Build.scala +++ /dev/null @@ -1,58 +0,0 @@ -import org.scalajs.sbtplugin.ScalaJSPlugin -import org.scalajs.sbtplugin.ScalaJSPlugin.autoImport._ -import play.sbt.{PlayLayoutPlugin, PlayScala} -import play.twirl.sbt.SbtTwirl -import playscalajs.PlayScalaJS.autoImport._ -import playscalajs.ScalaJSPlay -import sbt.Keys._ -import sbt._ - -object ScalaJSPlayCore extends Build { - - lazy val root = project.in(file(".")) - .aggregate( - sharedJVM, - sharedJS, - client, - server - ) - .settings( - publish := {}, - publishLocal := {}, - onLoad in Global := (Command.process("project server", _: State)) compose (onLoad in Global).value - ) - - lazy val shared = (crossProject.crossType(CrossType.Pure) in file("shared")) - .settings( - scalaVersion := versions.common.scala, - libraryDependencies ++= dependencies.sharedDependencies.value - ) - .jsConfigure(_ enablePlugins ScalaJSPlay) - .jvmSettings() - .jsSettings() - - lazy val sharedJVM = shared.jvm - lazy val sharedJS = shared.js - - lazy val client = project.in(file("client")) - .settings(Settings.clientSettings ++ Seq( - name := """scalajs-play-core-react""" - )) - .enablePlugins(ScalaJSPlugin, ScalaJSPlay) - .dependsOn(sharedJS) - - lazy val clients = Seq(client) - - lazy val server = project.in(file("server")) - .settings(Settings.serverSettings ++ Seq( - name := "server", - scalaJSProjects := clients - )) - .enablePlugins(SbtTwirl, PlayScala) - .disablePlugins(PlayLayoutPlugin) - .aggregate(client) - .dependsOn(sharedJVM) - - // loads the Play server project at sbt startup - -} diff --git a/examples/scalajs-play-core-react/project/Settings.scala b/examples/scalajs-play-core-react/project/Settings.scala deleted file mode 100644 index d669e40..0000000 --- a/examples/scalajs-play-core-react/project/Settings.scala +++ /dev/null @@ -1,129 +0,0 @@ -import com.typesafe.sbt.less.Import.LessKeys -import com.typesafe.sbt.web.Import._ -import org.scalajs.sbtplugin.ScalaJSPlugin.autoImport._ -import play.sbt.routes.RoutesKeys._ -import playscalajs.PlayScalaJS.autoImport._ -import sbt.Keys._ -import sbt._ - -object Settings { - val applicationName = "scalajs-play-demo" - val applicationVersion = "1.0.0" - lazy val elideOptions = settingKey[Seq[String]]("Set limit for elidable functions") - - lazy val applicationSettings = Seq( - name := applicationName, - version := applicationVersion - ) - - val sharedSettings = Seq( - scalaVersion := versions.common.scala, - scalacOptions ++= Seq( - "-Xlint", - "-unchecked", - "-deprecation", - "-feature" - ), - resolvers ++= Seq(Resolver.jcenterRepo) - ) - - lazy val clientSettings = applicationSettings ++ sharedSettings ++ Seq( - libraryDependencies ++= dependencies.clientDependencies.value, - elideOptions := Seq(), - scalacOptions ++= elideOptions.value, - jsDependencies ++= dependencies.jsDependencies.value, - skip in packageJSDependencies := false, - persistLauncher := true, - persistLauncher in Test := false, - testFrameworks += new TestFramework("utest.runner.Framework") - ) - - lazy val serverSettings = applicationSettings ++ sharedSettings ++ Seq( - libraryDependencies ++= dependencies.serverDependencies.value, - commands += ReleaseCmd, - pipelineStages := Seq(scalaJSProd), - LessKeys.compress in Assets := true, - includeFilter in(Assets, LessKeys.less) := "*.less", - excludeFilter in(Assets, LessKeys.less) := "_*.less", - routesGenerator := InjectedRoutesGenerator - ) - - // Command for building a release - lazy val ReleaseCmd = Command.command("release") { - state => "set elideOptions in client := Seq(\"-Xelide-below\", \"WARNING\")" :: - "client/clean" :: - "client/test" :: - "server/clean" :: - "server/test" :: - "server/dist" :: - "set elideOptions in client := Seq()" :: - state - } -} - -object dependencies { - val sharedDependencies = Def.setting(Seq( - "com.lihaoyi" %%% "autowire" % versions.common.autowire, - "me.chrons" %%% "boopickle" % versions.common.booPickle, - "com.lihaoyi" %%% "scalarx" % versions.common.scalaRx, - "com.lihaoyi" %%% "utest" % versions.common.uTest - )) - - val serverDependencies = Def.setting(Seq( - "com.softwaremill.macwire" %% "macros" % versions.server.macwire % "provided", - "com.softwaremill.macwire" %% "util" % versions.server.macwire, - "com.softwaremill.macwire" %% "proxy" % versions.server.macwire, - - "com.mohiva" %% "play-silhouette" % versions.server.silhouette, - "com.mohiva" %% "play-silhouette-testkit" % versions.server.silhouette % "test", - - "com.vmunier" %% "play-scalajs-scripts" % versions.server.playScripts - )) - - val clientDependencies = Def.setting(Seq( - "com.github.japgolly.scalajs-react" %%% "core" % versions.client.scalajsReact, - "com.github.japgolly.scalajs-react" %%% "extra" % versions.client.scalajsReact, - "com.github.japgolly.scalajs-react" %%% "ext-scalaz71" % versions.client.scalajsReact, - "com.github.japgolly.scalajs-react" %%% "ext-monocle" % versions.client.scalajsReact, - "com.github.japgolly.scalacss" %%% "ext-react" % versions.client.scalaCSS, - "org.scala-js" %%% "scalajs-dom" % versions.client.scalaDom, - "org.zalando.laas" % "nakadi-clients" % "2.0-SNAPSHOT" withSources() withJavadoc() - )) - - val jsDependencies = Def.setting(Seq( - "org.webjars.npm" % "react" % versions.js.react / "react-with-addons.js" minified "react-with-addons.min.js" commonJSName "React", - "org.webjars.npm" % "react-dom" % versions.js.react / "react-dom.js" commonJSName "ReactDOM" minified "react-dom.min.js" dependsOn "react-with-addons.js", - "org.webjars" % "jquery" % versions.js.jQuery / "jquery.js" minified "jquery.min.js", - RuntimeDOM % "test" - )) -} - - -object versions { - - object common { - val scala = "2.11.7" - val scalaRx = "0.2.8" - val autowire = "0.2.5" - val booPickle = "1.1.0" - val uTest = "0.3.1" - } - - object client { - val scalaDom = "0.8.2" - val scalajsReact = "0.10.1" - val scalaCSS = "0.3.1" - } - - object js { - val jQuery = "2.1.4" - val react = "0.14.2" - } - - object server { - val silhouette = "3.0.4" - val macwire = "2.1.0" - val playScripts = "0.3.0" - } - -} diff --git a/examples/scalajs-play-core-react/project/build.properties b/examples/scalajs-play-core-react/project/build.properties deleted file mode 100644 index 443bf66..0000000 --- a/examples/scalajs-play-core-react/project/build.properties +++ /dev/null @@ -1,4 +0,0 @@ -#Activator-generated Properties -#Fri Apr 22 09:23:32 CEST 2016 -template.uuid=03132e10-5044-46d1-ac44-2f753fd1acd0 -sbt.version=0.13.8 diff --git a/examples/scalajs-play-core-react/project/plugins.sbt b/examples/scalajs-play-core-react/project/plugins.sbt deleted file mode 100644 index 497f287..0000000 --- a/examples/scalajs-play-core-react/project/plugins.sbt +++ /dev/null @@ -1,13 +0,0 @@ -logLevel := Level.Warn - -resolvers += "Typesafe Releases" at "http://repo.typesafe.com/typesafe/releases/" - -addSbtPlugin("com.typesafe.sbt" % "sbt-less" % "1.0.6") - -addSbtPlugin("org.scala-js" % "sbt-scalajs" % "0.6.5") - -addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % "1.0.0") - -addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.4.3") - -addSbtPlugin("com.vmunier" % "sbt-play-scalajs" % "0.2.8") \ No newline at end of file diff --git a/examples/scalajs-play-core-react/server/src/main/assets/main.less b/examples/scalajs-play-core-react/server/src/main/assets/main.less deleted file mode 100644 index e69de29..0000000 diff --git a/examples/scalajs-play-core-react/server/src/main/assets/material/material.css b/examples/scalajs-play-core-react/server/src/main/assets/material/material.css deleted file mode 100644 index cdd0a01..0000000 --- a/examples/scalajs-play-core-react/server/src/main/assets/material/material.css +++ /dev/null @@ -1,9751 +0,0 @@ -/** - * material-design-lite - Material Design Components in CSS, JS and HTML - * @version v1.0.6 - * @license Apache-2.0 - * @copyright 2015 Google, Inc. - * @link https://github.com/google/material-design-lite - */ -@charset "UTF-8"; -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/* Material Design Lite */ -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/*------------------------------------* $CONTENTS -\*------------------------------------*/ -/** - * STYLE GUIDE VARIABLES------------------Declarations of Sass variables - * -----Typography - * -----Colors - * -----Textfield - * -----Switch - * -----Spinner - * -----Radio - * -----Menu - * -----List - * -----Layout - * -----Icon toggles - * -----Footer - * -----Column - * -----Checkbox - * -----Card - * -----Button - * -----Animation - * -----Progress - * -----Badge - * -----Shadows - * -----Grid - * -----Data table - */ -/* ========== TYPOGRAPHY ========== */ -/* We're splitting fonts into "preferred" and "performance" in order to optimize - page loading. For important text, such as the body, we want it to load - immediately and not wait for the web font load, whereas for other sections, - such as headers and titles, we're OK with things taking a bit longer to load. - We do have some optional classes and parameters in the mixins, in case you - definitely want to make sure you're using the preferred font and don't mind - the performance hit. - We should be able to improve on this once CSS Font Loading L3 becomes more - widely available. -*/ -/* ========== COLORS ========== */ -/** -* -* Material design color palettes. -* @see http://www.google.com/design/spec/style/color.html -* -**/ -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/* ========== Color Palettes ========== */ -/* colors.scss */ -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/* ========== IMAGES ========== */ -/* ========== Color & Themes ========== */ -/* ========== Typography ========== */ -/* ========== Components ========== */ -/* ========== Standard Buttons ========== */ -/* ========== Icon Toggles ========== */ -/* ========== Radio Buttons ========== */ -/* ========== Ripple effect ========== */ -/* ========== Layout ========== */ -/* ========== Content Tabs ========== */ -/* ========== Checkboxes ========== */ -/* ========== Switches ========== */ -/* ========== Spinner ========== */ -/* ========== Text fields ========== */ -/* ========== Card ========== */ -/* ========== Sliders ========== */ -/* ========== Progress ========== */ -/* ========== List ========== */ -/* ========== Item ========== */ -/* ========== Dropdown menu ========== */ -/* ========== Tooltips ========== */ -/* ========== Footer ========== */ -/* TEXTFIELD */ -/* SWITCH */ -/* SPINNER */ -/* RADIO */ -/* MENU */ -/* LIST */ -/* LAYOUT */ -/* ICON TOGGLE */ -/* FOOTER */ -/*mega-footer*/ -/*mini-footer*/ -/* CHECKBOX */ -/* CARD */ -/* Card dimensions */ -/* Cover image */ -/* BUTTON */ -/** - * - * Dimensions - * - */ -/* ANIMATION */ -/* PROGRESS */ -/* BADGE */ -/* SHADOWS */ -/* GRID */ -/* DATA TABLE */ -/* TOOLTIP */ -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/* Typography */ -/* Shadows */ -/* Animations */ -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/*------------------------------------* $CONTENTS -\*------------------------------------*/ -/** - * STYLE GUIDE VARIABLES------------------Declarations of Sass variables - * -----Typography - * -----Colors - * -----Textfield - * -----Switch - * -----Spinner - * -----Radio - * -----Menu - * -----List - * -----Layout - * -----Icon toggles - * -----Footer - * -----Column - * -----Checkbox - * -----Card - * -----Button - * -----Animation - * -----Progress - * -----Badge - * -----Shadows - * -----Grid - * -----Data table - */ -/* ========== TYPOGRAPHY ========== */ -/* We're splitting fonts into "preferred" and "performance" in order to optimize - page loading. For important text, such as the body, we want it to load - immediately and not wait for the web font load, whereas for other sections, - such as headers and titles, we're OK with things taking a bit longer to load. - We do have some optional classes and parameters in the mixins, in case you - definitely want to make sure you're using the preferred font and don't mind - the performance hit. - We should be able to improve on this once CSS Font Loading L3 becomes more - widely available. -*/ -/* ========== COLORS ========== */ -/** -* -* Material design color palettes. -* @see http://www.google.com/design/spec/style/color.html -* -**/ -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/* ========== Color Palettes ========== */ -/* colors.scss */ -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/* ========== IMAGES ========== */ -/* ========== Color & Themes ========== */ -/* ========== Typography ========== */ -/* ========== Components ========== */ -/* ========== Standard Buttons ========== */ -/* ========== Icon Toggles ========== */ -/* ========== Radio Buttons ========== */ -/* ========== Ripple effect ========== */ -/* ========== Layout ========== */ -/* ========== Content Tabs ========== */ -/* ========== Checkboxes ========== */ -/* ========== Switches ========== */ -/* ========== Spinner ========== */ -/* ========== Text fields ========== */ -/* ========== Card ========== */ -/* ========== Sliders ========== */ -/* ========== Progress ========== */ -/* ========== List ========== */ -/* ========== Item ========== */ -/* ========== Dropdown menu ========== */ -/* ========== Tooltips ========== */ -/* ========== Footer ========== */ -/* TEXTFIELD */ -/* SWITCH */ -/* SPINNER */ -/* RADIO */ -/* MENU */ -/* LIST */ -/* LAYOUT */ -/* ICON TOGGLE */ -/* FOOTER */ -/*mega-footer*/ -/*mini-footer*/ -/* CHECKBOX */ -/* CARD */ -/* Card dimensions */ -/* Cover image */ -/* BUTTON */ -/** - * - * Dimensions - * - */ -/* ANIMATION */ -/* PROGRESS */ -/* BADGE */ -/* SHADOWS */ -/* GRID */ -/* DATA TABLE */ -/* TOOLTIP */ -/* - * What follows is the result of much research on cross-browser styling. - * Credit left inline and big thanks to Nicolas Gallagher, Jonathan Neal, - * Kroc Camen, and the H5BP dev community and team. - */ -/* ========================================================================== - Base styles: opinionated defaults - ========================================================================== */ -html { - color: rgba(0,0,0, 0.87); - font-size: 1em; - line-height: 1.4; } - -/* - * Remove text-shadow in selection highlight: - * https://twitter.com/miketaylr/status/12228805301 - * - * These selection rule sets have to be separate. - * Customize the background color to match your design. - */ -::-moz-selection { - background: #b3d4fc; - text-shadow: none; } -::selection { - background: #b3d4fc; - text-shadow: none; } - -/* - * A better looking default horizontal rule - */ -hr { - display: block; - height: 1px; - border: 0; - border-top: 1px solid #ccc; - margin: 1em 0; - padding: 0; } - -/* - * Remove the gap between audio, canvas, iframes, - * images, videos and the bottom of their containers: - * https://github.com/h5bp/html5-boilerplate/issues/440 - */ -audio, -canvas, -iframe, -img, -svg, -video { - vertical-align: middle; } - -/* - * Remove default fieldset styles. - */ -fieldset { - border: 0; - margin: 0; - padding: 0; } - -/* - * Allow only vertical resizing of textareas. - */ -textarea { - resize: vertical; } - -/* ========================================================================== - Browser Upgrade Prompt - ========================================================================== */ -.browserupgrade { - margin: 0.2em 0; - background: #ccc; - color: #000; - padding: 0.2em 0; } - -/* ========================================================================== - Author's custom styles - ========================================================================== */ -/* ========================================================================== - Helper classes - ========================================================================== */ -/* - * Hide visually and from screen readers: - */ -.hidden { - display: none !important; } - -/* - * Hide only visually, but have it available for screen readers: - * http://snook.ca/archives/html_and_css/hiding-content-for-accessibility - */ -.visuallyhidden { - border: 0; - clip: rect(0 0 0 0); - height: 1px; - margin: -1px; - overflow: hidden; - padding: 0; - position: absolute; - width: 1px; } - -/* - * Extends the .visuallyhidden class to allow the element - * to be focusable when navigated to via the keyboard: - * https://www.drupal.org/node/897638 - */ -.visuallyhidden.focusable:active, -.visuallyhidden.focusable:focus { - clip: auto; - height: auto; - margin: 0; - overflow: visible; - position: static; - width: auto; } - -/* - * Hide visually and from screen readers, but maintain layout - */ -.invisible { - visibility: hidden; } - -/* - * Clearfix: contain floats - * - * For modern browsers - * 1. The space content is one way to avoid an Opera bug when the - * `contenteditable` attribute is included anywhere else in the document. - * Otherwise it causes space to appear at the top and bottom of elements - * that receive the `clearfix` class. - * 2. The use of `table` rather than `block` is only necessary if using - * `:before` to contain the top-margins of child elements. - */ -.clearfix:before, -.clearfix:after { - content: " "; - /* 1 */ - display: table; - /* 2 */ } - -.clearfix:after { - clear: both; } - -/* ========================================================================== - EXAMPLE Media Queries for Responsive Design. - These examples override the primary ('mobile first') styles. - Modify as content requires. - ========================================================================== */ -/* ========================================================================== - Print styles. - Inlined to avoid the additional HTTP request: - http://www.phpied.com/delay-loading-your-print-css/ - ========================================================================== */ -@media print { - *, - *:before, - *:after, - *:first-letter, - *:first-line { - background: transparent !important; - color: #000 !important; - /* Black prints faster: http://www.sanbeiji.com/archives/953 */ - box-shadow: none !important; - text-shadow: none !important; } - a, - a:visited { - text-decoration: underline; } - a[href]:after { - content: " (" attr(href) ")"; } - abbr[title]:after { - content: " (" attr(title) ")"; } - /* - * Don't show links that are fragment identifiers, - * or use the `javascript:` pseudo protocol - */ - a[href^="#"]:after, - a[href^="javascript:"]:after { - content: ""; } - pre, - blockquote { - border: 1px solid #999; - page-break-inside: avoid; } - /* - * Printing Tables: - * http://css-discuss.incutio.com/wiki/Printing_Tables - */ - thead { - display: table-header-group; } - tr, - img { - page-break-inside: avoid; } - img { - max-width: 100% !important; } - p, - h2, - h3 { - orphans: 3; - widows: 3; } - h2, - h3 { - page-break-after: avoid; } } - -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/* Remove the unwanted box around FAB buttons */ -/* More info: http://goo.gl/IPwKi */ -a, .mdl-accordion, .mdl-button, .mdl-card, .mdl-checkbox, .mdl-dropdown-menu, -.mdl-icon-toggle, .mdl-item, .mdl-radio, .mdl-slider, .mdl-switch, .mdl-tabs__tab { - -webkit-tap-highlight-color: transparent; - -webkit-tap-highlight-color: rgba(255, 255, 255, 0); } - -/* - * Make html take up the entire screen - * Then set touch-action to avoid touch delay on mobile IE - */ -html { - width: 100%; - height: 100%; - -ms-touch-action: manipulation; - touch-action: manipulation; } - -/* -* Make body take up the entire screen -* Remove body margin so layout containers don't cause extra overflow. -*/ -body { - width: 100%; - min-height: 100%; - margin: 0; } - -/* - * Main display reset for IE support. - * Source: http://weblog.west-wind.com/posts/2015/Jan/12/main-HTML5-Tag-not-working-in-Internet-Explorer-91011 - */ -main { - display: block; } - -/* -* Apply no display to elements with the hidden attribute. -* IE 9 and 10 support. -*/ -*[hidden] { - display: none !important; } - -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/*------------------------------------* $CONTENTS -\*------------------------------------*/ -/** - * STYLE GUIDE VARIABLES------------------Declarations of Sass variables - * -----Typography - * -----Colors - * -----Textfield - * -----Switch - * -----Spinner - * -----Radio - * -----Menu - * -----List - * -----Layout - * -----Icon toggles - * -----Footer - * -----Column - * -----Checkbox - * -----Card - * -----Button - * -----Animation - * -----Progress - * -----Badge - * -----Shadows - * -----Grid - * -----Data table - */ -/* ========== TYPOGRAPHY ========== */ -/* We're splitting fonts into "preferred" and "performance" in order to optimize - page loading. For important text, such as the body, we want it to load - immediately and not wait for the web font load, whereas for other sections, - such as headers and titles, we're OK with things taking a bit longer to load. - We do have some optional classes and parameters in the mixins, in case you - definitely want to make sure you're using the preferred font and don't mind - the performance hit. - We should be able to improve on this once CSS Font Loading L3 becomes more - widely available. -*/ -/* ========== COLORS ========== */ -/** -* -* Material design color palettes. -* @see http://www.google.com/design/spec/style/color.html -* -**/ -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/* ========== Color Palettes ========== */ -/* colors.scss */ -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/* ========== IMAGES ========== */ -/* ========== Color & Themes ========== */ -/* ========== Typography ========== */ -/* ========== Components ========== */ -/* ========== Standard Buttons ========== */ -/* ========== Icon Toggles ========== */ -/* ========== Radio Buttons ========== */ -/* ========== Ripple effect ========== */ -/* ========== Layout ========== */ -/* ========== Content Tabs ========== */ -/* ========== Checkboxes ========== */ -/* ========== Switches ========== */ -/* ========== Spinner ========== */ -/* ========== Text fields ========== */ -/* ========== Card ========== */ -/* ========== Sliders ========== */ -/* ========== Progress ========== */ -/* ========== List ========== */ -/* ========== Item ========== */ -/* ========== Dropdown menu ========== */ -/* ========== Tooltips ========== */ -/* ========== Footer ========== */ -/* TEXTFIELD */ -/* SWITCH */ -/* SPINNER */ -/* RADIO */ -/* MENU */ -/* LIST */ -/* LAYOUT */ -/* ICON TOGGLE */ -/* FOOTER */ -/*mega-footer*/ -/*mini-footer*/ -/* CHECKBOX */ -/* CARD */ -/* Card dimensions */ -/* Cover image */ -/* BUTTON */ -/** - * - * Dimensions - * - */ -/* ANIMATION */ -/* PROGRESS */ -/* BADGE */ -/* SHADOWS */ -/* GRID */ -/* DATA TABLE */ -/* TOOLTIP */ -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/* Typography */ -/* Shadows */ -/* Animations */ -html, body { - font-family: "Helvetica", "Arial", sans-serif; - font-size: 14px; - font-weight: 400; - line-height: 20px; } - -h1, h2, h3, h4, h5, h6, p { - margin: 0; - padding: 0; } - -/** - * Styles for HTML elements - */ -h1 small, h2 small, h3 small, h4 small, h5 small, h6 small { - font-family: "Roboto", "Helvetica", "Arial", sans-serif; - font-size: 56px; - font-weight: 400; - line-height: 1.35; - letter-spacing: -0.02em; - opacity: 0.54; - font-size: 0.6em; } - -h1 { - font-family: "Roboto", "Helvetica", "Arial", sans-serif; - font-size: 56px; - font-weight: 400; - line-height: 1.35; - letter-spacing: -0.02em; - margin-top: 24px; - margin-bottom: 24px; } - -h2 { - font-family: "Roboto", "Helvetica", "Arial", sans-serif; - font-size: 45px; - font-weight: 400; - line-height: 48px; - margin-top: 24px; - margin-bottom: 24px; } - -h3 { - font-family: "Roboto", "Helvetica", "Arial", sans-serif; - font-size: 34px; - font-weight: 400; - line-height: 40px; - margin-top: 24px; - margin-bottom: 24px; } - -h4 { - font-family: "Roboto", "Helvetica", "Arial", sans-serif; - font-size: 24px; - font-weight: 400; - line-height: 32px; - -moz-osx-font-smoothing: grayscale; - margin-top: 24px; - margin-bottom: 16px; } - -h5 { - font-family: "Roboto", "Helvetica", "Arial", sans-serif; - font-size: 20px; - font-weight: 500; - line-height: 1; - letter-spacing: 0.02em; - margin-top: 24px; - margin-bottom: 16px; } - -h6 { - font-family: "Roboto", "Helvetica", "Arial", sans-serif; - font-size: 16px; - font-weight: 400; - line-height: 24px; - letter-spacing: 0.04em; - margin-top: 24px; - margin-bottom: 16px; } - -p { - font-size: 14px; - font-weight: 400; - line-height: 24px; - letter-spacing: 0; - margin-bottom: 16px; } - -a { - color: rgb(255,64,129); - font-weight: 500; } - -blockquote { - font-family: "Roboto", "Helvetica", "Arial", sans-serif; - position: relative; - font-size: 24px; - font-weight: 300; - font-style: italic; - line-height: 1.35; - letter-spacing: 0.08em; } - blockquote:before { - position: absolute; - left: -0.5em; - content: '“'; } - blockquote:after { - content: '”'; - margin-left: -0.05em; } - -mark { - background-color: #f4ff81; } - -dt { - font-weight: 700; } - -address { - font-size: 12px; - font-weight: 400; - line-height: 1; - letter-spacing: 0; - font-style: normal; } - -ul, ol { - font-size: 14px; - font-weight: 400; - line-height: 24px; - letter-spacing: 0; } - -/** - * Class Name Styles - */ -.mdl-typography--display-4 { - font-family: "Roboto", "Helvetica", "Arial", sans-serif; - font-size: 112px; - font-weight: 300; - line-height: 1; - letter-spacing: -0.04em; } - -.mdl-typography--display-4-color-contrast { - font-family: "Roboto", "Helvetica", "Arial", sans-serif; - font-size: 112px; - font-weight: 300; - line-height: 1; - letter-spacing: -0.04em; - opacity: 0.54; } - -.mdl-typography--display-3 { - font-family: "Roboto", "Helvetica", "Arial", sans-serif; - font-size: 56px; - font-weight: 400; - line-height: 1.35; - letter-spacing: -0.02em; } - -.mdl-typography--display-3-color-contrast { - font-family: "Roboto", "Helvetica", "Arial", sans-serif; - font-size: 56px; - font-weight: 400; - line-height: 1.35; - letter-spacing: -0.02em; - opacity: 0.54; } - -.mdl-typography--display-2 { - font-family: "Roboto", "Helvetica", "Arial", sans-serif; - font-size: 45px; - font-weight: 400; - line-height: 48px; } - -.mdl-typography--display-2-color-contrast { - font-family: "Roboto", "Helvetica", "Arial", sans-serif; - font-size: 45px; - font-weight: 400; - line-height: 48px; - opacity: 0.54; } - -.mdl-typography--display-1 { - font-family: "Roboto", "Helvetica", "Arial", sans-serif; - font-size: 34px; - font-weight: 400; - line-height: 40px; } - -.mdl-typography--display-1-color-contrast { - font-family: "Roboto", "Helvetica", "Arial", sans-serif; - font-size: 34px; - font-weight: 400; - line-height: 40px; - opacity: 0.54; } - -.mdl-typography--headline { - font-family: "Roboto", "Helvetica", "Arial", sans-serif; - font-size: 24px; - font-weight: 400; - line-height: 32px; - -moz-osx-font-smoothing: grayscale; } - -.mdl-typography--headline-color-contrast { - font-family: "Roboto", "Helvetica", "Arial", sans-serif; - font-size: 24px; - font-weight: 400; - line-height: 32px; - -moz-osx-font-smoothing: grayscale; - opacity: 0.87; } - -.mdl-typography--title { - font-family: "Roboto", "Helvetica", "Arial", sans-serif; - font-size: 20px; - font-weight: 500; - line-height: 1; - letter-spacing: 0.02em; } - -.mdl-typography--title-color-contrast { - font-family: "Roboto", "Helvetica", "Arial", sans-serif; - font-size: 20px; - font-weight: 500; - line-height: 1; - letter-spacing: 0.02em; - opacity: 0.87; } - -.mdl-typography--subhead { - font-family: "Roboto", "Helvetica", "Arial", sans-serif; - font-size: 16px; - font-weight: 400; - line-height: 24px; - letter-spacing: 0.04em; } - -.mdl-typography--subhead-color-contrast { - font-family: "Roboto", "Helvetica", "Arial", sans-serif; - font-size: 16px; - font-weight: 400; - line-height: 24px; - letter-spacing: 0.04em; - opacity: 0.87; } - -.mdl-typography--body-2 { - font-size: 14px; - font-weight: bold; - line-height: 24px; - letter-spacing: 0; } - -.mdl-typography--body-2-color-contrast { - font-size: 14px; - font-weight: bold; - line-height: 24px; - letter-spacing: 0; - opacity: 0.87; } - -.mdl-typography--body-1 { - font-size: 14px; - font-weight: 400; - line-height: 24px; - letter-spacing: 0; } - -.mdl-typography--body-1-color-contrast { - font-size: 14px; - font-weight: 400; - line-height: 24px; - letter-spacing: 0; - opacity: 0.87; } - -.mdl-typography--body-2-force-preferred-font { - font-family: "Roboto", "Helvetica", "Arial", sans-serif; - font-size: 14px; - font-weight: 500; - line-height: 24px; - letter-spacing: 0; } - -.mdl-typography--body-2-force-preferred-font-color-contrast { - font-family: "Roboto", "Helvetica", "Arial", sans-serif; - font-size: 14px; - font-weight: 500; - line-height: 24px; - letter-spacing: 0; - opacity: 0.87; } - -.mdl-typography--body-1-force-preferred-font { - font-family: "Roboto", "Helvetica", "Arial", sans-serif; - font-size: 14px; - font-weight: 400; - line-height: 24px; - letter-spacing: 0; } - -.mdl-typography--body-1-force-preferred-font-color-contrast { - font-family: "Roboto", "Helvetica", "Arial", sans-serif; - font-size: 14px; - font-weight: 400; - line-height: 24px; - letter-spacing: 0; - opacity: 0.87; } - -.mdl-typography--caption { - font-size: 12px; - font-weight: 400; - line-height: 1; - letter-spacing: 0; } - -.mdl-typography--caption-force-preferred-font { - font-family: "Roboto", "Helvetica", "Arial", sans-serif; - font-size: 12px; - font-weight: 400; - line-height: 1; - letter-spacing: 0; } - -.mdl-typography--caption-color-contrast { - font-size: 12px; - font-weight: 400; - line-height: 1; - letter-spacing: 0; - opacity: 0.54; } - -.mdl-typography--caption-force-preferred-font-color-contrast { - font-family: "Roboto", "Helvetica", "Arial", sans-serif; - font-size: 12px; - font-weight: 400; - line-height: 1; - letter-spacing: 0; - opacity: 0.54; } - -.mdl-typography--menu { - font-family: "Roboto", "Helvetica", "Arial", sans-serif; - font-size: 14px; - font-weight: 500; - line-height: 1; - letter-spacing: 0; } - -.mdl-typography--menu-color-contrast { - font-family: "Roboto", "Helvetica", "Arial", sans-serif; - font-size: 14px; - font-weight: 500; - line-height: 1; - letter-spacing: 0; - opacity: 0.87; } - -.mdl-typography--button { - font-family: "Roboto", "Helvetica", "Arial", sans-serif; - font-size: 14px; - font-weight: 500; - text-transform: uppercase; - line-height: 1; - letter-spacing: 0; } - -.mdl-typography--button-color-contrast { - font-family: "Roboto", "Helvetica", "Arial", sans-serif; - font-size: 14px; - font-weight: 500; - text-transform: uppercase; - line-height: 1; - letter-spacing: 0; - opacity: 0.87; } - -.mdl-typography--text-left { - text-align: left; } - -.mdl-typography--text-right { - text-align: right; } - -.mdl-typography--text-center { - text-align: center; } - -.mdl-typography--text-justify { - text-align: justify; } - -.mdl-typography--text-nowrap { - white-space: nowrap; } - -.mdl-typography--text-lowercase { - text-transform: lowercase; } - -.mdl-typography--text-uppercase { - text-transform: uppercase; } - -.mdl-typography--text-capitalize { - text-transform: capitalize; } - -.mdl-typography--font-thin { - font-weight: 200 !important; } - -.mdl-typography--font-light { - font-weight: 300 !important; } - -.mdl-typography--font-regular { - font-weight: 400 !important; } - -.mdl-typography--font-medium { - font-weight: 500 !important; } - -.mdl-typography--font-bold { - font-weight: 700 !important; } - -.mdl-typography--font-black { - font-weight: 900 !important; } - -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/*------------------------------------* $CONTENTS -\*------------------------------------*/ -/** - * STYLE GUIDE VARIABLES------------------Declarations of Sass variables - * -----Typography - * -----Colors - * -----Textfield - * -----Switch - * -----Spinner - * -----Radio - * -----Menu - * -----List - * -----Layout - * -----Icon toggles - * -----Footer - * -----Column - * -----Checkbox - * -----Card - * -----Button - * -----Animation - * -----Progress - * -----Badge - * -----Shadows - * -----Grid - * -----Data table - */ -/* ========== TYPOGRAPHY ========== */ -/* We're splitting fonts into "preferred" and "performance" in order to optimize - page loading. For important text, such as the body, we want it to load - immediately and not wait for the web font load, whereas for other sections, - such as headers and titles, we're OK with things taking a bit longer to load. - We do have some optional classes and parameters in the mixins, in case you - definitely want to make sure you're using the preferred font and don't mind - the performance hit. - We should be able to improve on this once CSS Font Loading L3 becomes more - widely available. -*/ -/* ========== COLORS ========== */ -/** -* -* Material design color palettes. -* @see http://www.google.com/design/spec/style/color.html -* -**/ -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/* ========== Color Palettes ========== */ -/* colors.scss */ -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/* ========== IMAGES ========== */ -/* ========== Color & Themes ========== */ -/* ========== Typography ========== */ -/* ========== Components ========== */ -/* ========== Standard Buttons ========== */ -/* ========== Icon Toggles ========== */ -/* ========== Radio Buttons ========== */ -/* ========== Ripple effect ========== */ -/* ========== Layout ========== */ -/* ========== Content Tabs ========== */ -/* ========== Checkboxes ========== */ -/* ========== Switches ========== */ -/* ========== Spinner ========== */ -/* ========== Text fields ========== */ -/* ========== Card ========== */ -/* ========== Sliders ========== */ -/* ========== Progress ========== */ -/* ========== List ========== */ -/* ========== Item ========== */ -/* ========== Dropdown menu ========== */ -/* ========== Tooltips ========== */ -/* ========== Footer ========== */ -/* TEXTFIELD */ -/* SWITCH */ -/* SPINNER */ -/* RADIO */ -/* MENU */ -/* LIST */ -/* LAYOUT */ -/* ICON TOGGLE */ -/* FOOTER */ -/*mega-footer*/ -/*mini-footer*/ -/* CHECKBOX */ -/* CARD */ -/* Card dimensions */ -/* Cover image */ -/* BUTTON */ -/** - * - * Dimensions - * - */ -/* ANIMATION */ -/* PROGRESS */ -/* BADGE */ -/* SHADOWS */ -/* GRID */ -/* DATA TABLE */ -/* TOOLTIP */ -.mdl-color-text--red { - color: rgb(244,67,54) !important; } - -.mdl-color--red { - background-color: rgb(244,67,54) !important; } - -.mdl-color-text--red-50 { - color: rgb(255,235,238) !important; } - -.mdl-color--red-50 { - background-color: rgb(255,235,238) !important; } - -.mdl-color-text--red-100 { - color: rgb(255,205,210) !important; } - -.mdl-color--red-100 { - background-color: rgb(255,205,210) !important; } - -.mdl-color-text--red-200 { - color: rgb(239,154,154) !important; } - -.mdl-color--red-200 { - background-color: rgb(239,154,154) !important; } - -.mdl-color-text--red-300 { - color: rgb(229,115,115) !important; } - -.mdl-color--red-300 { - background-color: rgb(229,115,115) !important; } - -.mdl-color-text--red-400 { - color: rgb(239,83,80) !important; } - -.mdl-color--red-400 { - background-color: rgb(239,83,80) !important; } - -.mdl-color-text--red-500 { - color: rgb(244,67,54) !important; } - -.mdl-color--red-500 { - background-color: rgb(244,67,54) !important; } - -.mdl-color-text--red-600 { - color: rgb(229,57,53) !important; } - -.mdl-color--red-600 { - background-color: rgb(229,57,53) !important; } - -.mdl-color-text--red-700 { - color: rgb(211,47,47) !important; } - -.mdl-color--red-700 { - background-color: rgb(211,47,47) !important; } - -.mdl-color-text--red-800 { - color: rgb(198,40,40) !important; } - -.mdl-color--red-800 { - background-color: rgb(198,40,40) !important; } - -.mdl-color-text--red-900 { - color: rgb(183,28,28) !important; } - -.mdl-color--red-900 { - background-color: rgb(183,28,28) !important; } - -.mdl-color-text--red-A100 { - color: rgb(255,138,128) !important; } - -.mdl-color--red-A100 { - background-color: rgb(255,138,128) !important; } - -.mdl-color-text--red-A200 { - color: rgb(255,82,82) !important; } - -.mdl-color--red-A200 { - background-color: rgb(255,82,82) !important; } - -.mdl-color-text--red-A400 { - color: rgb(255,23,68) !important; } - -.mdl-color--red-A400 { - background-color: rgb(255,23,68) !important; } - -.mdl-color-text--red-A700 { - color: rgb(213,0,0) !important; } - -.mdl-color--red-A700 { - background-color: rgb(213,0,0) !important; } - -.mdl-color-text--pink { - color: rgb(233,30,99) !important; } - -.mdl-color--pink { - background-color: rgb(233,30,99) !important; } - -.mdl-color-text--pink-50 { - color: rgb(252,228,236) !important; } - -.mdl-color--pink-50 { - background-color: rgb(252,228,236) !important; } - -.mdl-color-text--pink-100 { - color: rgb(248,187,208) !important; } - -.mdl-color--pink-100 { - background-color: rgb(248,187,208) !important; } - -.mdl-color-text--pink-200 { - color: rgb(244,143,177) !important; } - -.mdl-color--pink-200 { - background-color: rgb(244,143,177) !important; } - -.mdl-color-text--pink-300 { - color: rgb(240,98,146) !important; } - -.mdl-color--pink-300 { - background-color: rgb(240,98,146) !important; } - -.mdl-color-text--pink-400 { - color: rgb(236,64,122) !important; } - -.mdl-color--pink-400 { - background-color: rgb(236,64,122) !important; } - -.mdl-color-text--pink-500 { - color: rgb(233,30,99) !important; } - -.mdl-color--pink-500 { - background-color: rgb(233,30,99) !important; } - -.mdl-color-text--pink-600 { - color: rgb(216,27,96) !important; } - -.mdl-color--pink-600 { - background-color: rgb(216,27,96) !important; } - -.mdl-color-text--pink-700 { - color: rgb(194,24,91) !important; } - -.mdl-color--pink-700 { - background-color: rgb(194,24,91) !important; } - -.mdl-color-text--pink-800 { - color: rgb(173,20,87) !important; } - -.mdl-color--pink-800 { - background-color: rgb(173,20,87) !important; } - -.mdl-color-text--pink-900 { - color: rgb(136,14,79) !important; } - -.mdl-color--pink-900 { - background-color: rgb(136,14,79) !important; } - -.mdl-color-text--pink-A100 { - color: rgb(255,128,171) !important; } - -.mdl-color--pink-A100 { - background-color: rgb(255,128,171) !important; } - -.mdl-color-text--pink-A200 { - color: rgb(255,64,129) !important; } - -.mdl-color--pink-A200 { - background-color: rgb(255,64,129) !important; } - -.mdl-color-text--pink-A400 { - color: rgb(245,0,87) !important; } - -.mdl-color--pink-A400 { - background-color: rgb(245,0,87) !important; } - -.mdl-color-text--pink-A700 { - color: rgb(197,17,98) !important; } - -.mdl-color--pink-A700 { - background-color: rgb(197,17,98) !important; } - -.mdl-color-text--purple { - color: rgb(156,39,176) !important; } - -.mdl-color--purple { - background-color: rgb(156,39,176) !important; } - -.mdl-color-text--purple-50 { - color: rgb(243,229,245) !important; } - -.mdl-color--purple-50 { - background-color: rgb(243,229,245) !important; } - -.mdl-color-text--purple-100 { - color: rgb(225,190,231) !important; } - -.mdl-color--purple-100 { - background-color: rgb(225,190,231) !important; } - -.mdl-color-text--purple-200 { - color: rgb(206,147,216) !important; } - -.mdl-color--purple-200 { - background-color: rgb(206,147,216) !important; } - -.mdl-color-text--purple-300 { - color: rgb(186,104,200) !important; } - -.mdl-color--purple-300 { - background-color: rgb(186,104,200) !important; } - -.mdl-color-text--purple-400 { - color: rgb(171,71,188) !important; } - -.mdl-color--purple-400 { - background-color: rgb(171,71,188) !important; } - -.mdl-color-text--purple-500 { - color: rgb(156,39,176) !important; } - -.mdl-color--purple-500 { - background-color: rgb(156,39,176) !important; } - -.mdl-color-text--purple-600 { - color: rgb(142,36,170) !important; } - -.mdl-color--purple-600 { - background-color: rgb(142,36,170) !important; } - -.mdl-color-text--purple-700 { - color: rgb(123,31,162) !important; } - -.mdl-color--purple-700 { - background-color: rgb(123,31,162) !important; } - -.mdl-color-text--purple-800 { - color: rgb(106,27,154) !important; } - -.mdl-color--purple-800 { - background-color: rgb(106,27,154) !important; } - -.mdl-color-text--purple-900 { - color: rgb(74,20,140) !important; } - -.mdl-color--purple-900 { - background-color: rgb(74,20,140) !important; } - -.mdl-color-text--purple-A100 { - color: rgb(234,128,252) !important; } - -.mdl-color--purple-A100 { - background-color: rgb(234,128,252) !important; } - -.mdl-color-text--purple-A200 { - color: rgb(224,64,251) !important; } - -.mdl-color--purple-A200 { - background-color: rgb(224,64,251) !important; } - -.mdl-color-text--purple-A400 { - color: rgb(213,0,249) !important; } - -.mdl-color--purple-A400 { - background-color: rgb(213,0,249) !important; } - -.mdl-color-text--purple-A700 { - color: rgb(170,0,255) !important; } - -.mdl-color--purple-A700 { - background-color: rgb(170,0,255) !important; } - -.mdl-color-text--deep-purple { - color: rgb(103,58,183) !important; } - -.mdl-color--deep-purple { - background-color: rgb(103,58,183) !important; } - -.mdl-color-text--deep-purple-50 { - color: rgb(237,231,246) !important; } - -.mdl-color--deep-purple-50 { - background-color: rgb(237,231,246) !important; } - -.mdl-color-text--deep-purple-100 { - color: rgb(209,196,233) !important; } - -.mdl-color--deep-purple-100 { - background-color: rgb(209,196,233) !important; } - -.mdl-color-text--deep-purple-200 { - color: rgb(179,157,219) !important; } - -.mdl-color--deep-purple-200 { - background-color: rgb(179,157,219) !important; } - -.mdl-color-text--deep-purple-300 { - color: rgb(149,117,205) !important; } - -.mdl-color--deep-purple-300 { - background-color: rgb(149,117,205) !important; } - -.mdl-color-text--deep-purple-400 { - color: rgb(126,87,194) !important; } - -.mdl-color--deep-purple-400 { - background-color: rgb(126,87,194) !important; } - -.mdl-color-text--deep-purple-500 { - color: rgb(103,58,183) !important; } - -.mdl-color--deep-purple-500 { - background-color: rgb(103,58,183) !important; } - -.mdl-color-text--deep-purple-600 { - color: rgb(94,53,177) !important; } - -.mdl-color--deep-purple-600 { - background-color: rgb(94,53,177) !important; } - -.mdl-color-text--deep-purple-700 { - color: rgb(81,45,168) !important; } - -.mdl-color--deep-purple-700 { - background-color: rgb(81,45,168) !important; } - -.mdl-color-text--deep-purple-800 { - color: rgb(69,39,160) !important; } - -.mdl-color--deep-purple-800 { - background-color: rgb(69,39,160) !important; } - -.mdl-color-text--deep-purple-900 { - color: rgb(49,27,146) !important; } - -.mdl-color--deep-purple-900 { - background-color: rgb(49,27,146) !important; } - -.mdl-color-text--deep-purple-A100 { - color: rgb(179,136,255) !important; } - -.mdl-color--deep-purple-A100 { - background-color: rgb(179,136,255) !important; } - -.mdl-color-text--deep-purple-A200 { - color: rgb(124,77,255) !important; } - -.mdl-color--deep-purple-A200 { - background-color: rgb(124,77,255) !important; } - -.mdl-color-text--deep-purple-A400 { - color: rgb(101,31,255) !important; } - -.mdl-color--deep-purple-A400 { - background-color: rgb(101,31,255) !important; } - -.mdl-color-text--deep-purple-A700 { - color: rgb(98,0,234) !important; } - -.mdl-color--deep-purple-A700 { - background-color: rgb(98,0,234) !important; } - -.mdl-color-text--indigo { - color: rgb(63,81,181) !important; } - -.mdl-color--indigo { - background-color: rgb(63,81,181) !important; } - -.mdl-color-text--indigo-50 { - color: rgb(232,234,246) !important; } - -.mdl-color--indigo-50 { - background-color: rgb(232,234,246) !important; } - -.mdl-color-text--indigo-100 { - color: rgb(197,202,233) !important; } - -.mdl-color--indigo-100 { - background-color: rgb(197,202,233) !important; } - -.mdl-color-text--indigo-200 { - color: rgb(159,168,218) !important; } - -.mdl-color--indigo-200 { - background-color: rgb(159,168,218) !important; } - -.mdl-color-text--indigo-300 { - color: rgb(121,134,203) !important; } - -.mdl-color--indigo-300 { - background-color: rgb(121,134,203) !important; } - -.mdl-color-text--indigo-400 { - color: rgb(92,107,192) !important; } - -.mdl-color--indigo-400 { - background-color: rgb(92,107,192) !important; } - -.mdl-color-text--indigo-500 { - color: rgb(63,81,181) !important; } - -.mdl-color--indigo-500 { - background-color: rgb(63,81,181) !important; } - -.mdl-color-text--indigo-600 { - color: rgb(57,73,171) !important; } - -.mdl-color--indigo-600 { - background-color: rgb(57,73,171) !important; } - -.mdl-color-text--indigo-700 { - color: rgb(48,63,159) !important; } - -.mdl-color--indigo-700 { - background-color: rgb(48,63,159) !important; } - -.mdl-color-text--indigo-800 { - color: rgb(40,53,147) !important; } - -.mdl-color--indigo-800 { - background-color: rgb(40,53,147) !important; } - -.mdl-color-text--indigo-900 { - color: rgb(26,35,126) !important; } - -.mdl-color--indigo-900 { - background-color: rgb(26,35,126) !important; } - -.mdl-color-text--indigo-A100 { - color: rgb(140,158,255) !important; } - -.mdl-color--indigo-A100 { - background-color: rgb(140,158,255) !important; } - -.mdl-color-text--indigo-A200 { - color: rgb(83,109,254) !important; } - -.mdl-color--indigo-A200 { - background-color: rgb(83,109,254) !important; } - -.mdl-color-text--indigo-A400 { - color: rgb(61,90,254) !important; } - -.mdl-color--indigo-A400 { - background-color: rgb(61,90,254) !important; } - -.mdl-color-text--indigo-A700 { - color: rgb(48,79,254) !important; } - -.mdl-color--indigo-A700 { - background-color: rgb(48,79,254) !important; } - -.mdl-color-text--blue { - color: rgb(33,150,243) !important; } - -.mdl-color--blue { - background-color: rgb(33,150,243) !important; } - -.mdl-color-text--blue-50 { - color: rgb(227,242,253) !important; } - -.mdl-color--blue-50 { - background-color: rgb(227,242,253) !important; } - -.mdl-color-text--blue-100 { - color: rgb(187,222,251) !important; } - -.mdl-color--blue-100 { - background-color: rgb(187,222,251) !important; } - -.mdl-color-text--blue-200 { - color: rgb(144,202,249) !important; } - -.mdl-color--blue-200 { - background-color: rgb(144,202,249) !important; } - -.mdl-color-text--blue-300 { - color: rgb(100,181,246) !important; } - -.mdl-color--blue-300 { - background-color: rgb(100,181,246) !important; } - -.mdl-color-text--blue-400 { - color: rgb(66,165,245) !important; } - -.mdl-color--blue-400 { - background-color: rgb(66,165,245) !important; } - -.mdl-color-text--blue-500 { - color: rgb(33,150,243) !important; } - -.mdl-color--blue-500 { - background-color: rgb(33,150,243) !important; } - -.mdl-color-text--blue-600 { - color: rgb(30,136,229) !important; } - -.mdl-color--blue-600 { - background-color: rgb(30,136,229) !important; } - -.mdl-color-text--blue-700 { - color: rgb(25,118,210) !important; } - -.mdl-color--blue-700 { - background-color: rgb(25,118,210) !important; } - -.mdl-color-text--blue-800 { - color: rgb(21,101,192) !important; } - -.mdl-color--blue-800 { - background-color: rgb(21,101,192) !important; } - -.mdl-color-text--blue-900 { - color: rgb(13,71,161) !important; } - -.mdl-color--blue-900 { - background-color: rgb(13,71,161) !important; } - -.mdl-color-text--blue-A100 { - color: rgb(130,177,255) !important; } - -.mdl-color--blue-A100 { - background-color: rgb(130,177,255) !important; } - -.mdl-color-text--blue-A200 { - color: rgb(68,138,255) !important; } - -.mdl-color--blue-A200 { - background-color: rgb(68,138,255) !important; } - -.mdl-color-text--blue-A400 { - color: rgb(41,121,255) !important; } - -.mdl-color--blue-A400 { - background-color: rgb(41,121,255) !important; } - -.mdl-color-text--blue-A700 { - color: rgb(41,98,255) !important; } - -.mdl-color--blue-A700 { - background-color: rgb(41,98,255) !important; } - -.mdl-color-text--light-blue { - color: rgb(3,169,244) !important; } - -.mdl-color--light-blue { - background-color: rgb(3,169,244) !important; } - -.mdl-color-text--light-blue-50 { - color: rgb(225,245,254) !important; } - -.mdl-color--light-blue-50 { - background-color: rgb(225,245,254) !important; } - -.mdl-color-text--light-blue-100 { - color: rgb(179,229,252) !important; } - -.mdl-color--light-blue-100 { - background-color: rgb(179,229,252) !important; } - -.mdl-color-text--light-blue-200 { - color: rgb(129,212,250) !important; } - -.mdl-color--light-blue-200 { - background-color: rgb(129,212,250) !important; } - -.mdl-color-text--light-blue-300 { - color: rgb(79,195,247) !important; } - -.mdl-color--light-blue-300 { - background-color: rgb(79,195,247) !important; } - -.mdl-color-text--light-blue-400 { - color: rgb(41,182,246) !important; } - -.mdl-color--light-blue-400 { - background-color: rgb(41,182,246) !important; } - -.mdl-color-text--light-blue-500 { - color: rgb(3,169,244) !important; } - -.mdl-color--light-blue-500 { - background-color: rgb(3,169,244) !important; } - -.mdl-color-text--light-blue-600 { - color: rgb(3,155,229) !important; } - -.mdl-color--light-blue-600 { - background-color: rgb(3,155,229) !important; } - -.mdl-color-text--light-blue-700 { - color: rgb(2,136,209) !important; } - -.mdl-color--light-blue-700 { - background-color: rgb(2,136,209) !important; } - -.mdl-color-text--light-blue-800 { - color: rgb(2,119,189) !important; } - -.mdl-color--light-blue-800 { - background-color: rgb(2,119,189) !important; } - -.mdl-color-text--light-blue-900 { - color: rgb(1,87,155) !important; } - -.mdl-color--light-blue-900 { - background-color: rgb(1,87,155) !important; } - -.mdl-color-text--light-blue-A100 { - color: rgb(128,216,255) !important; } - -.mdl-color--light-blue-A100 { - background-color: rgb(128,216,255) !important; } - -.mdl-color-text--light-blue-A200 { - color: rgb(64,196,255) !important; } - -.mdl-color--light-blue-A200 { - background-color: rgb(64,196,255) !important; } - -.mdl-color-text--light-blue-A400 { - color: rgb(0,176,255) !important; } - -.mdl-color--light-blue-A400 { - background-color: rgb(0,176,255) !important; } - -.mdl-color-text--light-blue-A700 { - color: rgb(0,145,234) !important; } - -.mdl-color--light-blue-A700 { - background-color: rgb(0,145,234) !important; } - -.mdl-color-text--cyan { - color: rgb(0,188,212) !important; } - -.mdl-color--cyan { - background-color: rgb(0,188,212) !important; } - -.mdl-color-text--cyan-50 { - color: rgb(224,247,250) !important; } - -.mdl-color--cyan-50 { - background-color: rgb(224,247,250) !important; } - -.mdl-color-text--cyan-100 { - color: rgb(178,235,242) !important; } - -.mdl-color--cyan-100 { - background-color: rgb(178,235,242) !important; } - -.mdl-color-text--cyan-200 { - color: rgb(128,222,234) !important; } - -.mdl-color--cyan-200 { - background-color: rgb(128,222,234) !important; } - -.mdl-color-text--cyan-300 { - color: rgb(77,208,225) !important; } - -.mdl-color--cyan-300 { - background-color: rgb(77,208,225) !important; } - -.mdl-color-text--cyan-400 { - color: rgb(38,198,218) !important; } - -.mdl-color--cyan-400 { - background-color: rgb(38,198,218) !important; } - -.mdl-color-text--cyan-500 { - color: rgb(0,188,212) !important; } - -.mdl-color--cyan-500 { - background-color: rgb(0,188,212) !important; } - -.mdl-color-text--cyan-600 { - color: rgb(0,172,193) !important; } - -.mdl-color--cyan-600 { - background-color: rgb(0,172,193) !important; } - -.mdl-color-text--cyan-700 { - color: rgb(0,151,167) !important; } - -.mdl-color--cyan-700 { - background-color: rgb(0,151,167) !important; } - -.mdl-color-text--cyan-800 { - color: rgb(0,131,143) !important; } - -.mdl-color--cyan-800 { - background-color: rgb(0,131,143) !important; } - -.mdl-color-text--cyan-900 { - color: rgb(0,96,100) !important; } - -.mdl-color--cyan-900 { - background-color: rgb(0,96,100) !important; } - -.mdl-color-text--cyan-A100 { - color: rgb(132,255,255) !important; } - -.mdl-color--cyan-A100 { - background-color: rgb(132,255,255) !important; } - -.mdl-color-text--cyan-A200 { - color: rgb(24,255,255) !important; } - -.mdl-color--cyan-A200 { - background-color: rgb(24,255,255) !important; } - -.mdl-color-text--cyan-A400 { - color: rgb(0,229,255) !important; } - -.mdl-color--cyan-A400 { - background-color: rgb(0,229,255) !important; } - -.mdl-color-text--cyan-A700 { - color: rgb(0,184,212) !important; } - -.mdl-color--cyan-A700 { - background-color: rgb(0,184,212) !important; } - -.mdl-color-text--teal { - color: rgb(0,150,136) !important; } - -.mdl-color--teal { - background-color: rgb(0,150,136) !important; } - -.mdl-color-text--teal-50 { - color: rgb(224,242,241) !important; } - -.mdl-color--teal-50 { - background-color: rgb(224,242,241) !important; } - -.mdl-color-text--teal-100 { - color: rgb(178,223,219) !important; } - -.mdl-color--teal-100 { - background-color: rgb(178,223,219) !important; } - -.mdl-color-text--teal-200 { - color: rgb(128,203,196) !important; } - -.mdl-color--teal-200 { - background-color: rgb(128,203,196) !important; } - -.mdl-color-text--teal-300 { - color: rgb(77,182,172) !important; } - -.mdl-color--teal-300 { - background-color: rgb(77,182,172) !important; } - -.mdl-color-text--teal-400 { - color: rgb(38,166,154) !important; } - -.mdl-color--teal-400 { - background-color: rgb(38,166,154) !important; } - -.mdl-color-text--teal-500 { - color: rgb(0,150,136) !important; } - -.mdl-color--teal-500 { - background-color: rgb(0,150,136) !important; } - -.mdl-color-text--teal-600 { - color: rgb(0,137,123) !important; } - -.mdl-color--teal-600 { - background-color: rgb(0,137,123) !important; } - -.mdl-color-text--teal-700 { - color: rgb(0,121,107) !important; } - -.mdl-color--teal-700 { - background-color: rgb(0,121,107) !important; } - -.mdl-color-text--teal-800 { - color: rgb(0,105,92) !important; } - -.mdl-color--teal-800 { - background-color: rgb(0,105,92) !important; } - -.mdl-color-text--teal-900 { - color: rgb(0,77,64) !important; } - -.mdl-color--teal-900 { - background-color: rgb(0,77,64) !important; } - -.mdl-color-text--teal-A100 { - color: rgb(167,255,235) !important; } - -.mdl-color--teal-A100 { - background-color: rgb(167,255,235) !important; } - -.mdl-color-text--teal-A200 { - color: rgb(100,255,218) !important; } - -.mdl-color--teal-A200 { - background-color: rgb(100,255,218) !important; } - -.mdl-color-text--teal-A400 { - color: rgb(29,233,182) !important; } - -.mdl-color--teal-A400 { - background-color: rgb(29,233,182) !important; } - -.mdl-color-text--teal-A700 { - color: rgb(0,191,165) !important; } - -.mdl-color--teal-A700 { - background-color: rgb(0,191,165) !important; } - -.mdl-color-text--green { - color: rgb(76,175,80) !important; } - -.mdl-color--green { - background-color: rgb(76,175,80) !important; } - -.mdl-color-text--green-50 { - color: rgb(232,245,233) !important; } - -.mdl-color--green-50 { - background-color: rgb(232,245,233) !important; } - -.mdl-color-text--green-100 { - color: rgb(200,230,201) !important; } - -.mdl-color--green-100 { - background-color: rgb(200,230,201) !important; } - -.mdl-color-text--green-200 { - color: rgb(165,214,167) !important; } - -.mdl-color--green-200 { - background-color: rgb(165,214,167) !important; } - -.mdl-color-text--green-300 { - color: rgb(129,199,132) !important; } - -.mdl-color--green-300 { - background-color: rgb(129,199,132) !important; } - -.mdl-color-text--green-400 { - color: rgb(102,187,106) !important; } - -.mdl-color--green-400 { - background-color: rgb(102,187,106) !important; } - -.mdl-color-text--green-500 { - color: rgb(76,175,80) !important; } - -.mdl-color--green-500 { - background-color: rgb(76,175,80) !important; } - -.mdl-color-text--green-600 { - color: rgb(67,160,71) !important; } - -.mdl-color--green-600 { - background-color: rgb(67,160,71) !important; } - -.mdl-color-text--green-700 { - color: rgb(56,142,60) !important; } - -.mdl-color--green-700 { - background-color: rgb(56,142,60) !important; } - -.mdl-color-text--green-800 { - color: rgb(46,125,50) !important; } - -.mdl-color--green-800 { - background-color: rgb(46,125,50) !important; } - -.mdl-color-text--green-900 { - color: rgb(27,94,32) !important; } - -.mdl-color--green-900 { - background-color: rgb(27,94,32) !important; } - -.mdl-color-text--green-A100 { - color: rgb(185,246,202) !important; } - -.mdl-color--green-A100 { - background-color: rgb(185,246,202) !important; } - -.mdl-color-text--green-A200 { - color: rgb(105,240,174) !important; } - -.mdl-color--green-A200 { - background-color: rgb(105,240,174) !important; } - -.mdl-color-text--green-A400 { - color: rgb(0,230,118) !important; } - -.mdl-color--green-A400 { - background-color: rgb(0,230,118) !important; } - -.mdl-color-text--green-A700 { - color: rgb(0,200,83) !important; } - -.mdl-color--green-A700 { - background-color: rgb(0,200,83) !important; } - -.mdl-color-text--light-green { - color: rgb(139,195,74) !important; } - -.mdl-color--light-green { - background-color: rgb(139,195,74) !important; } - -.mdl-color-text--light-green-50 { - color: rgb(241,248,233) !important; } - -.mdl-color--light-green-50 { - background-color: rgb(241,248,233) !important; } - -.mdl-color-text--light-green-100 { - color: rgb(220,237,200) !important; } - -.mdl-color--light-green-100 { - background-color: rgb(220,237,200) !important; } - -.mdl-color-text--light-green-200 { - color: rgb(197,225,165) !important; } - -.mdl-color--light-green-200 { - background-color: rgb(197,225,165) !important; } - -.mdl-color-text--light-green-300 { - color: rgb(174,213,129) !important; } - -.mdl-color--light-green-300 { - background-color: rgb(174,213,129) !important; } - -.mdl-color-text--light-green-400 { - color: rgb(156,204,101) !important; } - -.mdl-color--light-green-400 { - background-color: rgb(156,204,101) !important; } - -.mdl-color-text--light-green-500 { - color: rgb(139,195,74) !important; } - -.mdl-color--light-green-500 { - background-color: rgb(139,195,74) !important; } - -.mdl-color-text--light-green-600 { - color: rgb(124,179,66) !important; } - -.mdl-color--light-green-600 { - background-color: rgb(124,179,66) !important; } - -.mdl-color-text--light-green-700 { - color: rgb(104,159,56) !important; } - -.mdl-color--light-green-700 { - background-color: rgb(104,159,56) !important; } - -.mdl-color-text--light-green-800 { - color: rgb(85,139,47) !important; } - -.mdl-color--light-green-800 { - background-color: rgb(85,139,47) !important; } - -.mdl-color-text--light-green-900 { - color: rgb(51,105,30) !important; } - -.mdl-color--light-green-900 { - background-color: rgb(51,105,30) !important; } - -.mdl-color-text--light-green-A100 { - color: rgb(204,255,144) !important; } - -.mdl-color--light-green-A100 { - background-color: rgb(204,255,144) !important; } - -.mdl-color-text--light-green-A200 { - color: rgb(178,255,89) !important; } - -.mdl-color--light-green-A200 { - background-color: rgb(178,255,89) !important; } - -.mdl-color-text--light-green-A400 { - color: rgb(118,255,3) !important; } - -.mdl-color--light-green-A400 { - background-color: rgb(118,255,3) !important; } - -.mdl-color-text--light-green-A700 { - color: rgb(100,221,23) !important; } - -.mdl-color--light-green-A700 { - background-color: rgb(100,221,23) !important; } - -.mdl-color-text--lime { - color: rgb(205,220,57) !important; } - -.mdl-color--lime { - background-color: rgb(205,220,57) !important; } - -.mdl-color-text--lime-50 { - color: rgb(249,251,231) !important; } - -.mdl-color--lime-50 { - background-color: rgb(249,251,231) !important; } - -.mdl-color-text--lime-100 { - color: rgb(240,244,195) !important; } - -.mdl-color--lime-100 { - background-color: rgb(240,244,195) !important; } - -.mdl-color-text--lime-200 { - color: rgb(230,238,156) !important; } - -.mdl-color--lime-200 { - background-color: rgb(230,238,156) !important; } - -.mdl-color-text--lime-300 { - color: rgb(220,231,117) !important; } - -.mdl-color--lime-300 { - background-color: rgb(220,231,117) !important; } - -.mdl-color-text--lime-400 { - color: rgb(212,225,87) !important; } - -.mdl-color--lime-400 { - background-color: rgb(212,225,87) !important; } - -.mdl-color-text--lime-500 { - color: rgb(205,220,57) !important; } - -.mdl-color--lime-500 { - background-color: rgb(205,220,57) !important; } - -.mdl-color-text--lime-600 { - color: rgb(192,202,51) !important; } - -.mdl-color--lime-600 { - background-color: rgb(192,202,51) !important; } - -.mdl-color-text--lime-700 { - color: rgb(175,180,43) !important; } - -.mdl-color--lime-700 { - background-color: rgb(175,180,43) !important; } - -.mdl-color-text--lime-800 { - color: rgb(158,157,36) !important; } - -.mdl-color--lime-800 { - background-color: rgb(158,157,36) !important; } - -.mdl-color-text--lime-900 { - color: rgb(130,119,23) !important; } - -.mdl-color--lime-900 { - background-color: rgb(130,119,23) !important; } - -.mdl-color-text--lime-A100 { - color: rgb(244,255,129) !important; } - -.mdl-color--lime-A100 { - background-color: rgb(244,255,129) !important; } - -.mdl-color-text--lime-A200 { - color: rgb(238,255,65) !important; } - -.mdl-color--lime-A200 { - background-color: rgb(238,255,65) !important; } - -.mdl-color-text--lime-A400 { - color: rgb(198,255,0) !important; } - -.mdl-color--lime-A400 { - background-color: rgb(198,255,0) !important; } - -.mdl-color-text--lime-A700 { - color: rgb(174,234,0) !important; } - -.mdl-color--lime-A700 { - background-color: rgb(174,234,0) !important; } - -.mdl-color-text--yellow { - color: rgb(255,235,59) !important; } - -.mdl-color--yellow { - background-color: rgb(255,235,59) !important; } - -.mdl-color-text--yellow-50 { - color: rgb(255,253,231) !important; } - -.mdl-color--yellow-50 { - background-color: rgb(255,253,231) !important; } - -.mdl-color-text--yellow-100 { - color: rgb(255,249,196) !important; } - -.mdl-color--yellow-100 { - background-color: rgb(255,249,196) !important; } - -.mdl-color-text--yellow-200 { - color: rgb(255,245,157) !important; } - -.mdl-color--yellow-200 { - background-color: rgb(255,245,157) !important; } - -.mdl-color-text--yellow-300 { - color: rgb(255,241,118) !important; } - -.mdl-color--yellow-300 { - background-color: rgb(255,241,118) !important; } - -.mdl-color-text--yellow-400 { - color: rgb(255,238,88) !important; } - -.mdl-color--yellow-400 { - background-color: rgb(255,238,88) !important; } - -.mdl-color-text--yellow-500 { - color: rgb(255,235,59) !important; } - -.mdl-color--yellow-500 { - background-color: rgb(255,235,59) !important; } - -.mdl-color-text--yellow-600 { - color: rgb(253,216,53) !important; } - -.mdl-color--yellow-600 { - background-color: rgb(253,216,53) !important; } - -.mdl-color-text--yellow-700 { - color: rgb(251,192,45) !important; } - -.mdl-color--yellow-700 { - background-color: rgb(251,192,45) !important; } - -.mdl-color-text--yellow-800 { - color: rgb(249,168,37) !important; } - -.mdl-color--yellow-800 { - background-color: rgb(249,168,37) !important; } - -.mdl-color-text--yellow-900 { - color: rgb(245,127,23) !important; } - -.mdl-color--yellow-900 { - background-color: rgb(245,127,23) !important; } - -.mdl-color-text--yellow-A100 { - color: rgb(255,255,141) !important; } - -.mdl-color--yellow-A100 { - background-color: rgb(255,255,141) !important; } - -.mdl-color-text--yellow-A200 { - color: rgb(255,255,0) !important; } - -.mdl-color--yellow-A200 { - background-color: rgb(255,255,0) !important; } - -.mdl-color-text--yellow-A400 { - color: rgb(255,234,0) !important; } - -.mdl-color--yellow-A400 { - background-color: rgb(255,234,0) !important; } - -.mdl-color-text--yellow-A700 { - color: rgb(255,214,0) !important; } - -.mdl-color--yellow-A700 { - background-color: rgb(255,214,0) !important; } - -.mdl-color-text--amber { - color: rgb(255,193,7) !important; } - -.mdl-color--amber { - background-color: rgb(255,193,7) !important; } - -.mdl-color-text--amber-50 { - color: rgb(255,248,225) !important; } - -.mdl-color--amber-50 { - background-color: rgb(255,248,225) !important; } - -.mdl-color-text--amber-100 { - color: rgb(255,236,179) !important; } - -.mdl-color--amber-100 { - background-color: rgb(255,236,179) !important; } - -.mdl-color-text--amber-200 { - color: rgb(255,224,130) !important; } - -.mdl-color--amber-200 { - background-color: rgb(255,224,130) !important; } - -.mdl-color-text--amber-300 { - color: rgb(255,213,79) !important; } - -.mdl-color--amber-300 { - background-color: rgb(255,213,79) !important; } - -.mdl-color-text--amber-400 { - color: rgb(255,202,40) !important; } - -.mdl-color--amber-400 { - background-color: rgb(255,202,40) !important; } - -.mdl-color-text--amber-500 { - color: rgb(255,193,7) !important; } - -.mdl-color--amber-500 { - background-color: rgb(255,193,7) !important; } - -.mdl-color-text--amber-600 { - color: rgb(255,179,0) !important; } - -.mdl-color--amber-600 { - background-color: rgb(255,179,0) !important; } - -.mdl-color-text--amber-700 { - color: rgb(255,160,0) !important; } - -.mdl-color--amber-700 { - background-color: rgb(255,160,0) !important; } - -.mdl-color-text--amber-800 { - color: rgb(255,143,0) !important; } - -.mdl-color--amber-800 { - background-color: rgb(255,143,0) !important; } - -.mdl-color-text--amber-900 { - color: rgb(255,111,0) !important; } - -.mdl-color--amber-900 { - background-color: rgb(255,111,0) !important; } - -.mdl-color-text--amber-A100 { - color: rgb(255,229,127) !important; } - -.mdl-color--amber-A100 { - background-color: rgb(255,229,127) !important; } - -.mdl-color-text--amber-A200 { - color: rgb(255,215,64) !important; } - -.mdl-color--amber-A200 { - background-color: rgb(255,215,64) !important; } - -.mdl-color-text--amber-A400 { - color: rgb(255,196,0) !important; } - -.mdl-color--amber-A400 { - background-color: rgb(255,196,0) !important; } - -.mdl-color-text--amber-A700 { - color: rgb(255,171,0) !important; } - -.mdl-color--amber-A700 { - background-color: rgb(255,171,0) !important; } - -.mdl-color-text--orange { - color: rgb(255,152,0) !important; } - -.mdl-color--orange { - background-color: rgb(255,152,0) !important; } - -.mdl-color-text--orange-50 { - color: rgb(255,243,224) !important; } - -.mdl-color--orange-50 { - background-color: rgb(255,243,224) !important; } - -.mdl-color-text--orange-100 { - color: rgb(255,224,178) !important; } - -.mdl-color--orange-100 { - background-color: rgb(255,224,178) !important; } - -.mdl-color-text--orange-200 { - color: rgb(255,204,128) !important; } - -.mdl-color--orange-200 { - background-color: rgb(255,204,128) !important; } - -.mdl-color-text--orange-300 { - color: rgb(255,183,77) !important; } - -.mdl-color--orange-300 { - background-color: rgb(255,183,77) !important; } - -.mdl-color-text--orange-400 { - color: rgb(255,167,38) !important; } - -.mdl-color--orange-400 { - background-color: rgb(255,167,38) !important; } - -.mdl-color-text--orange-500 { - color: rgb(255,152,0) !important; } - -.mdl-color--orange-500 { - background-color: rgb(255,152,0) !important; } - -.mdl-color-text--orange-600 { - color: rgb(251,140,0) !important; } - -.mdl-color--orange-600 { - background-color: rgb(251,140,0) !important; } - -.mdl-color-text--orange-700 { - color: rgb(245,124,0) !important; } - -.mdl-color--orange-700 { - background-color: rgb(245,124,0) !important; } - -.mdl-color-text--orange-800 { - color: rgb(239,108,0) !important; } - -.mdl-color--orange-800 { - background-color: rgb(239,108,0) !important; } - -.mdl-color-text--orange-900 { - color: rgb(230,81,0) !important; } - -.mdl-color--orange-900 { - background-color: rgb(230,81,0) !important; } - -.mdl-color-text--orange-A100 { - color: rgb(255,209,128) !important; } - -.mdl-color--orange-A100 { - background-color: rgb(255,209,128) !important; } - -.mdl-color-text--orange-A200 { - color: rgb(255,171,64) !important; } - -.mdl-color--orange-A200 { - background-color: rgb(255,171,64) !important; } - -.mdl-color-text--orange-A400 { - color: rgb(255,145,0) !important; } - -.mdl-color--orange-A400 { - background-color: rgb(255,145,0) !important; } - -.mdl-color-text--orange-A700 { - color: rgb(255,109,0) !important; } - -.mdl-color--orange-A700 { - background-color: rgb(255,109,0) !important; } - -.mdl-color-text--deep-orange { - color: rgb(255,87,34) !important; } - -.mdl-color--deep-orange { - background-color: rgb(255,87,34) !important; } - -.mdl-color-text--deep-orange-50 { - color: rgb(251,233,231) !important; } - -.mdl-color--deep-orange-50 { - background-color: rgb(251,233,231) !important; } - -.mdl-color-text--deep-orange-100 { - color: rgb(255,204,188) !important; } - -.mdl-color--deep-orange-100 { - background-color: rgb(255,204,188) !important; } - -.mdl-color-text--deep-orange-200 { - color: rgb(255,171,145) !important; } - -.mdl-color--deep-orange-200 { - background-color: rgb(255,171,145) !important; } - -.mdl-color-text--deep-orange-300 { - color: rgb(255,138,101) !important; } - -.mdl-color--deep-orange-300 { - background-color: rgb(255,138,101) !important; } - -.mdl-color-text--deep-orange-400 { - color: rgb(255,112,67) !important; } - -.mdl-color--deep-orange-400 { - background-color: rgb(255,112,67) !important; } - -.mdl-color-text--deep-orange-500 { - color: rgb(255,87,34) !important; } - -.mdl-color--deep-orange-500 { - background-color: rgb(255,87,34) !important; } - -.mdl-color-text--deep-orange-600 { - color: rgb(244,81,30) !important; } - -.mdl-color--deep-orange-600 { - background-color: rgb(244,81,30) !important; } - -.mdl-color-text--deep-orange-700 { - color: rgb(230,74,25) !important; } - -.mdl-color--deep-orange-700 { - background-color: rgb(230,74,25) !important; } - -.mdl-color-text--deep-orange-800 { - color: rgb(216,67,21) !important; } - -.mdl-color--deep-orange-800 { - background-color: rgb(216,67,21) !important; } - -.mdl-color-text--deep-orange-900 { - color: rgb(191,54,12) !important; } - -.mdl-color--deep-orange-900 { - background-color: rgb(191,54,12) !important; } - -.mdl-color-text--deep-orange-A100 { - color: rgb(255,158,128) !important; } - -.mdl-color--deep-orange-A100 { - background-color: rgb(255,158,128) !important; } - -.mdl-color-text--deep-orange-A200 { - color: rgb(255,110,64) !important; } - -.mdl-color--deep-orange-A200 { - background-color: rgb(255,110,64) !important; } - -.mdl-color-text--deep-orange-A400 { - color: rgb(255,61,0) !important; } - -.mdl-color--deep-orange-A400 { - background-color: rgb(255,61,0) !important; } - -.mdl-color-text--deep-orange-A700 { - color: rgb(221,44,0) !important; } - -.mdl-color--deep-orange-A700 { - background-color: rgb(221,44,0) !important; } - -.mdl-color-text--brown { - color: rgb(121,85,72) !important; } - -.mdl-color--brown { - background-color: rgb(121,85,72) !important; } - -.mdl-color-text--brown-50 { - color: rgb(239,235,233) !important; } - -.mdl-color--brown-50 { - background-color: rgb(239,235,233) !important; } - -.mdl-color-text--brown-100 { - color: rgb(215,204,200) !important; } - -.mdl-color--brown-100 { - background-color: rgb(215,204,200) !important; } - -.mdl-color-text--brown-200 { - color: rgb(188,170,164) !important; } - -.mdl-color--brown-200 { - background-color: rgb(188,170,164) !important; } - -.mdl-color-text--brown-300 { - color: rgb(161,136,127) !important; } - -.mdl-color--brown-300 { - background-color: rgb(161,136,127) !important; } - -.mdl-color-text--brown-400 { - color: rgb(141,110,99) !important; } - -.mdl-color--brown-400 { - background-color: rgb(141,110,99) !important; } - -.mdl-color-text--brown-500 { - color: rgb(121,85,72) !important; } - -.mdl-color--brown-500 { - background-color: rgb(121,85,72) !important; } - -.mdl-color-text--brown-600 { - color: rgb(109,76,65) !important; } - -.mdl-color--brown-600 { - background-color: rgb(109,76,65) !important; } - -.mdl-color-text--brown-700 { - color: rgb(93,64,55) !important; } - -.mdl-color--brown-700 { - background-color: rgb(93,64,55) !important; } - -.mdl-color-text--brown-800 { - color: rgb(78,52,46) !important; } - -.mdl-color--brown-800 { - background-color: rgb(78,52,46) !important; } - -.mdl-color-text--brown-900 { - color: rgb(62,39,35) !important; } - -.mdl-color--brown-900 { - background-color: rgb(62,39,35) !important; } - -.mdl-color-text--grey { - color: rgb(158,158,158) !important; } - -.mdl-color--grey { - background-color: rgb(158,158,158) !important; } - -.mdl-color-text--grey-50 { - color: rgb(250,250,250) !important; } - -.mdl-color--grey-50 { - background-color: rgb(250,250,250) !important; } - -.mdl-color-text--grey-100 { - color: rgb(245,245,245) !important; } - -.mdl-color--grey-100 { - background-color: rgb(245,245,245) !important; } - -.mdl-color-text--grey-200 { - color: rgb(238,238,238) !important; } - -.mdl-color--grey-200 { - background-color: rgb(238,238,238) !important; } - -.mdl-color-text--grey-300 { - color: rgb(224,224,224) !important; } - -.mdl-color--grey-300 { - background-color: rgb(224,224,224) !important; } - -.mdl-color-text--grey-400 { - color: rgb(189,189,189) !important; } - -.mdl-color--grey-400 { - background-color: rgb(189,189,189) !important; } - -.mdl-color-text--grey-500 { - color: rgb(158,158,158) !important; } - -.mdl-color--grey-500 { - background-color: rgb(158,158,158) !important; } - -.mdl-color-text--grey-600 { - color: rgb(117,117,117) !important; } - -.mdl-color--grey-600 { - background-color: rgb(117,117,117) !important; } - -.mdl-color-text--grey-700 { - color: rgb(97,97,97) !important; } - -.mdl-color--grey-700 { - background-color: rgb(97,97,97) !important; } - -.mdl-color-text--grey-800 { - color: rgb(66,66,66) !important; } - -.mdl-color--grey-800 { - background-color: rgb(66,66,66) !important; } - -.mdl-color-text--grey-900 { - color: rgb(33,33,33) !important; } - -.mdl-color--grey-900 { - background-color: rgb(33,33,33) !important; } - -.mdl-color-text--blue-grey { - color: rgb(96,125,139) !important; } - -.mdl-color--blue-grey { - background-color: rgb(96,125,139) !important; } - -.mdl-color-text--blue-grey-50 { - color: rgb(236,239,241) !important; } - -.mdl-color--blue-grey-50 { - background-color: rgb(236,239,241) !important; } - -.mdl-color-text--blue-grey-100 { - color: rgb(207,216,220) !important; } - -.mdl-color--blue-grey-100 { - background-color: rgb(207,216,220) !important; } - -.mdl-color-text--blue-grey-200 { - color: rgb(176,190,197) !important; } - -.mdl-color--blue-grey-200 { - background-color: rgb(176,190,197) !important; } - -.mdl-color-text--blue-grey-300 { - color: rgb(144,164,174) !important; } - -.mdl-color--blue-grey-300 { - background-color: rgb(144,164,174) !important; } - -.mdl-color-text--blue-grey-400 { - color: rgb(120,144,156) !important; } - -.mdl-color--blue-grey-400 { - background-color: rgb(120,144,156) !important; } - -.mdl-color-text--blue-grey-500 { - color: rgb(96,125,139) !important; } - -.mdl-color--blue-grey-500 { - background-color: rgb(96,125,139) !important; } - -.mdl-color-text--blue-grey-600 { - color: rgb(84,110,122) !important; } - -.mdl-color--blue-grey-600 { - background-color: rgb(84,110,122) !important; } - -.mdl-color-text--blue-grey-700 { - color: rgb(69,90,100) !important; } - -.mdl-color--blue-grey-700 { - background-color: rgb(69,90,100) !important; } - -.mdl-color-text--blue-grey-800 { - color: rgb(55,71,79) !important; } - -.mdl-color--blue-grey-800 { - background-color: rgb(55,71,79) !important; } - -.mdl-color-text--blue-grey-900 { - color: rgb(38,50,56) !important; } - -.mdl-color--blue-grey-900 { - background-color: rgb(38,50,56) !important; } - -.mdl-color--black { - background-color: rgb(0,0,0) !important; } - -.mdl-color-text--black { - color: rgb(0,0,0) !important; } - -.mdl-color--white { - background-color: rgb(255,255,255) !important; } - -.mdl-color-text--white { - color: rgb(255,255,255) !important; } - -.mdl-color--primary { - background-color: rgb(63,81,181) !important; } - -.mdl-color--primary-contrast { - background-color: rgb(255,255,255) !important; } - -.mdl-color--primary-dark { - background-color: rgb(48,63,159) !important; } - -.mdl-color--accent { - background-color: rgb(255,64,129) !important; } - -.mdl-color--accent-contrast { - background-color: rgb(255,255,255) !important; } - -.mdl-color-text--primary { - color: rgb(63,81,181) !important; } - -.mdl-color-text--primary-contrast { - color: rgb(255,255,255) !important; } - -.mdl-color-text--primary-dark { - color: rgb(48,63,159) !important; } - -.mdl-color-text--accent { - color: rgb(255,64,129) !important; } - -.mdl-color-text--accent-contrast { - color: rgb(255,255,255) !important; } - -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/*------------------------------------* $CONTENTS -\*------------------------------------*/ -/** - * STYLE GUIDE VARIABLES------------------Declarations of Sass variables - * -----Typography - * -----Colors - * -----Textfield - * -----Switch - * -----Spinner - * -----Radio - * -----Menu - * -----List - * -----Layout - * -----Icon toggles - * -----Footer - * -----Column - * -----Checkbox - * -----Card - * -----Button - * -----Animation - * -----Progress - * -----Badge - * -----Shadows - * -----Grid - * -----Data table - */ -/* ========== TYPOGRAPHY ========== */ -/* We're splitting fonts into "preferred" and "performance" in order to optimize - page loading. For important text, such as the body, we want it to load - immediately and not wait for the web font load, whereas for other sections, - such as headers and titles, we're OK with things taking a bit longer to load. - We do have some optional classes and parameters in the mixins, in case you - definitely want to make sure you're using the preferred font and don't mind - the performance hit. - We should be able to improve on this once CSS Font Loading L3 becomes more - widely available. -*/ -/* ========== COLORS ========== */ -/** -* -* Material design color palettes. -* @see http://www.google.com/design/spec/style/color.html -* -**/ -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/* ========== Color Palettes ========== */ -/* colors.scss */ -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/* ========== IMAGES ========== */ -/* ========== Color & Themes ========== */ -/* ========== Typography ========== */ -/* ========== Components ========== */ -/* ========== Standard Buttons ========== */ -/* ========== Icon Toggles ========== */ -/* ========== Radio Buttons ========== */ -/* ========== Ripple effect ========== */ -/* ========== Layout ========== */ -/* ========== Content Tabs ========== */ -/* ========== Checkboxes ========== */ -/* ========== Switches ========== */ -/* ========== Spinner ========== */ -/* ========== Text fields ========== */ -/* ========== Card ========== */ -/* ========== Sliders ========== */ -/* ========== Progress ========== */ -/* ========== List ========== */ -/* ========== Item ========== */ -/* ========== Dropdown menu ========== */ -/* ========== Tooltips ========== */ -/* ========== Footer ========== */ -/* TEXTFIELD */ -/* SWITCH */ -/* SPINNER */ -/* RADIO */ -/* MENU */ -/* LIST */ -/* LAYOUT */ -/* ICON TOGGLE */ -/* FOOTER */ -/*mega-footer*/ -/*mini-footer*/ -/* CHECKBOX */ -/* CARD */ -/* Card dimensions */ -/* Cover image */ -/* BUTTON */ -/** - * - * Dimensions - * - */ -/* ANIMATION */ -/* PROGRESS */ -/* BADGE */ -/* SHADOWS */ -/* GRID */ -/* DATA TABLE */ -/* TOOLTIP */ -.mdl-ripple { - background: rgb(0,0,0); - border-radius: 50%; - height: 50px; - left: 0; - opacity: 0; - pointer-events: none; - position: absolute; - top: 0; - -webkit-transform: translate(-50%, -50%); - -ms-transform: translate(-50%, -50%); - transform: translate(-50%, -50%); - width: 50px; - overflow: hidden; } - .mdl-ripple.is-animating { - -webkit-transition: -webkit-transform 0.3s cubic-bezier(0, 0, 0.2, 1), width 0.3s cubic-bezier(0, 0, 0.2, 1), height 0.3s cubic-bezier(0, 0, 0.2, 1), opacity 0.6s cubic-bezier(0, 0, 0.2, 1); - transition: transform 0.3s cubic-bezier(0, 0, 0.2, 1), width 0.3s cubic-bezier(0, 0, 0.2, 1), height 0.3s cubic-bezier(0, 0, 0.2, 1), opacity 0.6s cubic-bezier(0, 0, 0.2, 1); } - .mdl-ripple.is-visible { - opacity: 0.3; } - -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/*------------------------------------* $CONTENTS -\*------------------------------------*/ -/** - * STYLE GUIDE VARIABLES------------------Declarations of Sass variables - * -----Typography - * -----Colors - * -----Textfield - * -----Switch - * -----Spinner - * -----Radio - * -----Menu - * -----List - * -----Layout - * -----Icon toggles - * -----Footer - * -----Column - * -----Checkbox - * -----Card - * -----Button - * -----Animation - * -----Progress - * -----Badge - * -----Shadows - * -----Grid - * -----Data table - */ -/* ========== TYPOGRAPHY ========== */ -/* We're splitting fonts into "preferred" and "performance" in order to optimize - page loading. For important text, such as the body, we want it to load - immediately and not wait for the web font load, whereas for other sections, - such as headers and titles, we're OK with things taking a bit longer to load. - We do have some optional classes and parameters in the mixins, in case you - definitely want to make sure you're using the preferred font and don't mind - the performance hit. - We should be able to improve on this once CSS Font Loading L3 becomes more - widely available. -*/ -/* ========== COLORS ========== */ -/** -* -* Material design color palettes. -* @see http://www.google.com/design/spec/style/color.html -* -**/ -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/* ========== Color Palettes ========== */ -/* colors.scss */ -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/* ========== IMAGES ========== */ -/* ========== Color & Themes ========== */ -/* ========== Typography ========== */ -/* ========== Components ========== */ -/* ========== Standard Buttons ========== */ -/* ========== Icon Toggles ========== */ -/* ========== Radio Buttons ========== */ -/* ========== Ripple effect ========== */ -/* ========== Layout ========== */ -/* ========== Content Tabs ========== */ -/* ========== Checkboxes ========== */ -/* ========== Switches ========== */ -/* ========== Spinner ========== */ -/* ========== Text fields ========== */ -/* ========== Card ========== */ -/* ========== Sliders ========== */ -/* ========== Progress ========== */ -/* ========== List ========== */ -/* ========== Item ========== */ -/* ========== Dropdown menu ========== */ -/* ========== Tooltips ========== */ -/* ========== Footer ========== */ -/* TEXTFIELD */ -/* SWITCH */ -/* SPINNER */ -/* RADIO */ -/* MENU */ -/* LIST */ -/* LAYOUT */ -/* ICON TOGGLE */ -/* FOOTER */ -/*mega-footer*/ -/*mini-footer*/ -/* CHECKBOX */ -/* CARD */ -/* Card dimensions */ -/* Cover image */ -/* BUTTON */ -/** - * - * Dimensions - * - */ -/* ANIMATION */ -/* PROGRESS */ -/* BADGE */ -/* SHADOWS */ -/* GRID */ -/* DATA TABLE */ -/* TOOLTIP */ -.mdl-animation--default { - -webkit-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); } - -.mdl-animation--fast-out-slow-in { - -webkit-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); } - -.mdl-animation--linear-out-slow-in { - -webkit-transition-timing-function: cubic-bezier(0, 0, 0.2, 1); - transition-timing-function: cubic-bezier(0, 0, 0.2, 1); } - -.mdl-animation--fast-out-linear-in { - -webkit-transition-timing-function: cubic-bezier(0.4, 0, 1, 1); - transition-timing-function: cubic-bezier(0.4, 0, 1, 1); } - -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/*------------------------------------* $CONTENTS -\*------------------------------------*/ -/** - * STYLE GUIDE VARIABLES------------------Declarations of Sass variables - * -----Typography - * -----Colors - * -----Textfield - * -----Switch - * -----Spinner - * -----Radio - * -----Menu - * -----List - * -----Layout - * -----Icon toggles - * -----Footer - * -----Column - * -----Checkbox - * -----Card - * -----Button - * -----Animation - * -----Progress - * -----Badge - * -----Shadows - * -----Grid - * -----Data table - */ -/* ========== TYPOGRAPHY ========== */ -/* We're splitting fonts into "preferred" and "performance" in order to optimize - page loading. For important text, such as the body, we want it to load - immediately and not wait for the web font load, whereas for other sections, - such as headers and titles, we're OK with things taking a bit longer to load. - We do have some optional classes and parameters in the mixins, in case you - definitely want to make sure you're using the preferred font and don't mind - the performance hit. - We should be able to improve on this once CSS Font Loading L3 becomes more - widely available. -*/ -/* ========== COLORS ========== */ -/** -* -* Material design color palettes. -* @see http://www.google.com/design/spec/style/color.html -* -**/ -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/* ========== Color Palettes ========== */ -/* colors.scss */ -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/* ========== IMAGES ========== */ -/* ========== Color & Themes ========== */ -/* ========== Typography ========== */ -/* ========== Components ========== */ -/* ========== Standard Buttons ========== */ -/* ========== Icon Toggles ========== */ -/* ========== Radio Buttons ========== */ -/* ========== Ripple effect ========== */ -/* ========== Layout ========== */ -/* ========== Content Tabs ========== */ -/* ========== Checkboxes ========== */ -/* ========== Switches ========== */ -/* ========== Spinner ========== */ -/* ========== Text fields ========== */ -/* ========== Card ========== */ -/* ========== Sliders ========== */ -/* ========== Progress ========== */ -/* ========== List ========== */ -/* ========== Item ========== */ -/* ========== Dropdown menu ========== */ -/* ========== Tooltips ========== */ -/* ========== Footer ========== */ -/* TEXTFIELD */ -/* SWITCH */ -/* SPINNER */ -/* RADIO */ -/* MENU */ -/* LIST */ -/* LAYOUT */ -/* ICON TOGGLE */ -/* FOOTER */ -/*mega-footer*/ -/*mini-footer*/ -/* CHECKBOX */ -/* CARD */ -/* Card dimensions */ -/* Cover image */ -/* BUTTON */ -/** - * - * Dimensions - * - */ -/* ANIMATION */ -/* PROGRESS */ -/* BADGE */ -/* SHADOWS */ -/* GRID */ -/* DATA TABLE */ -/* TOOLTIP */ -.mdl-badge { - position: relative; - white-space: nowrap; - margin-right: 24px; } - .mdl-badge:not([data-badge]) { - margin-right: auto; } - .mdl-badge[data-badge]:after { - content: attr(data-badge); - display: -webkit-box; - display: -webkit-flex; - display: -ms-flexbox; - display: flex; - -webkit-box-orient: horizontal; - -webkit-box-direction: normal; - -webkit-flex-direction: row; - -ms-flex-direction: row; - flex-direction: row; - -webkit-flex-wrap: wrap; - -ms-flex-wrap: wrap; - flex-wrap: wrap; - -webkit-box-pack: center; - -webkit-justify-content: center; - -ms-flex-pack: center; - justify-content: center; - -webkit-align-content: center; - -ms-flex-line-pack: center; - align-content: center; - -webkit-box-align: center; - -webkit-align-items: center; - -ms-flex-align: center; - align-items: center; - position: absolute; - top: -11px; - right: -24px; - font-family: "Roboto", "Helvetica", "Arial", sans-serif; - font-weight: 600; - font-size: 12px; - width: 22px; - height: 22px; - border-radius: 50%; - background: rgb(255,64,129); - color: rgb(255,255,255); } - .mdl-button .mdl-badge[data-badge]:after { - top: -10px; - right: -5px; } - .mdl-badge.mdl-badge--no-background[data-badge]:after { - color: rgb(255,64,129); - background: rgb(255,255,255); - box-shadow: 0 0 1px gray; } - -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/*------------------------------------* $CONTENTS -\*------------------------------------*/ -/** - * STYLE GUIDE VARIABLES------------------Declarations of Sass variables - * -----Typography - * -----Colors - * -----Textfield - * -----Switch - * -----Spinner - * -----Radio - * -----Menu - * -----List - * -----Layout - * -----Icon toggles - * -----Footer - * -----Column - * -----Checkbox - * -----Card - * -----Button - * -----Animation - * -----Progress - * -----Badge - * -----Shadows - * -----Grid - * -----Data table - */ -/* ========== TYPOGRAPHY ========== */ -/* We're splitting fonts into "preferred" and "performance" in order to optimize - page loading. For important text, such as the body, we want it to load - immediately and not wait for the web font load, whereas for other sections, - such as headers and titles, we're OK with things taking a bit longer to load. - We do have some optional classes and parameters in the mixins, in case you - definitely want to make sure you're using the preferred font and don't mind - the performance hit. - We should be able to improve on this once CSS Font Loading L3 becomes more - widely available. -*/ -/* ========== COLORS ========== */ -/** -* -* Material design color palettes. -* @see http://www.google.com/design/spec/style/color.html -* -**/ -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/* ========== Color Palettes ========== */ -/* colors.scss */ -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/* ========== IMAGES ========== */ -/* ========== Color & Themes ========== */ -/* ========== Typography ========== */ -/* ========== Components ========== */ -/* ========== Standard Buttons ========== */ -/* ========== Icon Toggles ========== */ -/* ========== Radio Buttons ========== */ -/* ========== Ripple effect ========== */ -/* ========== Layout ========== */ -/* ========== Content Tabs ========== */ -/* ========== Checkboxes ========== */ -/* ========== Switches ========== */ -/* ========== Spinner ========== */ -/* ========== Text fields ========== */ -/* ========== Card ========== */ -/* ========== Sliders ========== */ -/* ========== Progress ========== */ -/* ========== List ========== */ -/* ========== Item ========== */ -/* ========== Dropdown menu ========== */ -/* ========== Tooltips ========== */ -/* ========== Footer ========== */ -/* TEXTFIELD */ -/* SWITCH */ -/* SPINNER */ -/* RADIO */ -/* MENU */ -/* LIST */ -/* LAYOUT */ -/* ICON TOGGLE */ -/* FOOTER */ -/*mega-footer*/ -/*mini-footer*/ -/* CHECKBOX */ -/* CARD */ -/* Card dimensions */ -/* Cover image */ -/* BUTTON */ -/** - * - * Dimensions - * - */ -/* ANIMATION */ -/* PROGRESS */ -/* BADGE */ -/* SHADOWS */ -/* GRID */ -/* DATA TABLE */ -/* TOOLTIP */ -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/* Typography */ -/* Shadows */ -/* Animations */ -.mdl-button { - background: transparent; - border: none; - border-radius: 2px; - color: rgb(0,0,0); - position: relative; - height: 36px; - min-width: 64px; - padding: 0 16px; - display: inline-block; - font-family: "Roboto", "Helvetica", "Arial", sans-serif; - font-size: 14px; - font-weight: 500; - text-transform: uppercase; - line-height: 1; - letter-spacing: 0; - overflow: hidden; - will-change: box-shadow, transform; - -webkit-transition: box-shadow 0.2s cubic-bezier(0.4, 0, 1, 1), background-color 0.2s cubic-bezier(0.4, 0, 0.2, 1), color 0.2s cubic-bezier(0.4, 0, 0.2, 1); - transition: box-shadow 0.2s cubic-bezier(0.4, 0, 1, 1), background-color 0.2s cubic-bezier(0.4, 0, 0.2, 1), color 0.2s cubic-bezier(0.4, 0, 0.2, 1); - outline: none; - cursor: pointer; - text-decoration: none; - text-align: center; - line-height: 36px; - vertical-align: middle; } - .mdl-button::-moz-focus-inner { - border: 0; } - .mdl-button:hover { - background-color: rgba(158,158,158, 0.20); } - .mdl-button:focus:not(:active) { - background-color: rgba(0,0,0, 0.12); } - .mdl-button:active { - background-color: rgba(158,158,158, 0.40); } - .mdl-button.mdl-button--colored { - color: rgb(63,81,181); } - .mdl-button.mdl-button--colored:focus:not(:active) { - background-color: rgba(0,0,0, 0.12); } - -input.mdl-button[type="submit"] { - -webkit-appearance: none; } - -.mdl-button--raised { - background: rgba(158,158,158, 0.20); - box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12); } - .mdl-button--raised:active { - box-shadow: 0 4px 5px 0 rgba(0, 0, 0, 0.14), 0 1px 10px 0 rgba(0, 0, 0, 0.12), 0 2px 4px -1px rgba(0, 0, 0, 0.2); - background-color: rgba(158,158,158, 0.40); } - .mdl-button--raised:focus:not(:active) { - box-shadow: 0 0 8px rgba(0, 0, 0, 0.18), 0 8px 16px rgba(0, 0, 0, 0.36); - background-color: rgba(158,158,158, 0.40); } - .mdl-button--raised.mdl-button--colored { - background: rgb(63,81,181); - color: rgb(255,255,255); } - .mdl-button--raised.mdl-button--colored:hover { - background-color: rgb(63,81,181); } - .mdl-button--raised.mdl-button--colored:active { - background-color: rgb(63,81,181); } - .mdl-button--raised.mdl-button--colored:focus:not(:active) { - background-color: rgb(63,81,181); } - .mdl-button--raised.mdl-button--colored .mdl-ripple { - background: rgb(255,255,255); } - -.mdl-button--fab { - border-radius: 50%; - font-size: 24px; - height: 56px; - margin: auto; - min-width: 56px; - width: 56px; - padding: 0; - overflow: hidden; - background: rgba(158,158,158, 0.20); - box-shadow: 0 1px 1.5px 0 rgba(0, 0, 0, 0.12), 0 1px 1px 0 rgba(0, 0, 0, 0.24); - position: relative; - line-height: normal; } - .mdl-button--fab .material-icons { - position: absolute; - top: 50%; - left: 50%; - -webkit-transform: translate(-12px, -12px); - -ms-transform: translate(-12px, -12px); - transform: translate(-12px, -12px); - line-height: 24px; - width: 24px; } - .mdl-button--fab.mdl-button--mini-fab { - height: 40px; - min-width: 40px; - width: 40px; } - .mdl-button--fab .mdl-button__ripple-container { - border-radius: 50%; - -webkit-mask-image: -webkit-radial-gradient(circle, white, black); } - .mdl-button--fab:active { - box-shadow: 0 4px 5px 0 rgba(0, 0, 0, 0.14), 0 1px 10px 0 rgba(0, 0, 0, 0.12), 0 2px 4px -1px rgba(0, 0, 0, 0.2); - background-color: rgba(158,158,158, 0.40); } - .mdl-button--fab:focus:not(:active) { - box-shadow: 0 0 8px rgba(0, 0, 0, 0.18), 0 8px 16px rgba(0, 0, 0, 0.36); - background-color: rgba(158,158,158, 0.40); } - .mdl-button--fab.mdl-button--colored { - background: rgb(255,64,129); - color: rgb(255,255,255); } - .mdl-button--fab.mdl-button--colored:hover { - background-color: rgb(255,64,129); } - .mdl-button--fab.mdl-button--colored:focus:not(:active) { - background-color: rgb(255,64,129); } - .mdl-button--fab.mdl-button--colored:active { - background-color: rgb(255,64,129); } - .mdl-button--fab.mdl-button--colored .mdl-ripple { - background: rgb(255,255,255); } - -.mdl-button--icon { - border-radius: 50%; - font-size: 24px; - height: 32px; - margin-left: 0; - margin-right: 0; - min-width: 32px; - width: 32px; - padding: 0; - overflow: hidden; - color: inherit; - line-height: normal; } - .mdl-button--icon .material-icons { - position: absolute; - top: 50%; - left: 50%; - -webkit-transform: translate(-12px, -12px); - -ms-transform: translate(-12px, -12px); - transform: translate(-12px, -12px); - line-height: 24px; - width: 24px; } - .mdl-button--icon.mdl-button--mini-icon { - height: 24px; - min-width: 24px; - width: 24px; } - .mdl-button--icon.mdl-button--mini-icon .material-icons { - top: 0px; - left: 0px; } - .mdl-button--icon .mdl-button__ripple-container { - border-radius: 50%; - -webkit-mask-image: -webkit-radial-gradient(circle, white, black); } - -.mdl-button__ripple-container { - display: block; - height: 100%; - left: 0px; - position: absolute; - top: 0px; - width: 100%; - z-index: 0; - overflow: hidden; } - .mdl-button[disabled] .mdl-button__ripple-container .mdl-ripple, - .mdl-button.mdl-button--disabled .mdl-button__ripple-container .mdl-ripple { - background-color: transparent; } - -.mdl-button--primary.mdl-button--primary { - color: rgb(63,81,181); } - .mdl-button--primary.mdl-button--primary .mdl-ripple { - background: rgb(255,255,255); } - .mdl-button--primary.mdl-button--primary.mdl-button--raised, .mdl-button--primary.mdl-button--primary.mdl-button--fab { - color: rgb(255,255,255); - background-color: rgb(63,81,181); } - -.mdl-button--accent.mdl-button--accent { - color: rgb(255,64,129); } - .mdl-button--accent.mdl-button--accent .mdl-ripple { - background: rgb(255,255,255); } - .mdl-button--accent.mdl-button--accent.mdl-button--raised, .mdl-button--accent.mdl-button--accent.mdl-button--fab { - color: rgb(255,255,255); - background-color: rgb(255,64,129); } - -.mdl-button[disabled][disabled], .mdl-button.mdl-button--disabled.mdl-button--disabled { - color: rgba(0,0,0, 0.26); - cursor: default; - background-color: transparent; } - -.mdl-button--fab[disabled][disabled], .mdl-button--fab.mdl-button--disabled.mdl-button--disabled { - background-color: rgba(0,0,0, 0.12); - color: rgba(0,0,0, 0.26); - box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12); } - -.mdl-button--raised[disabled][disabled], .mdl-button--raised.mdl-button--disabled.mdl-button--disabled { - background-color: rgba(0,0,0, 0.12); - color: rgba(0,0,0, 0.26); - box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12); } - -.mdl-button--colored[disabled][disabled], .mdl-button--colored.mdl-button--disabled.mdl-button--disabled { - color: rgba(0,0,0, 0.26); } - -.mdl-button .material-icons { - vertical-align: middle; } - -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/*------------------------------------* $CONTENTS -\*------------------------------------*/ -/** - * STYLE GUIDE VARIABLES------------------Declarations of Sass variables - * -----Typography - * -----Colors - * -----Textfield - * -----Switch - * -----Spinner - * -----Radio - * -----Menu - * -----List - * -----Layout - * -----Icon toggles - * -----Footer - * -----Column - * -----Checkbox - * -----Card - * -----Button - * -----Animation - * -----Progress - * -----Badge - * -----Shadows - * -----Grid - * -----Data table - */ -/* ========== TYPOGRAPHY ========== */ -/* We're splitting fonts into "preferred" and "performance" in order to optimize - page loading. For important text, such as the body, we want it to load - immediately and not wait for the web font load, whereas for other sections, - such as headers and titles, we're OK with things taking a bit longer to load. - We do have some optional classes and parameters in the mixins, in case you - definitely want to make sure you're using the preferred font and don't mind - the performance hit. - We should be able to improve on this once CSS Font Loading L3 becomes more - widely available. -*/ -/* ========== COLORS ========== */ -/** -* -* Material design color palettes. -* @see http://www.google.com/design/spec/style/color.html -* -**/ -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/* ========== Color Palettes ========== */ -/* colors.scss */ -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/* ========== IMAGES ========== */ -/* ========== Color & Themes ========== */ -/* ========== Typography ========== */ -/* ========== Components ========== */ -/* ========== Standard Buttons ========== */ -/* ========== Icon Toggles ========== */ -/* ========== Radio Buttons ========== */ -/* ========== Ripple effect ========== */ -/* ========== Layout ========== */ -/* ========== Content Tabs ========== */ -/* ========== Checkboxes ========== */ -/* ========== Switches ========== */ -/* ========== Spinner ========== */ -/* ========== Text fields ========== */ -/* ========== Card ========== */ -/* ========== Sliders ========== */ -/* ========== Progress ========== */ -/* ========== List ========== */ -/* ========== Item ========== */ -/* ========== Dropdown menu ========== */ -/* ========== Tooltips ========== */ -/* ========== Footer ========== */ -/* TEXTFIELD */ -/* SWITCH */ -/* SPINNER */ -/* RADIO */ -/* MENU */ -/* LIST */ -/* LAYOUT */ -/* ICON TOGGLE */ -/* FOOTER */ -/*mega-footer*/ -/*mini-footer*/ -/* CHECKBOX */ -/* CARD */ -/* Card dimensions */ -/* Cover image */ -/* BUTTON */ -/** - * - * Dimensions - * - */ -/* ANIMATION */ -/* PROGRESS */ -/* BADGE */ -/* SHADOWS */ -/* GRID */ -/* DATA TABLE */ -/* TOOLTIP */ -.mdl-card { - display: -webkit-box; - display: -webkit-flex; - display: -ms-flexbox; - display: flex; - -webkit-box-orient: vertical; - -webkit-box-direction: normal; - -webkit-flex-direction: column; - -ms-flex-direction: column; - flex-direction: column; - font-size: 16px; - font-weight: 400; - min-height: 200px; - overflow: hidden; - width: 330px; - z-index: 1; - position: relative; - background: rgb(255,255,255); - border-radius: 2px; - box-sizing: border-box; } - -.mdl-card__media { - background-color: rgb(255,64,129); - background-repeat: repeat; - background-position: 50% 50%; - background-size: cover; - background-origin: padding-box; - background-attachment: scroll; - box-sizing: border-box; } - -.mdl-card__title { - -webkit-box-align: center; - -webkit-align-items: center; - -ms-flex-align: center; - align-items: center; - color: rgb(0,0,0); - display: block; - display: -webkit-box; - display: -webkit-flex; - display: -ms-flexbox; - display: flex; - -webkit-box-pack: stretch; - -webkit-justify-content: stretch; - -ms-flex-pack: stretch; - justify-content: stretch; - line-height: normal; - padding: 16px 16px; - -webkit-perspective-origin: 165px 56px; - perspective-origin: 165px 56px; - -webkit-transform-origin: 165px 56px; - -ms-transform-origin: 165px 56px; - transform-origin: 165px 56px; - box-sizing: border-box; } - .mdl-card__title.mdl-card--border { - border-bottom: 1px solid rgba(0, 0, 0, 0.1); } - -.mdl-card__title-text { - -webkit-align-self: flex-end; - -ms-flex-item-align: end; - align-self: flex-end; - color: inherit; - display: block; - display: -webkit-box; - display: -webkit-flex; - display: -ms-flexbox; - display: flex; - font-size: 24px; - font-weight: 300; - line-height: normal; - overflow: hidden; - -webkit-transform-origin: 149px 48px; - -ms-transform-origin: 149px 48px; - transform-origin: 149px 48px; - margin: 0; } - -.mdl-card__subtitle-text { - font-size: 14px; - color: rgba(0,0,0, 0.54); - margin: 0; } - -.mdl-card__supporting-text { - color: rgba(0,0,0, 0.54); - font-size: 13px; - line-height: 18px; - overflow: hidden; - padding: 16px 16px; - width: 90%; } - -.mdl-card__actions { - font-size: 16px; - line-height: normal; - width: 100%; - background-color: transparent; - padding: 8px; - box-sizing: border-box; } - .mdl-card__actions.mdl-card--border { - border-top: 1px solid rgba(0, 0, 0, 0.1); } - -.mdl-card--expand { - -webkit-box-flex: 1; - -webkit-flex-grow: 1; - -ms-flex-positive: 1; - flex-grow: 1; } - -.mdl-card__menu { - position: absolute; - right: 16px; - top: 16px; } - -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/*------------------------------------* $CONTENTS -\*------------------------------------*/ -/** - * STYLE GUIDE VARIABLES------------------Declarations of Sass variables - * -----Typography - * -----Colors - * -----Textfield - * -----Switch - * -----Spinner - * -----Radio - * -----Menu - * -----List - * -----Layout - * -----Icon toggles - * -----Footer - * -----Column - * -----Checkbox - * -----Card - * -----Button - * -----Animation - * -----Progress - * -----Badge - * -----Shadows - * -----Grid - * -----Data table - */ -/* ========== TYPOGRAPHY ========== */ -/* We're splitting fonts into "preferred" and "performance" in order to optimize - page loading. For important text, such as the body, we want it to load - immediately and not wait for the web font load, whereas for other sections, - such as headers and titles, we're OK with things taking a bit longer to load. - We do have some optional classes and parameters in the mixins, in case you - definitely want to make sure you're using the preferred font and don't mind - the performance hit. - We should be able to improve on this once CSS Font Loading L3 becomes more - widely available. -*/ -/* ========== COLORS ========== */ -/** -* -* Material design color palettes. -* @see http://www.google.com/design/spec/style/color.html -* -**/ -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/* ========== Color Palettes ========== */ -/* colors.scss */ -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/* ========== IMAGES ========== */ -/* ========== Color & Themes ========== */ -/* ========== Typography ========== */ -/* ========== Components ========== */ -/* ========== Standard Buttons ========== */ -/* ========== Icon Toggles ========== */ -/* ========== Radio Buttons ========== */ -/* ========== Ripple effect ========== */ -/* ========== Layout ========== */ -/* ========== Content Tabs ========== */ -/* ========== Checkboxes ========== */ -/* ========== Switches ========== */ -/* ========== Spinner ========== */ -/* ========== Text fields ========== */ -/* ========== Card ========== */ -/* ========== Sliders ========== */ -/* ========== Progress ========== */ -/* ========== List ========== */ -/* ========== Item ========== */ -/* ========== Dropdown menu ========== */ -/* ========== Tooltips ========== */ -/* ========== Footer ========== */ -/* TEXTFIELD */ -/* SWITCH */ -/* SPINNER */ -/* RADIO */ -/* MENU */ -/* LIST */ -/* LAYOUT */ -/* ICON TOGGLE */ -/* FOOTER */ -/*mega-footer*/ -/*mini-footer*/ -/* CHECKBOX */ -/* CARD */ -/* Card dimensions */ -/* Cover image */ -/* BUTTON */ -/** - * - * Dimensions - * - */ -/* ANIMATION */ -/* PROGRESS */ -/* BADGE */ -/* SHADOWS */ -/* GRID */ -/* DATA TABLE */ -/* TOOLTIP */ -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/* Typography */ -/* Shadows */ -/* Animations */ -.mdl-checkbox { - position: relative; - z-index: 1; - vertical-align: middle; - display: inline-block; - box-sizing: border-box; - width: 100%; - height: 24px; - margin: 0; - padding: 0; } - .mdl-checkbox.is-upgraded { - padding-left: 24px; } - -.mdl-checkbox__input { - line-height: 24px; } - .mdl-checkbox.is-upgraded .mdl-checkbox__input { - position: absolute; - width: 0; - height: 0; - margin: 0; - padding: 0; - opacity: 0; - -ms-appearance: none; - -moz-appearance: none; - -webkit-appearance: none; - appearance: none; - border: none; } - -.mdl-checkbox__box-outline { - position: absolute; - top: 3px; - left: 0; - display: inline-block; - box-sizing: border-box; - width: 16px; - height: 16px; - margin: 0; - cursor: pointer; - overflow: hidden; - border: 2px solid rgba(0,0,0, 0.54); - border-radius: 2px; - z-index: 2; } - .mdl-checkbox.is-checked .mdl-checkbox__box-outline { - border: 2px solid rgb(63,81,181); } - .mdl-checkbox.is-disabled .mdl-checkbox__box-outline { - border: 2px solid rgba(0,0,0, 0.26); - cursor: auto; } - -.mdl-checkbox__focus-helper { - position: absolute; - top: 3px; - left: 0; - display: inline-block; - box-sizing: border-box; - width: 16px; - height: 16px; - border-radius: 50%; - background-color: transparent; } - .mdl-checkbox.is-focused .mdl-checkbox__focus-helper { - box-shadow: 0 0 0px 8px rgba(0, 0, 0, 0.1); - background-color: rgba(0, 0, 0, 0.1); } - .mdl-checkbox.is-focused.is-checked .mdl-checkbox__focus-helper { - box-shadow: 0 0 0px 8px rgba(63,81,181, 0.26); - background-color: rgba(63,81,181, 0.26); } - -.mdl-checkbox__tick-outline { - position: absolute; - top: 0; - left: 0; - height: 100%; - width: 100%; - -webkit-mask: url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcKICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICB4bWxuczpjYz0iaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbnMjIgogICB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiCiAgIHhtbG5zOnN2Zz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgdmVyc2lvbj0iMS4xIgogICB2aWV3Qm94PSIwIDAgMSAxIgogICBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWluWU1pbiBtZWV0Ij4KICA8ZGVmcz4KICAgIDxjbGlwUGF0aCBpZD0iY2xpcCI+CiAgICAgIDxwYXRoCiAgICAgICAgIGQ9Ik0gMCwwIDAsMSAxLDEgMSwwIDAsMCB6IE0gMC44NTM0Mzc1LDAuMTY3MTg3NSAwLjk1OTY4NzUsMC4yNzMxMjUgMC40MjkzNzUsMC44MDM0Mzc1IDAuMzIzMTI1LDAuOTA5Njg3NSAwLjIxNzE4NzUsMC44MDM0Mzc1IDAuMDQwMzEyNSwwLjYyNjg3NSAwLjE0NjU2MjUsMC41MjA2MjUgMC4zMjMxMjUsMC42OTc1IDAuODUzNDM3NSwwLjE2NzE4NzUgeiIKICAgICAgICAgc3R5bGU9ImZpbGw6I2ZmZmZmZjtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZSIgLz4KICAgIDwvY2xpcFBhdGg+CiAgICA8bWFzayBpZD0ibWFzayIgbWFza1VuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgbWFza0NvbnRlbnRVbml0cz0ib2JqZWN0Qm91bmRpbmdCb3giPgogICAgICA8cGF0aAogICAgICAgICBkPSJNIDAsMCAwLDEgMSwxIDEsMCAwLDAgeiBNIDAuODUzNDM3NSwwLjE2NzE4NzUgMC45NTk2ODc1LDAuMjczMTI1IDAuNDI5Mzc1LDAuODAzNDM3NSAwLjMyMzEyNSwwLjkwOTY4NzUgMC4yMTcxODc1LDAuODAzNDM3NSAwLjA0MDMxMjUsMC42MjY4NzUgMC4xNDY1NjI1LDAuNTIwNjI1IDAuMzIzMTI1LDAuNjk3NSAwLjg1MzQzNzUsMC4xNjcxODc1IHoiCiAgICAgICAgIHN0eWxlPSJmaWxsOiNmZmZmZmY7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmUiIC8+CiAgICA8L21hc2s+CiAgPC9kZWZzPgogIDxyZWN0CiAgICAgd2lkdGg9IjEiCiAgICAgaGVpZ2h0PSIxIgogICAgIHg9IjAiCiAgICAgeT0iMCIKICAgICBjbGlwLXBhdGg9InVybCgjY2xpcCkiCiAgICAgc3R5bGU9ImZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZSIgLz4KPC9zdmc+Cg=="); - mask: url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcKICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICB4bWxuczpjYz0iaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbnMjIgogICB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiCiAgIHhtbG5zOnN2Zz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgdmVyc2lvbj0iMS4xIgogICB2aWV3Qm94PSIwIDAgMSAxIgogICBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWluWU1pbiBtZWV0Ij4KICA8ZGVmcz4KICAgIDxjbGlwUGF0aCBpZD0iY2xpcCI+CiAgICAgIDxwYXRoCiAgICAgICAgIGQ9Ik0gMCwwIDAsMSAxLDEgMSwwIDAsMCB6IE0gMC44NTM0Mzc1LDAuMTY3MTg3NSAwLjk1OTY4NzUsMC4yNzMxMjUgMC40MjkzNzUsMC44MDM0Mzc1IDAuMzIzMTI1LDAuOTA5Njg3NSAwLjIxNzE4NzUsMC44MDM0Mzc1IDAuMDQwMzEyNSwwLjYyNjg3NSAwLjE0NjU2MjUsMC41MjA2MjUgMC4zMjMxMjUsMC42OTc1IDAuODUzNDM3NSwwLjE2NzE4NzUgeiIKICAgICAgICAgc3R5bGU9ImZpbGw6I2ZmZmZmZjtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZSIgLz4KICAgIDwvY2xpcFBhdGg+CiAgICA8bWFzayBpZD0ibWFzayIgbWFza1VuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgbWFza0NvbnRlbnRVbml0cz0ib2JqZWN0Qm91bmRpbmdCb3giPgogICAgICA8cGF0aAogICAgICAgICBkPSJNIDAsMCAwLDEgMSwxIDEsMCAwLDAgeiBNIDAuODUzNDM3NSwwLjE2NzE4NzUgMC45NTk2ODc1LDAuMjczMTI1IDAuNDI5Mzc1LDAuODAzNDM3NSAwLjMyMzEyNSwwLjkwOTY4NzUgMC4yMTcxODc1LDAuODAzNDM3NSAwLjA0MDMxMjUsMC42MjY4NzUgMC4xNDY1NjI1LDAuNTIwNjI1IDAuMzIzMTI1LDAuNjk3NSAwLjg1MzQzNzUsMC4xNjcxODc1IHoiCiAgICAgICAgIHN0eWxlPSJmaWxsOiNmZmZmZmY7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmUiIC8+CiAgICA8L21hc2s+CiAgPC9kZWZzPgogIDxyZWN0CiAgICAgd2lkdGg9IjEiCiAgICAgaGVpZ2h0PSIxIgogICAgIHg9IjAiCiAgICAgeT0iMCIKICAgICBjbGlwLXBhdGg9InVybCgjY2xpcCkiCiAgICAgc3R5bGU9ImZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZSIgLz4KPC9zdmc+Cg=="); - background: transparent; - -webkit-transition-duration: 0.28s; - transition-duration: 0.28s; - -webkit-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); - -webkit-transition-property: background; - transition-property: background; } - .mdl-checkbox.is-checked .mdl-checkbox__tick-outline { - background: rgb(63,81,181) url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcKICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICB4bWxuczpjYz0iaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbnMjIgogICB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiCiAgIHhtbG5zOnN2Zz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgdmVyc2lvbj0iMS4xIgogICB2aWV3Qm94PSIwIDAgMSAxIgogICBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWluWU1pbiBtZWV0Ij4KICA8cGF0aAogICAgIGQ9Ik0gMC4wNDAzODA1OSwwLjYyNjc3NjcgMC4xNDY0NDY2MSwwLjUyMDcxMDY4IDAuNDI5Mjg5MzIsMC44MDM1NTMzOSAwLjMyMzIyMzMsMC45MDk2MTk0MSB6IE0gMC4yMTcxNTcyOSwwLjgwMzU1MzM5IDAuODUzNTUzMzksMC4xNjcxNTcyOSAwLjk1OTYxOTQxLDAuMjczMjIzMyAwLjMyMzIyMzMsMC45MDk2MTk0MSB6IgogICAgIGlkPSJyZWN0Mzc4MCIKICAgICBzdHlsZT0iZmlsbDojZmZmZmZmO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lIiAvPgo8L3N2Zz4K"); } - .mdl-checkbox.is-checked.is-disabled .mdl-checkbox__tick-outline { - background: rgba(0,0,0, 0.26) url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcKICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICB4bWxuczpjYz0iaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbnMjIgogICB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiCiAgIHhtbG5zOnN2Zz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgdmVyc2lvbj0iMS4xIgogICB2aWV3Qm94PSIwIDAgMSAxIgogICBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWluWU1pbiBtZWV0Ij4KICA8cGF0aAogICAgIGQ9Ik0gMC4wNDAzODA1OSwwLjYyNjc3NjcgMC4xNDY0NDY2MSwwLjUyMDcxMDY4IDAuNDI5Mjg5MzIsMC44MDM1NTMzOSAwLjMyMzIyMzMsMC45MDk2MTk0MSB6IE0gMC4yMTcxNTcyOSwwLjgwMzU1MzM5IDAuODUzNTUzMzksMC4xNjcxNTcyOSAwLjk1OTYxOTQxLDAuMjczMjIzMyAwLjMyMzIyMzMsMC45MDk2MTk0MSB6IgogICAgIGlkPSJyZWN0Mzc4MCIKICAgICBzdHlsZT0iZmlsbDojZmZmZmZmO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lIiAvPgo8L3N2Zz4K"); } - -.mdl-checkbox__label { - position: relative; - cursor: pointer; - font-size: 16px; - line-height: 24px; - margin: 0; } - .mdl-checkbox.is-disabled .mdl-checkbox__label { - color: rgba(0,0,0, 0.26); - cursor: auto; } - -.mdl-checkbox__ripple-container { - position: absolute; - z-index: 2; - top: -6px; - left: -10px; - box-sizing: border-box; - width: 36px; - height: 36px; - border-radius: 50%; - cursor: pointer; - overflow: hidden; - -webkit-mask-image: -webkit-radial-gradient(circle, white, black); } - .mdl-checkbox__ripple-container .mdl-ripple { - background: rgb(63,81,181); } - .mdl-checkbox.is-disabled .mdl-checkbox__ripple-container { - cursor: auto; } - .mdl-checkbox.is-disabled .mdl-checkbox__ripple-container .mdl-ripple { - background: transparent; } - -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/*------------------------------------* $CONTENTS -\*------------------------------------*/ -/** - * STYLE GUIDE VARIABLES------------------Declarations of Sass variables - * -----Typography - * -----Colors - * -----Textfield - * -----Switch - * -----Spinner - * -----Radio - * -----Menu - * -----List - * -----Layout - * -----Icon toggles - * -----Footer - * -----Column - * -----Checkbox - * -----Card - * -----Button - * -----Animation - * -----Progress - * -----Badge - * -----Shadows - * -----Grid - * -----Data table - */ -/* ========== TYPOGRAPHY ========== */ -/* We're splitting fonts into "preferred" and "performance" in order to optimize - page loading. For important text, such as the body, we want it to load - immediately and not wait for the web font load, whereas for other sections, - such as headers and titles, we're OK with things taking a bit longer to load. - We do have some optional classes and parameters in the mixins, in case you - definitely want to make sure you're using the preferred font and don't mind - the performance hit. - We should be able to improve on this once CSS Font Loading L3 becomes more - widely available. -*/ -/* ========== COLORS ========== */ -/** -* -* Material design color palettes. -* @see http://www.google.com/design/spec/style/color.html -* -**/ -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/* ========== Color Palettes ========== */ -/* colors.scss */ -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/* ========== IMAGES ========== */ -/* ========== Color & Themes ========== */ -/* ========== Typography ========== */ -/* ========== Components ========== */ -/* ========== Standard Buttons ========== */ -/* ========== Icon Toggles ========== */ -/* ========== Radio Buttons ========== */ -/* ========== Ripple effect ========== */ -/* ========== Layout ========== */ -/* ========== Content Tabs ========== */ -/* ========== Checkboxes ========== */ -/* ========== Switches ========== */ -/* ========== Spinner ========== */ -/* ========== Text fields ========== */ -/* ========== Card ========== */ -/* ========== Sliders ========== */ -/* ========== Progress ========== */ -/* ========== List ========== */ -/* ========== Item ========== */ -/* ========== Dropdown menu ========== */ -/* ========== Tooltips ========== */ -/* ========== Footer ========== */ -/* TEXTFIELD */ -/* SWITCH */ -/* SPINNER */ -/* RADIO */ -/* MENU */ -/* LIST */ -/* LAYOUT */ -/* ICON TOGGLE */ -/* FOOTER */ -/*mega-footer*/ -/*mini-footer*/ -/* CHECKBOX */ -/* CARD */ -/* Card dimensions */ -/* Cover image */ -/* BUTTON */ -/** - * - * Dimensions - * - */ -/* ANIMATION */ -/* PROGRESS */ -/* BADGE */ -/* SHADOWS */ -/* GRID */ -/* DATA TABLE */ -/* TOOLTIP */ -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/* Typography */ -/* Shadows */ -/* Animations */ -.mdl-data-table { - position: relative; - border: 1px solid rgba(0, 0, 0, 0.12); - border-collapse: collapse; - white-space: nowrap; - font-size: 13px; - background-color: rgb(255,255,255); } - .mdl-data-table thead { - padding-bottom: 3px; } - .mdl-data-table thead .mdl-data-table__select { - margin-top: 0; } - .mdl-data-table tbody tr { - position: relative; - height: 48px; - -webkit-transition-duration: 0.28s; - transition-duration: 0.28s; - -webkit-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); - -webkit-transition-property: background-color; - transition-property: background-color; } - .mdl-data-table tbody tr.is-selected { - background-color: #e0e0e0; } - .mdl-data-table tbody tr:hover { - background-color: #eeeeee; } - .mdl-data-table td, .mdl-data-table th { - padding: 0 18px 0 18px; - text-align: right; } - .mdl-data-table td:first-of-type, .mdl-data-table th:first-of-type { - padding-left: 24px; } - .mdl-data-table td:last-of-type, .mdl-data-table th:last-of-type { - padding-right: 24px; } - .mdl-data-table td { - position: relative; - vertical-align: top; - height: 48px; - border-top: 1px solid rgba(0, 0, 0, 0.12); - border-bottom: 1px solid rgba(0, 0, 0, 0.12); - padding-top: 12px; - box-sizing: border-box; } - .mdl-data-table td .mdl-data-table__select { - vertical-align: top; - position: absolute; - left: 24px; } - .mdl-data-table th { - position: relative; - vertical-align: bottom; - text-overflow: ellipsis; - font-size: 14px; - font-weight: bold; - line-height: 24px; - letter-spacing: 0; - height: 48px; - font-size: 12px; - color: rgba(0, 0, 0, 0.54); - padding-bottom: 8px; - box-sizing: border-box; } - .mdl-data-table th .mdl-data-table__select { - position: absolute; - bottom: 8px; - left: 24px; } - -.mdl-data-table__select { - width: 16px; } - -.mdl-data-table__cell--non-numeric.mdl-data-table__cell--non-numeric { - text-align: left; } - -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/*------------------------------------* $CONTENTS -\*------------------------------------*/ -/** - * STYLE GUIDE VARIABLES------------------Declarations of Sass variables - * -----Typography - * -----Colors - * -----Textfield - * -----Switch - * -----Spinner - * -----Radio - * -----Menu - * -----List - * -----Layout - * -----Icon toggles - * -----Footer - * -----Column - * -----Checkbox - * -----Card - * -----Button - * -----Animation - * -----Progress - * -----Badge - * -----Shadows - * -----Grid - * -----Data table - */ -/* ========== TYPOGRAPHY ========== */ -/* We're splitting fonts into "preferred" and "performance" in order to optimize - page loading. For important text, such as the body, we want it to load - immediately and not wait for the web font load, whereas for other sections, - such as headers and titles, we're OK with things taking a bit longer to load. - We do have some optional classes and parameters in the mixins, in case you - definitely want to make sure you're using the preferred font and don't mind - the performance hit. - We should be able to improve on this once CSS Font Loading L3 becomes more - widely available. -*/ -/* ========== COLORS ========== */ -/** -* -* Material design color palettes. -* @see http://www.google.com/design/spec/style/color.html -* -**/ -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/* ========== Color Palettes ========== */ -/* colors.scss */ -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/* ========== IMAGES ========== */ -/* ========== Color & Themes ========== */ -/* ========== Typography ========== */ -/* ========== Components ========== */ -/* ========== Standard Buttons ========== */ -/* ========== Icon Toggles ========== */ -/* ========== Radio Buttons ========== */ -/* ========== Ripple effect ========== */ -/* ========== Layout ========== */ -/* ========== Content Tabs ========== */ -/* ========== Checkboxes ========== */ -/* ========== Switches ========== */ -/* ========== Spinner ========== */ -/* ========== Text fields ========== */ -/* ========== Card ========== */ -/* ========== Sliders ========== */ -/* ========== Progress ========== */ -/* ========== List ========== */ -/* ========== Item ========== */ -/* ========== Dropdown menu ========== */ -/* ========== Tooltips ========== */ -/* ========== Footer ========== */ -/* TEXTFIELD */ -/* SWITCH */ -/* SPINNER */ -/* RADIO */ -/* MENU */ -/* LIST */ -/* LAYOUT */ -/* ICON TOGGLE */ -/* FOOTER */ -/*mega-footer*/ -/*mini-footer*/ -/* CHECKBOX */ -/* CARD */ -/* Card dimensions */ -/* Cover image */ -/* BUTTON */ -/** - * - * Dimensions - * - */ -/* ANIMATION */ -/* PROGRESS */ -/* BADGE */ -/* SHADOWS */ -/* GRID */ -/* DATA TABLE */ -/* TOOLTIP */ -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/* Typography */ -/* Shadows */ -/* Animations */ -.mdl-mega-footer { - padding: 16px 40px; - color: rgb(158,158,158); - background-color: rgb(66,66,66); } - -.mdl-mega-footer--top-section:after, -.mdl-mega-footer--middle-section:after, -.mdl-mega-footer--bottom-section:after, -.mdl-mega-footer__top-section:after, -.mdl-mega-footer__middle-section:after, -.mdl-mega-footer__bottom-section:after { - content: ''; - display: block; - clear: both; } - -.mdl-mega-footer--left-section, -.mdl-mega-footer__left-section { - margin-bottom: 16px; } - -.mdl-mega-footer--right-section, -.mdl-mega-footer__right-section { - margin-bottom: 16px; } - -.mdl-mega-footer--right-section a, -.mdl-mega-footer__right-section a { - display: block; - margin-bottom: 16px; - color: inherit; - text-decoration: none; } - -@media screen and (min-width: 760px) { - .mdl-mega-footer--left-section, - .mdl-mega-footer__left-section { - float: left; } - .mdl-mega-footer--right-section, - .mdl-mega-footer__right-section { - float: right; } - .mdl-mega-footer--right-section a, - .mdl-mega-footer__right-section a { - display: inline-block; - margin-left: 16px; - line-height: 36px; - vertical-align: middle; } } - -.mdl-mega-footer--social-btn, -.mdl-mega-footer__social-btn { - width: 36px; - height: 36px; - padding: 0; - margin: 0; - background-color: rgb(158,158,158); - border: none; } - -.mdl-mega-footer--drop-down-section, -.mdl-mega-footer__drop-down-section { - display: block; - position: relative; } - -@media screen and (min-width: 760px) { - .mdl-mega-footer--drop-down-section, - .mdl-mega-footer__drop-down-section { - width: 33%; } - .mdl-mega-footer--drop-down-section:nth-child(1), - .mdl-mega-footer--drop-down-section:nth-child(2), - .mdl-mega-footer__drop-down-section:nth-child(1), - .mdl-mega-footer__drop-down-section:nth-child(2) { - float: left; } - .mdl-mega-footer--drop-down-section:nth-child(3), - .mdl-mega-footer__drop-down-section:nth-child(3) { - float: right; } - .mdl-mega-footer--drop-down-section:nth-child(3):after, - .mdl-mega-footer__drop-down-section:nth-child(3):after { - clear: right; } - .mdl-mega-footer--drop-down-section:nth-child(4), - .mdl-mega-footer__drop-down-section:nth-child(4) { - clear: right; - float: right; } - .mdl-mega-footer--middle-section:after, - .mdl-mega-footer__middle-section:after { - content: ''; - display: block; - clear: both; } - .mdl-mega-footer--bottom-section, - .mdl-mega-footer__bottom-section { - padding-top: 0; } } - -@media screen and (min-width: 1024px) { - .mdl-mega-footer--drop-down-section, - .mdl-mega-footer--drop-down-section:nth-child(3), - .mdl-mega-footer--drop-down-section:nth-child(4), - .mdl-mega-footer__drop-down-section, - .mdl-mega-footer__drop-down-section:nth-child(3), - .mdl-mega-footer__drop-down-section:nth-child(4) { - width: 24%; - float: left; } } - -.mdl-mega-footer--heading-checkbox, -.mdl-mega-footer__heading-checkbox { - position: absolute; - width: 100%; - height: 55.8px; - padding: 32px; - margin: 0; - margin-top: -16px; - cursor: pointer; - z-index: 1; - opacity: 0; } - .mdl-mega-footer--heading-checkbox + .mdl-mega-footer--heading:after, - .mdl-mega-footer--heading-checkbox + .mdl-mega-footer__heading:after, - .mdl-mega-footer__heading-checkbox + .mdl-mega-footer--heading:after, - .mdl-mega-footer__heading-checkbox + .mdl-mega-footer__heading:after { - font-family: 'Material Icons'; - content: '\E5CE'; } - -.mdl-mega-footer--heading-checkbox:checked ~ .mdl-mega-footer--link-list, -.mdl-mega-footer--heading-checkbox:checked ~ .mdl-mega-footer__link-list, -.mdl-mega-footer--heading-checkbox:checked + .mdl-mega-footer--heading + .mdl-mega-footer--link-list, -.mdl-mega-footer--heading-checkbox:checked + .mdl-mega-footer__heading + .mdl-mega-footer__link-list, -.mdl-mega-footer__heading-checkbox:checked ~ .mdl-mega-footer--link-list, -.mdl-mega-footer__heading-checkbox:checked ~ .mdl-mega-footer__link-list, -.mdl-mega-footer__heading-checkbox:checked + .mdl-mega-footer--heading + .mdl-mega-footer--link-list, -.mdl-mega-footer__heading-checkbox:checked + .mdl-mega-footer__heading + .mdl-mega-footer__link-list { - display: none; } - -.mdl-mega-footer--heading-checkbox:checked + .mdl-mega-footer--heading:after, -.mdl-mega-footer--heading-checkbox:checked + .mdl-mega-footer__heading:after, -.mdl-mega-footer__heading-checkbox:checked + .mdl-mega-footer--heading:after, -.mdl-mega-footer__heading-checkbox:checked + .mdl-mega-footer__heading:after { - font-family: 'Material Icons'; - content: '\E5CF'; } - -.mdl-mega-footer--heading, -.mdl-mega-footer__heading { - position: relative; - width: 100%; - padding-right: 39.8px; - margin-bottom: 16px; - box-sizing: border-box; - font-size: 14px; - line-height: 23.8px; - font-weight: 500; - white-space: nowrap; - text-overflow: ellipsis; - overflow: hidden; - color: rgb(224,224,224); } - -.mdl-mega-footer--heading:after, -.mdl-mega-footer__heading:after { - content: ''; - position: absolute; - top: 0; - right: 0; - display: block; - width: 23.8px; - height: 23.8px; - background-size: cover; } - -.mdl-mega-footer--link-list, -.mdl-mega-footer__link-list { - list-style: none; - margin: 0; - padding: 0; - margin-bottom: 32px; } - .mdl-mega-footer--link-list:after, - .mdl-mega-footer__link-list:after { - clear: both; - display: block; - content: ''; } - -.mdl-mega-footer--link-list li, -.mdl-mega-footer__link-list li { - font-size: 14px; - font-weight: 400; - line-height: 24px; - letter-spacing: 0; - line-height: 20px; } - -.mdl-mega-footer--link-list a, -.mdl-mega-footer__link-list a { - color: inherit; - text-decoration: none; - white-space: nowrap; } - -@media screen and (min-width: 760px) { - .mdl-mega-footer--heading-checkbox, - .mdl-mega-footer__heading-checkbox { - display: none; } - .mdl-mega-footer--heading-checkbox + .mdl-mega-footer--heading:after, - .mdl-mega-footer--heading-checkbox + .mdl-mega-footer__heading:after, - .mdl-mega-footer__heading-checkbox + .mdl-mega-footer--heading:after, - .mdl-mega-footer__heading-checkbox + .mdl-mega-footer__heading:after { - background-image: none; } - .mdl-mega-footer--heading-checkbox:checked ~ .mdl-mega-footer--link-list, - .mdl-mega-footer--heading-checkbox:checked ~ .mdl-mega-footer__link-list, - .mdl-mega-footer--heading-checkbox:checked + .mdl-mega-footer__heading + .mdl-mega-footer__link-list, - .mdl-mega-footer--heading-checkbox:checked + .mdl-mega-footer--heading + .mdl-mega-footer--link-list, - .mdl-mega-footer__heading-checkbox:checked ~ .mdl-mega-footer--link-list, - .mdl-mega-footer__heading-checkbox:checked ~ .mdl-mega-footer__link-list, - .mdl-mega-footer__heading-checkbox:checked + .mdl-mega-footer__heading + .mdl-mega-footer__link-list, - .mdl-mega-footer__heading-checkbox:checked + .mdl-mega-footer--heading + .mdl-mega-footer--link-list { - display: block; } - .mdl-mega-footer--heading-checkbox:checked + .mdl-mega-footer--heading:after, - .mdl-mega-footer--heading-checkbox:checked + .mdl-mega-footer__heading:after, - .mdl-mega-footer__heading-checkbox:checked + .mdl-mega-footer--heading:after, - .mdl-mega-footer__heading-checkbox:checked + .mdl-mega-footer__heading:after { - content: ''; } } - -.mdl-mega-footer--bottom-section, -.mdl-mega-footer__bottom-section { - padding-top: 16px; - margin-bottom: 16px; } - -.mdl-logo { - margin-bottom: 16px; - color: white; } - -.mdl-mega-footer--bottom-section .mdl-mega-footer--link-list li, -.mdl-mega-footer__bottom-section .mdl-mega-footer__link-list li { - float: left; - margin-bottom: 0; - margin-right: 16px; } - -@media screen and (min-width: 760px) { - .mdl-logo { - float: left; - margin-bottom: 0; - margin-right: 16px; } } - -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/*------------------------------------* $CONTENTS -\*------------------------------------*/ -/** - * STYLE GUIDE VARIABLES------------------Declarations of Sass variables - * -----Typography - * -----Colors - * -----Textfield - * -----Switch - * -----Spinner - * -----Radio - * -----Menu - * -----List - * -----Layout - * -----Icon toggles - * -----Footer - * -----Column - * -----Checkbox - * -----Card - * -----Button - * -----Animation - * -----Progress - * -----Badge - * -----Shadows - * -----Grid - * -----Data table - */ -/* ========== TYPOGRAPHY ========== */ -/* We're splitting fonts into "preferred" and "performance" in order to optimize - page loading. For important text, such as the body, we want it to load - immediately and not wait for the web font load, whereas for other sections, - such as headers and titles, we're OK with things taking a bit longer to load. - We do have some optional classes and parameters in the mixins, in case you - definitely want to make sure you're using the preferred font and don't mind - the performance hit. - We should be able to improve on this once CSS Font Loading L3 becomes more - widely available. -*/ -/* ========== COLORS ========== */ -/** -* -* Material design color palettes. -* @see http://www.google.com/design/spec/style/color.html -* -**/ -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/* ========== Color Palettes ========== */ -/* colors.scss */ -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/* ========== IMAGES ========== */ -/* ========== Color & Themes ========== */ -/* ========== Typography ========== */ -/* ========== Components ========== */ -/* ========== Standard Buttons ========== */ -/* ========== Icon Toggles ========== */ -/* ========== Radio Buttons ========== */ -/* ========== Ripple effect ========== */ -/* ========== Layout ========== */ -/* ========== Content Tabs ========== */ -/* ========== Checkboxes ========== */ -/* ========== Switches ========== */ -/* ========== Spinner ========== */ -/* ========== Text fields ========== */ -/* ========== Card ========== */ -/* ========== Sliders ========== */ -/* ========== Progress ========== */ -/* ========== List ========== */ -/* ========== Item ========== */ -/* ========== Dropdown menu ========== */ -/* ========== Tooltips ========== */ -/* ========== Footer ========== */ -/* TEXTFIELD */ -/* SWITCH */ -/* SPINNER */ -/* RADIO */ -/* MENU */ -/* LIST */ -/* LAYOUT */ -/* ICON TOGGLE */ -/* FOOTER */ -/*mega-footer*/ -/*mini-footer*/ -/* CHECKBOX */ -/* CARD */ -/* Card dimensions */ -/* Cover image */ -/* BUTTON */ -/** - * - * Dimensions - * - */ -/* ANIMATION */ -/* PROGRESS */ -/* BADGE */ -/* SHADOWS */ -/* GRID */ -/* DATA TABLE */ -/* TOOLTIP */ -.mdl-mini-footer { - display: -webkit-box; - display: -webkit-flex; - display: -ms-flexbox; - display: flex; - -webkit-flex-flow: row wrap; - -ms-flex-flow: row wrap; - flex-flow: row wrap; - -webkit-box-pack: justify; - -webkit-justify-content: space-between; - -ms-flex-pack: justify; - justify-content: space-between; - padding: 32px 16px; - color: rgb(158,158,158); - background-color: rgb(66,66,66); } - .mdl-mini-footer:after { - content: ''; - display: block; } - .mdl-mini-footer .mdl-logo { - line-height: 36px; } - -.mdl-mini-footer--link-list, -.mdl-mini-footer__link-list { - display: -webkit-box; - display: -webkit-flex; - display: -ms-flexbox; - display: flex; - -webkit-flex-flow: row nowrap; - -ms-flex-flow: row nowrap; - flex-flow: row nowrap; - list-style: none; - margin: 0; - padding: 0; } - .mdl-mini-footer--link-list li, - .mdl-mini-footer__link-list li { - margin-bottom: 0; - margin-right: 16px; } - @media screen and (min-width: 760px) { - .mdl-mini-footer--link-list li, - .mdl-mini-footer__link-list li { - line-height: 36px; } } - .mdl-mini-footer--link-list a, - .mdl-mini-footer__link-list a { - color: inherit; - text-decoration: none; - white-space: nowrap; } - -.mdl-mini-footer--left-section, -.mdl-mini-footer__left-section { - display: inline-block; - -webkit-box-ordinal-group: 1; - -webkit-order: 0; - -ms-flex-order: 0; - order: 0; } - -.mdl-mini-footer--right-section, -.mdl-mini-footer__right-section { - display: inline-block; - -webkit-box-ordinal-group: 2; - -webkit-order: 1; - -ms-flex-order: 1; - order: 1; } - -.mdl-mini-footer--social-btn, -.mdl-mini-footer__social-btn { - width: 36px; - height: 36px; - padding: 0; - margin: 0; - background-color: rgb(158,158,158); - border: none; } - -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/*------------------------------------* $CONTENTS -\*------------------------------------*/ -/** - * STYLE GUIDE VARIABLES------------------Declarations of Sass variables - * -----Typography - * -----Colors - * -----Textfield - * -----Switch - * -----Spinner - * -----Radio - * -----Menu - * -----List - * -----Layout - * -----Icon toggles - * -----Footer - * -----Column - * -----Checkbox - * -----Card - * -----Button - * -----Animation - * -----Progress - * -----Badge - * -----Shadows - * -----Grid - * -----Data table - */ -/* ========== TYPOGRAPHY ========== */ -/* We're splitting fonts into "preferred" and "performance" in order to optimize - page loading. For important text, such as the body, we want it to load - immediately and not wait for the web font load, whereas for other sections, - such as headers and titles, we're OK with things taking a bit longer to load. - We do have some optional classes and parameters in the mixins, in case you - definitely want to make sure you're using the preferred font and don't mind - the performance hit. - We should be able to improve on this once CSS Font Loading L3 becomes more - widely available. -*/ -/* ========== COLORS ========== */ -/** -* -* Material design color palettes. -* @see http://www.google.com/design/spec/style/color.html -* -**/ -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/* ========== Color Palettes ========== */ -/* colors.scss */ -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/* ========== IMAGES ========== */ -/* ========== Color & Themes ========== */ -/* ========== Typography ========== */ -/* ========== Components ========== */ -/* ========== Standard Buttons ========== */ -/* ========== Icon Toggles ========== */ -/* ========== Radio Buttons ========== */ -/* ========== Ripple effect ========== */ -/* ========== Layout ========== */ -/* ========== Content Tabs ========== */ -/* ========== Checkboxes ========== */ -/* ========== Switches ========== */ -/* ========== Spinner ========== */ -/* ========== Text fields ========== */ -/* ========== Card ========== */ -/* ========== Sliders ========== */ -/* ========== Progress ========== */ -/* ========== List ========== */ -/* ========== Item ========== */ -/* ========== Dropdown menu ========== */ -/* ========== Tooltips ========== */ -/* ========== Footer ========== */ -/* TEXTFIELD */ -/* SWITCH */ -/* SPINNER */ -/* RADIO */ -/* MENU */ -/* LIST */ -/* LAYOUT */ -/* ICON TOGGLE */ -/* FOOTER */ -/*mega-footer*/ -/*mini-footer*/ -/* CHECKBOX */ -/* CARD */ -/* Card dimensions */ -/* Cover image */ -/* BUTTON */ -/** - * - * Dimensions - * - */ -/* ANIMATION */ -/* PROGRESS */ -/* BADGE */ -/* SHADOWS */ -/* GRID */ -/* DATA TABLE */ -/* TOOLTIP */ -.mdl-icon-toggle { - position: relative; - z-index: 1; - vertical-align: middle; - display: inline-block; - height: 32px; - margin: 0; - padding: 0; } - -.mdl-icon-toggle__input { - line-height: 32px; } - .mdl-icon-toggle.is-upgraded .mdl-icon-toggle__input { - position: absolute; - width: 0; - height: 0; - margin: 0; - padding: 0; - opacity: 0; - -ms-appearance: none; - -moz-appearance: none; - -webkit-appearance: none; - appearance: none; - border: none; } - -.mdl-icon-toggle__label { - display: inline-block; - position: relative; - cursor: pointer; - height: 32px; - width: 32px; - min-width: 32px; - color: rgb(97,97,97); - border-radius: 50%; - padding: 0; - margin-left: 0; - margin-right: 0; - text-align: center; - background-color: transparent; - will-change: background-color; - -webkit-transition: background-color 0.2s cubic-bezier(0.4, 0, 0.2, 1), color 0.2s cubic-bezier(0.4, 0, 0.2, 1); - transition: background-color 0.2s cubic-bezier(0.4, 0, 0.2, 1), color 0.2s cubic-bezier(0.4, 0, 0.2, 1); } - .mdl-icon-toggle__label.material-icons { - line-height: 32px; - font-size: 24px; } - .mdl-icon-toggle.is-checked .mdl-icon-toggle__label { - color: rgb(63,81,181); } - .mdl-icon-toggle.is-disabled .mdl-icon-toggle__label { - color: rgba(0,0,0, 0.26); - cursor: auto; - -webkit-transition: none; - transition: none; } - .mdl-icon-toggle.is-focused .mdl-icon-toggle__label { - background-color: rgba(0,0,0, 0.12); } - .mdl-icon-toggle.is-focused.is-checked .mdl-icon-toggle__label { - background-color: rgba(63,81,181, 0.26); } - -.mdl-icon-toggle__ripple-container { - position: absolute; - z-index: 2; - top: -2px; - left: -2px; - box-sizing: border-box; - width: 36px; - height: 36px; - border-radius: 50%; - cursor: pointer; - overflow: hidden; - -webkit-mask-image: -webkit-radial-gradient(circle, white, black); } - .mdl-icon-toggle__ripple-container .mdl-ripple { - background: rgb(97,97,97); } - .mdl-icon-toggle.is-disabled .mdl-icon-toggle__ripple-container { - cursor: auto; } - .mdl-icon-toggle.is-disabled .mdl-icon-toggle__ripple-container .mdl-ripple { - background: transparent; } - -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/*------------------------------------* $CONTENTS -\*------------------------------------*/ -/** - * STYLE GUIDE VARIABLES------------------Declarations of Sass variables - * -----Typography - * -----Colors - * -----Textfield - * -----Switch - * -----Spinner - * -----Radio - * -----Menu - * -----List - * -----Layout - * -----Icon toggles - * -----Footer - * -----Column - * -----Checkbox - * -----Card - * -----Button - * -----Animation - * -----Progress - * -----Badge - * -----Shadows - * -----Grid - * -----Data table - */ -/* ========== TYPOGRAPHY ========== */ -/* We're splitting fonts into "preferred" and "performance" in order to optimize - page loading. For important text, such as the body, we want it to load - immediately and not wait for the web font load, whereas for other sections, - such as headers and titles, we're OK with things taking a bit longer to load. - We do have some optional classes and parameters in the mixins, in case you - definitely want to make sure you're using the preferred font and don't mind - the performance hit. - We should be able to improve on this once CSS Font Loading L3 becomes more - widely available. -*/ -/* ========== COLORS ========== */ -/** -* -* Material design color palettes. -* @see http://www.google.com/design/spec/style/color.html -* -**/ -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/* ========== Color Palettes ========== */ -/* colors.scss */ -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/* ========== IMAGES ========== */ -/* ========== Color & Themes ========== */ -/* ========== Typography ========== */ -/* ========== Components ========== */ -/* ========== Standard Buttons ========== */ -/* ========== Icon Toggles ========== */ -/* ========== Radio Buttons ========== */ -/* ========== Ripple effect ========== */ -/* ========== Layout ========== */ -/* ========== Content Tabs ========== */ -/* ========== Checkboxes ========== */ -/* ========== Switches ========== */ -/* ========== Spinner ========== */ -/* ========== Text fields ========== */ -/* ========== Card ========== */ -/* ========== Sliders ========== */ -/* ========== Progress ========== */ -/* ========== List ========== */ -/* ========== Item ========== */ -/* ========== Dropdown menu ========== */ -/* ========== Tooltips ========== */ -/* ========== Footer ========== */ -/* TEXTFIELD */ -/* SWITCH */ -/* SPINNER */ -/* RADIO */ -/* MENU */ -/* LIST */ -/* LAYOUT */ -/* ICON TOGGLE */ -/* FOOTER */ -/*mega-footer*/ -/*mini-footer*/ -/* CHECKBOX */ -/* CARD */ -/* Card dimensions */ -/* Cover image */ -/* BUTTON */ -/** - * - * Dimensions - * - */ -/* ANIMATION */ -/* PROGRESS */ -/* BADGE */ -/* SHADOWS */ -/* GRID */ -/* DATA TABLE */ -/* TOOLTIP */ -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/* Typography */ -/* Shadows */ -/* Animations */ -.mdl-menu__container { - display: block; - margin: 0; - padding: 0; - border: none; - position: absolute; - overflow: visible; - height: 0; - width: 0; - visibility: hidden; - z-index: -1; } - .mdl-menu__container.is-visible, .mdl-menu__container.is-animating { - z-index: 999; - visibility: visible; } - -.mdl-menu__outline { - display: block; - background: rgb(255,255,255); - margin: 0; - padding: 0; - border: none; - border-radius: 2px; - position: absolute; - top: 0; - left: 0; - overflow: hidden; - opacity: 0; - -webkit-transform: scale(0); - -ms-transform: scale(0); - transform: scale(0); - -webkit-transform-origin: 0 0; - -ms-transform-origin: 0 0; - transform-origin: 0 0; - box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12); - will-change: transform; - -webkit-transition: -webkit-transform 0.3s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.2s cubic-bezier(0.4, 0, 0.2, 1); - transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.2s cubic-bezier(0.4, 0, 0.2, 1); - z-index: -1; } - .mdl-menu__container.is-visible .mdl-menu__outline { - opacity: 1; - -webkit-transform: scale(1); - -ms-transform: scale(1); - transform: scale(1); - z-index: 999; } - .mdl-menu__outline.mdl-menu--bottom-right { - -webkit-transform-origin: 100% 0; - -ms-transform-origin: 100% 0; - transform-origin: 100% 0; } - .mdl-menu__outline.mdl-menu--top-left { - -webkit-transform-origin: 0 100%; - -ms-transform-origin: 0 100%; - transform-origin: 0 100%; } - .mdl-menu__outline.mdl-menu--top-right { - -webkit-transform-origin: 100% 100%; - -ms-transform-origin: 100% 100%; - transform-origin: 100% 100%; } - -.mdl-menu { - position: absolute; - list-style: none; - top: 0; - left: 0; - height: auto; - width: auto; - min-width: 124px; - padding: 8px 0; - margin: 0; - opacity: 0; - clip: rect(0 0 0 0); - z-index: -1; } - .mdl-menu__container.is-visible .mdl-menu { - opacity: 1; - z-index: 999; } - .mdl-menu.is-animating { - -webkit-transition: opacity 0.2s cubic-bezier(0.4, 0, 0.2, 1), clip 0.3s cubic-bezier(0.4, 0, 0.2, 1); - transition: opacity 0.2s cubic-bezier(0.4, 0, 0.2, 1), clip 0.3s cubic-bezier(0.4, 0, 0.2, 1); } - .mdl-menu.mdl-menu--bottom-right { - left: auto; - right: 0; } - .mdl-menu.mdl-menu--top-left { - top: auto; - bottom: 0; } - .mdl-menu.mdl-menu--top-right { - top: auto; - left: auto; - bottom: 0; - right: 0; } - .mdl-menu.mdl-menu--unaligned { - top: auto; - left: auto; } - -.mdl-menu__item { - display: block; - border: none; - color: rgba(0,0,0, 0.87); - background-color: transparent; - text-align: left; - margin: 0; - padding: 0 16px; - outline-color: rgb(189,189,189); - position: relative; - overflow: hidden; - font-size: 14px; - font-weight: 400; - line-height: 24px; - letter-spacing: 0; - text-decoration: none; - cursor: pointer; - height: 48px; - line-height: 48px; - white-space: nowrap; - opacity: 0; - -webkit-transition: opacity 0.2s cubic-bezier(0.4, 0, 0.2, 1); - transition: opacity 0.2s cubic-bezier(0.4, 0, 0.2, 1); - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; } - .mdl-menu__container.is-visible .mdl-menu__item { - opacity: 1; } - .mdl-menu__item::-moz-focus-inner { - border: 0; } - .mdl-menu__item[disabled] { - color: rgb(189,189,189); - background-color: transparent; - cursor: auto; } - .mdl-menu__item[disabled]:hover { - background-color: transparent; } - .mdl-menu__item[disabled]:focus { - background-color: transparent; } - .mdl-menu__item[disabled] .mdl-ripple { - background: transparent; } - .mdl-menu__item:hover { - background-color: rgb(238,238,238); } - .mdl-menu__item:focus { - outline: none; - background-color: rgb(238,238,238); } - .mdl-menu__item:active { - background-color: rgb(224,224,224); } - -.mdl-menu__item--ripple-container { - display: block; - height: 100%; - left: 0px; - position: absolute; - top: 0px; - width: 100%; - z-index: 0; - overflow: hidden; } - -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/*------------------------------------* $CONTENTS -\*------------------------------------*/ -/** - * STYLE GUIDE VARIABLES------------------Declarations of Sass variables - * -----Typography - * -----Colors - * -----Textfield - * -----Switch - * -----Spinner - * -----Radio - * -----Menu - * -----List - * -----Layout - * -----Icon toggles - * -----Footer - * -----Column - * -----Checkbox - * -----Card - * -----Button - * -----Animation - * -----Progress - * -----Badge - * -----Shadows - * -----Grid - * -----Data table - */ -/* ========== TYPOGRAPHY ========== */ -/* We're splitting fonts into "preferred" and "performance" in order to optimize - page loading. For important text, such as the body, we want it to load - immediately and not wait for the web font load, whereas for other sections, - such as headers and titles, we're OK with things taking a bit longer to load. - We do have some optional classes and parameters in the mixins, in case you - definitely want to make sure you're using the preferred font and don't mind - the performance hit. - We should be able to improve on this once CSS Font Loading L3 becomes more - widely available. -*/ -/* ========== COLORS ========== */ -/** -* -* Material design color palettes. -* @see http://www.google.com/design/spec/style/color.html -* -**/ -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/* ========== Color Palettes ========== */ -/* colors.scss */ -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/* ========== IMAGES ========== */ -/* ========== Color & Themes ========== */ -/* ========== Typography ========== */ -/* ========== Components ========== */ -/* ========== Standard Buttons ========== */ -/* ========== Icon Toggles ========== */ -/* ========== Radio Buttons ========== */ -/* ========== Ripple effect ========== */ -/* ========== Layout ========== */ -/* ========== Content Tabs ========== */ -/* ========== Checkboxes ========== */ -/* ========== Switches ========== */ -/* ========== Spinner ========== */ -/* ========== Text fields ========== */ -/* ========== Card ========== */ -/* ========== Sliders ========== */ -/* ========== Progress ========== */ -/* ========== List ========== */ -/* ========== Item ========== */ -/* ========== Dropdown menu ========== */ -/* ========== Tooltips ========== */ -/* ========== Footer ========== */ -/* TEXTFIELD */ -/* SWITCH */ -/* SPINNER */ -/* RADIO */ -/* MENU */ -/* LIST */ -/* LAYOUT */ -/* ICON TOGGLE */ -/* FOOTER */ -/*mega-footer*/ -/*mini-footer*/ -/* CHECKBOX */ -/* CARD */ -/* Card dimensions */ -/* Cover image */ -/* BUTTON */ -/** - * - * Dimensions - * - */ -/* ANIMATION */ -/* PROGRESS */ -/* BADGE */ -/* SHADOWS */ -/* GRID */ -/* DATA TABLE */ -/* TOOLTIP */ -.mdl-progress { - display: block; - position: relative; - height: 4px; - width: 500px; } - -.mdl-progress > .bar { - display: block; - position: absolute; - top: 0; - bottom: 0; - width: 0%; - -webkit-transition: width 0.2s cubic-bezier(0.4, 0, 0.2, 1); - transition: width 0.2s cubic-bezier(0.4, 0, 0.2, 1); } - -.mdl-progress > .progressbar { - background-color: rgb(63,81,181); - z-index: 1; - left: 0; } - -.mdl-progress > .bufferbar { - background-image: -webkit-linear-gradient(left, rgba(255,255,255, 0.7), rgba(255,255,255, 0.7)), -webkit-linear-gradient(left, rgb(63,81,181), rgb(63,81,181)); - background-image: linear-gradient(to right, rgba(255,255,255, 0.7), rgba(255,255,255, 0.7)), linear-gradient(to right, rgb(63,81,181), rgb(63,81,181)); - z-index: 0; - left: 0; } - -.mdl-progress > .auxbar { - right: 0; } - -@supports (-webkit-appearance: none) { - .mdl-progress:not(.mdl-progress__indeterminate):not(.mdl-progress__indeterminate) > .auxbar { - background-image: -webkit-linear-gradient(left, rgba(255,255,255, 0.7), rgba(255,255,255, 0.7)), -webkit-linear-gradient(left, rgb(63,81,181), rgb(63,81,181)); - background-image: linear-gradient(to right, rgba(255,255,255, 0.7), rgba(255,255,255, 0.7)), linear-gradient(to right, rgb(63,81,181), rgb(63,81,181)); - -webkit-mask: url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIj8+Cjxzdmcgd2lkdGg9IjEyIiBoZWlnaHQ9IjQiIHZpZXdQb3J0PSIwIDAgMTIgNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgogIDxlbGxpcHNlIGN4PSIyIiBjeT0iMiIgcng9IjIiIHJ5PSIyIj4KICAgIDxhbmltYXRlIGF0dHJpYnV0ZU5hbWU9ImN4IiBmcm9tPSIyIiB0bz0iLTEwIiBkdXI9IjAuNnMiIHJlcGVhdENvdW50PSJpbmRlZmluaXRlIiAvPgogIDwvZWxsaXBzZT4KICA8ZWxsaXBzZSBjeD0iMTQiIGN5PSIyIiByeD0iMiIgcnk9IjIiIGNsYXNzPSJsb2FkZXIiPgogICAgPGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iY3giIGZyb209IjE0IiB0bz0iMiIgZHVyPSIwLjZzIiByZXBlYXRDb3VudD0iaW5kZWZpbml0ZSIgLz4KICA8L2VsbGlwc2U+Cjwvc3ZnPgo="); - mask: url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIj8+Cjxzdmcgd2lkdGg9IjEyIiBoZWlnaHQ9IjQiIHZpZXdQb3J0PSIwIDAgMTIgNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgogIDxlbGxpcHNlIGN4PSIyIiBjeT0iMiIgcng9IjIiIHJ5PSIyIj4KICAgIDxhbmltYXRlIGF0dHJpYnV0ZU5hbWU9ImN4IiBmcm9tPSIyIiB0bz0iLTEwIiBkdXI9IjAuNnMiIHJlcGVhdENvdW50PSJpbmRlZmluaXRlIiAvPgogIDwvZWxsaXBzZT4KICA8ZWxsaXBzZSBjeD0iMTQiIGN5PSIyIiByeD0iMiIgcnk9IjIiIGNsYXNzPSJsb2FkZXIiPgogICAgPGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iY3giIGZyb209IjE0IiB0bz0iMiIgZHVyPSIwLjZzIiByZXBlYXRDb3VudD0iaW5kZWZpbml0ZSIgLz4KICA8L2VsbGlwc2U+Cjwvc3ZnPgo="); } } - -.mdl-progress:not(.mdl-progress__indeterminate) > .auxbar { - background-image: -webkit-linear-gradient(left, rgba(255,255,255, 0.9), rgba(255,255,255, 0.9)), -webkit-linear-gradient(left, rgb(63,81,181), rgb(63,81,181)); - background-image: linear-gradient(to right, rgba(255,255,255, 0.9), rgba(255,255,255, 0.9)), linear-gradient(to right, rgb(63,81,181), rgb(63,81,181)); } - -.mdl-progress.mdl-progress__indeterminate > .bar1 { - background-color: rgb(63,81,181); - -webkit-animation-name: indeterminate1; - animation-name: indeterminate1; - -webkit-animation-duration: 2s; - animation-duration: 2s; - -webkit-animation-iteration-count: infinite; - animation-iteration-count: infinite; - -webkit-animation-timing-function: linear; - animation-timing-function: linear; } - -.mdl-progress.mdl-progress__indeterminate > .bar3 { - background-image: none; - background-color: rgb(63,81,181); - -webkit-animation-name: indeterminate2; - animation-name: indeterminate2; - -webkit-animation-duration: 2s; - animation-duration: 2s; - -webkit-animation-iteration-count: infinite; - animation-iteration-count: infinite; - -webkit-animation-timing-function: linear; - animation-timing-function: linear; } - -@-webkit-keyframes indeterminate1 { - 0% { - left: 0%; - width: 0%; } - 50% { - left: 25%; - width: 75%; } - 75% { - left: 100%; - width: 0%; } } - -@keyframes indeterminate1 { - 0% { - left: 0%; - width: 0%; } - 50% { - left: 25%; - width: 75%; } - 75% { - left: 100%; - width: 0%; } } - -@-webkit-keyframes indeterminate2 { - 0% { - left: 0%; - width: 0%; } - 50% { - left: 0%; - width: 0%; } - 75% { - left: 0%; - width: 25%; } - 100% { - left: 100%; - width: 0%; } } - -@keyframes indeterminate2 { - 0% { - left: 0%; - width: 0%; } - 50% { - left: 0%; - width: 0%; } - 75% { - left: 0%; - width: 25%; } - 100% { - left: 100%; - width: 0%; } } - -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/*------------------------------------* $CONTENTS -\*------------------------------------*/ -/** - * STYLE GUIDE VARIABLES------------------Declarations of Sass variables - * -----Typography - * -----Colors - * -----Textfield - * -----Switch - * -----Spinner - * -----Radio - * -----Menu - * -----List - * -----Layout - * -----Icon toggles - * -----Footer - * -----Column - * -----Checkbox - * -----Card - * -----Button - * -----Animation - * -----Progress - * -----Badge - * -----Shadows - * -----Grid - * -----Data table - */ -/* ========== TYPOGRAPHY ========== */ -/* We're splitting fonts into "preferred" and "performance" in order to optimize - page loading. For important text, such as the body, we want it to load - immediately and not wait for the web font load, whereas for other sections, - such as headers and titles, we're OK with things taking a bit longer to load. - We do have some optional classes and parameters in the mixins, in case you - definitely want to make sure you're using the preferred font and don't mind - the performance hit. - We should be able to improve on this once CSS Font Loading L3 becomes more - widely available. -*/ -/* ========== COLORS ========== */ -/** -* -* Material design color palettes. -* @see http://www.google.com/design/spec/style/color.html -* -**/ -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/* ========== Color Palettes ========== */ -/* colors.scss */ -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/* ========== IMAGES ========== */ -/* ========== Color & Themes ========== */ -/* ========== Typography ========== */ -/* ========== Components ========== */ -/* ========== Standard Buttons ========== */ -/* ========== Icon Toggles ========== */ -/* ========== Radio Buttons ========== */ -/* ========== Ripple effect ========== */ -/* ========== Layout ========== */ -/* ========== Content Tabs ========== */ -/* ========== Checkboxes ========== */ -/* ========== Switches ========== */ -/* ========== Spinner ========== */ -/* ========== Text fields ========== */ -/* ========== Card ========== */ -/* ========== Sliders ========== */ -/* ========== Progress ========== */ -/* ========== List ========== */ -/* ========== Item ========== */ -/* ========== Dropdown menu ========== */ -/* ========== Tooltips ========== */ -/* ========== Footer ========== */ -/* TEXTFIELD */ -/* SWITCH */ -/* SPINNER */ -/* RADIO */ -/* MENU */ -/* LIST */ -/* LAYOUT */ -/* ICON TOGGLE */ -/* FOOTER */ -/*mega-footer*/ -/*mini-footer*/ -/* CHECKBOX */ -/* CARD */ -/* Card dimensions */ -/* Cover image */ -/* BUTTON */ -/** - * - * Dimensions - * - */ -/* ANIMATION */ -/* PROGRESS */ -/* BADGE */ -/* SHADOWS */ -/* GRID */ -/* DATA TABLE */ -/* TOOLTIP */ -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/* Typography */ -/* Shadows */ -/* Animations */ -.mdl-navigation { - display: -webkit-box; - display: -webkit-flex; - display: -ms-flexbox; - display: flex; - -webkit-flex-wrap: nowrap; - -ms-flex-wrap: nowrap; - flex-wrap: nowrap; - box-sizing: border-box; } - -.mdl-navigation__link { - color: rgb(66,66,66); - text-decoration: none; - font-weight: 500; - font-size: 13px; - margin: 0; } - -.mdl-layout { - width: 100%; - height: 100%; - display: -webkit-box; - display: -webkit-flex; - display: -ms-flexbox; - display: flex; - -webkit-box-orient: vertical; - -webkit-box-direction: normal; - -webkit-flex-direction: column; - -ms-flex-direction: column; - flex-direction: column; - overflow-y: auto; - overflow-x: hidden; - position: relative; - -webkit-overflow-scrolling: touch; } - -.mdl-layout.is-small-screen .mdl-layout--large-screen-only { - display: none; } - -.mdl-layout:not(.is-small-screen) .mdl-layout--small-screen-only { - display: none; } - -.mdl-layout__container { - position: absolute; - width: 100%; - height: 100%; } - -.mdl-layout__title, -.mdl-layout-title { - display: block; - position: relative; - font-family: "Roboto", "Helvetica", "Arial", sans-serif; - font-size: 20px; - font-weight: 500; - line-height: 1; - letter-spacing: 0.02em; - font-weight: 400; - box-sizing: border-box; } - -.mdl-layout-spacer { - -webkit-box-flex: 1; - -webkit-flex-grow: 1; - -ms-flex-positive: 1; - flex-grow: 1; } - -.mdl-layout__drawer { - display: -webkit-box; - display: -webkit-flex; - display: -ms-flexbox; - display: flex; - -webkit-box-orient: vertical; - -webkit-box-direction: normal; - -webkit-flex-direction: column; - -ms-flex-direction: column; - flex-direction: column; - -webkit-flex-wrap: nowrap; - -ms-flex-wrap: nowrap; - flex-wrap: nowrap; - width: 240px; - height: 100%; - max-height: 100%; - position: absolute; - top: 0; - left: 0; - box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12); - box-sizing: border-box; - border-right: 1px solid rgb(224,224,224); - background: rgb(250,250,250); - -webkit-transform: translateX(-250px); - -ms-transform: translateX(-250px); - transform: translateX(-250px); - -webkit-transform-style: preserve-3d; - transform-style: preserve-3d; - will-change: transform; - -webkit-transition-duration: 0.2s; - transition-duration: 0.2s; - -webkit-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); - -webkit-transition-property: -webkit-transform; - transition-property: transform; - color: rgb(66,66,66); - overflow: visible; - overflow-y: auto; - z-index: 5; } - .mdl-layout__drawer.is-visible { - -webkit-transform: translateX(0); - -ms-transform: translateX(0); - transform: translateX(0); } - .mdl-layout__drawer.is-visible ~ .mdl-layout__content.mdl-layout__content { - overflow: hidden; } - .mdl-layout__drawer > * { - -webkit-flex-shrink: 0; - -ms-flex-negative: 0; - flex-shrink: 0; } - .mdl-layout__drawer > .mdl-layout__title, - .mdl-layout__drawer > .mdl-layout-title { - line-height: 64px; - padding-left: 40px; } - @media screen and (max-width: 1024px) { - .mdl-layout__drawer > .mdl-layout__title, - .mdl-layout__drawer > .mdl-layout-title { - line-height: 56px; - padding-left: 16px; } } - .mdl-layout__drawer .mdl-navigation { - -webkit-box-orient: vertical; - -webkit-box-direction: normal; - -webkit-flex-direction: column; - -ms-flex-direction: column; - flex-direction: column; - -webkit-box-align: stretch; - -webkit-align-items: stretch; - -ms-flex-align: stretch; - align-items: stretch; - padding-top: 16px; } - .mdl-layout__drawer .mdl-navigation .mdl-navigation__link { - display: block; - -webkit-flex-shrink: 0; - -ms-flex-negative: 0; - flex-shrink: 0; - padding: 16px 40px; - margin: 0; - color: #757575; } - @media screen and (max-width: 1024px) { - .mdl-layout__drawer .mdl-navigation .mdl-navigation__link { - padding: 16px 16px; } } - .mdl-layout__drawer .mdl-navigation .mdl-navigation__link:hover { - background-color: rgb(224,224,224); } - .mdl-layout__drawer .mdl-navigation .mdl-navigation__link--current { - background-color: rgb(0,0,0); - color: rgb(224,224,224); } - @media screen and (min-width: 1025px) { - .mdl-layout--fixed-drawer > .mdl-layout__drawer { - -webkit-transform: translateX(0); - -ms-transform: translateX(0); - transform: translateX(0); } } - -.mdl-layout__drawer-button { - display: block; - position: absolute; - height: 48px; - width: 48px; - border: 0; - -webkit-flex-shrink: 0; - -ms-flex-negative: 0; - flex-shrink: 0; - overflow: hidden; - text-align: center; - cursor: pointer; - font-size: 26px; - line-height: 50px; - font-family: Helvetica, Arial, sans-serif; - margin: 10px 12px; - top: 0; - left: 0; - color: rgb(255,255,255); - z-index: 4; } - .mdl-layout__header .mdl-layout__drawer-button { - position: absolute; - color: rgb(255,255,255); - background-color: inherit; } - @media screen and (max-width: 1024px) { - .mdl-layout__header .mdl-layout__drawer-button { - margin: 4px; } } - @media screen and (max-width: 1024px) { - .mdl-layout__drawer-button { - margin: 4px; - color: rgba(0, 0, 0, 0.5); } } - @media screen and (min-width: 1025px) { - .mdl-layout--fixed-drawer > .mdl-layout__drawer-button { - display: none; } } - -.mdl-layout__header { - display: -webkit-box; - display: -webkit-flex; - display: -ms-flexbox; - display: flex; - -webkit-box-orient: vertical; - -webkit-box-direction: normal; - -webkit-flex-direction: column; - -ms-flex-direction: column; - flex-direction: column; - -webkit-flex-wrap: nowrap; - -ms-flex-wrap: nowrap; - flex-wrap: nowrap; - -webkit-box-pack: start; - -webkit-justify-content: flex-start; - -ms-flex-pack: start; - justify-content: flex-start; - box-sizing: border-box; - -webkit-flex-shrink: 0; - -ms-flex-negative: 0; - flex-shrink: 0; - width: 100%; - margin: 0; - padding: 0; - border: none; - min-height: 64px; - max-height: 1000px; - z-index: 3; - background-color: rgb(63,81,181); - color: rgb(255,255,255); - box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12); - -webkit-transition-duration: 0.2s; - transition-duration: 0.2s; - -webkit-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); - -webkit-transition-property: max-height, box-shadow; - transition-property: max-height, box-shadow; } - @media screen and (max-width: 1024px) { - .mdl-layout__header { - min-height: 56px; } } - .mdl-layout--fixed-drawer.is-upgraded:not(.is-small-screen) > .mdl-layout__header { - margin-left: 240px; - width: calc(100% - 240px); } - @media screen and (min-width: 1025px) { - .mdl-layout--fixed-drawer > .mdl-layout__header .mdl-layout__header-row { - padding-left: 40px; } } - .mdl-layout__header > .mdl-layout-icon { - position: absolute; - left: 40px; - top: 16px; - height: 32px; - width: 32px; - overflow: hidden; - z-index: 3; - display: block; } - @media screen and (max-width: 1024px) { - .mdl-layout__header > .mdl-layout-icon { - left: 16px; - top: 12px; } } - .mdl-layout.has-drawer .mdl-layout__header > .mdl-layout-icon { - display: none; } - .mdl-layout__header.is-compact { - max-height: 64px; } - @media screen and (max-width: 1024px) { - .mdl-layout__header.is-compact { - max-height: 56px; } } - .mdl-layout__header.is-compact.has-tabs { - height: 112px; } - @media screen and (max-width: 1024px) { - .mdl-layout__header.is-compact.has-tabs { - min-height: 104px; } } - @media screen and (max-width: 1024px) { - .mdl-layout__header { - display: none; } - .mdl-layout--fixed-header > .mdl-layout__header { - display: -webkit-box; - display: -webkit-flex; - display: -ms-flexbox; - display: flex; } } - -.mdl-layout__header--transparent.mdl-layout__header--transparent { - background-color: transparent; - box-shadow: none; } - -.mdl-layout__header--seamed { - box-shadow: none; } - -.mdl-layout__header--scroll { - box-shadow: none; } - -.mdl-layout__header--waterfall { - box-shadow: none; - overflow: hidden; } - .mdl-layout__header--waterfall.is-casting-shadow { - box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12); } - -.mdl-layout__header-row { - display: -webkit-box; - display: -webkit-flex; - display: -ms-flexbox; - display: flex; - -webkit-box-orient: horizontal; - -webkit-box-direction: normal; - -webkit-flex-direction: row; - -ms-flex-direction: row; - flex-direction: row; - -webkit-flex-wrap: nowrap; - -ms-flex-wrap: nowrap; - flex-wrap: nowrap; - -webkit-flex-shrink: 0; - -ms-flex-negative: 0; - flex-shrink: 0; - box-sizing: border-box; - -webkit-align-self: stretch; - -ms-flex-item-align: stretch; - align-self: stretch; - -webkit-box-align: center; - -webkit-align-items: center; - -ms-flex-align: center; - align-items: center; - height: 64px; - margin: 0; - padding: 0 40px 0 80px; } - @media screen and (max-width: 1024px) { - .mdl-layout__header-row { - height: 56px; - padding: 0 16px 0 72px; } } - .mdl-layout__header-row > * { - -webkit-flex-shrink: 0; - -ms-flex-negative: 0; - flex-shrink: 0; } - .mdl-layout__header--scroll .mdl-layout__header-row { - width: 100%; } - .mdl-layout__header-row .mdl-navigation { - margin: 0; - padding: 0; - height: 64px; - -webkit-box-orient: horizontal; - -webkit-box-direction: normal; - -webkit-flex-direction: row; - -ms-flex-direction: row; - flex-direction: row; - -webkit-box-align: center; - -webkit-align-items: center; - -ms-flex-align: center; - align-items: center; } - @media screen and (max-width: 1024px) { - .mdl-layout__header-row .mdl-navigation { - height: 56px; } } - .mdl-layout__header-row .mdl-navigation__link { - display: block; - color: rgb(255,255,255); - line-height: 64px; - padding: 0 24px; } - @media screen and (max-width: 1024px) { - .mdl-layout__header-row .mdl-navigation__link { - line-height: 56px; - padding: 0 16px; } } - -.mdl-layout__obfuscator { - background-color: transparent; - position: absolute; - top: 0; - left: 0; - height: 100%; - width: 100%; - z-index: 4; - visibility: hidden; - -webkit-transition-property: background-color; - transition-property: background-color; - -webkit-transition-duration: 0.2s; - transition-duration: 0.2s; - -webkit-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); } - .mdl-layout__obfuscator.is-visible { - background-color: rgba(0, 0, 0, 0.5); - visibility: visible; } - -.mdl-layout__content { - -ms-flex: 0 1 auto; - display: inline-block; - overflow-y: auto; - overflow-x: hidden; - -webkit-box-flex: 1; - -webkit-flex-grow: 1; - -ms-flex-positive: 1; - flex-grow: 1; - z-index: 1; - -webkit-overflow-scrolling: touch; } - .mdl-layout--fixed-drawer > .mdl-layout__content { - margin-left: 240px; } - .mdl-layout__container.has-scrolling-header .mdl-layout__content { - overflow: visible; } - @media screen and (max-width: 1024px) { - .mdl-layout--fixed-drawer > .mdl-layout__content { - margin-left: 0; } - .mdl-layout__container.has-scrolling-header .mdl-layout__content { - overflow-y: auto; - overflow-x: hidden; } } - -.mdl-layout__tab-bar { - height: 96px; - margin: 0; - width: calc(100% - 112px); - padding: 0 0 0 56px; - display: -webkit-box; - display: -webkit-flex; - display: -ms-flexbox; - display: flex; - background-color: rgb(63,81,181); - overflow-y: hidden; - overflow-x: scroll; } - .mdl-layout__tab-bar::-webkit-scrollbar { - display: none; } - @media screen and (max-width: 1024px) { - .mdl-layout__tab-bar { - width: calc(100% - 60px); - padding: 0 0 0 60px; } } - .mdl-layout--fixed-tabs .mdl-layout__tab-bar { - padding: 0; - overflow: hidden; - width: 100%; } - -.mdl-layout__tab-bar-container { - position: relative; - height: 48px; - width: 100%; - border: none; - margin: 0; - z-index: 2; - -webkit-box-flex: 0; - -webkit-flex-grow: 0; - -ms-flex-positive: 0; - flex-grow: 0; - -webkit-flex-shrink: 0; - -ms-flex-negative: 0; - flex-shrink: 0; - overflow: hidden; } - .mdl-layout__container > .mdl-layout__tab-bar-container { - position: absolute; - top: 0; - left: 0; } - -.mdl-layout__tab-bar-button { - display: inline-block; - position: absolute; - top: 0; - height: 48px; - width: 56px; - z-index: 4; - text-align: center; - background-color: rgb(63,81,181); - color: transparent; - cursor: pointer; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; } - @media screen and (max-width: 1024px) { - .mdl-layout__tab-bar-button { - display: none; - width: 60px; } } - .mdl-layout--fixed-tabs .mdl-layout__tab-bar-button { - display: none; } - .mdl-layout__tab-bar-button .material-icons { - line-height: 48px; } - .mdl-layout__tab-bar-button.is-active { - color: rgb(255,255,255); } - -.mdl-layout__tab-bar-left-button { - left: 0; } - -.mdl-layout__tab-bar-right-button { - right: 0; } - -.mdl-layout__tab { - margin: 0; - border: none; - padding: 0 24px 0 24px; - float: left; - position: relative; - display: block; - -webkit-box-flex: 0; - -webkit-flex-grow: 0; - -ms-flex-positive: 0; - flex-grow: 0; - -webkit-flex-shrink: 0; - -ms-flex-negative: 0; - flex-shrink: 0; - text-decoration: none; - height: 48px; - line-height: 48px; - text-align: center; - font-weight: 500; - font-size: 14px; - text-transform: uppercase; - color: rgba(255,255,255, 0.6); - overflow: hidden; } - @media screen and (max-width: 1024px) { - .mdl-layout__tab { - padding: 0 12px 0 12px; } } - .mdl-layout--fixed-tabs .mdl-layout__tab { - float: none; - -webkit-box-flex: 1; - -webkit-flex-grow: 1; - -ms-flex-positive: 1; - flex-grow: 1; - padding: 0; } - .mdl-layout.is-upgraded .mdl-layout__tab.is-active { - color: rgb(255,255,255); } - .mdl-layout.is-upgraded .mdl-layout__tab.is-active::after { - height: 2px; - width: 100%; - display: block; - content: " "; - bottom: 0; - left: 0; - position: absolute; - background: rgb(255,64,129); - -webkit-animation: border-expand 0.2s cubic-bezier(0.4, 0, 0.4, 1) 0.01s alternate forwards; - animation: border-expand 0.2s cubic-bezier(0.4, 0, 0.4, 1) 0.01s alternate forwards; - -webkit-transition: all 1s cubic-bezier(0.4, 0, 1, 1); - transition: all 1s cubic-bezier(0.4, 0, 1, 1); } - .mdl-layout__tab .mdl-layout__tab-ripple-container { - display: block; - position: absolute; - height: 100%; - width: 100%; - left: 0; - top: 0; - z-index: 1; - overflow: hidden; } - .mdl-layout__tab .mdl-layout__tab-ripple-container .mdl-ripple { - background-color: rgb(255,255,255); } - -.mdl-layout__tab-panel { - display: block; } - .mdl-layout.is-upgraded .mdl-layout__tab-panel { - display: none; } - .mdl-layout.is-upgraded .mdl-layout__tab-panel.is-active { - display: block; } - -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/*------------------------------------* $CONTENTS -\*------------------------------------*/ -/** - * STYLE GUIDE VARIABLES------------------Declarations of Sass variables - * -----Typography - * -----Colors - * -----Textfield - * -----Switch - * -----Spinner - * -----Radio - * -----Menu - * -----List - * -----Layout - * -----Icon toggles - * -----Footer - * -----Column - * -----Checkbox - * -----Card - * -----Button - * -----Animation - * -----Progress - * -----Badge - * -----Shadows - * -----Grid - * -----Data table - */ -/* ========== TYPOGRAPHY ========== */ -/* We're splitting fonts into "preferred" and "performance" in order to optimize - page loading. For important text, such as the body, we want it to load - immediately and not wait for the web font load, whereas for other sections, - such as headers and titles, we're OK with things taking a bit longer to load. - We do have some optional classes and parameters in the mixins, in case you - definitely want to make sure you're using the preferred font and don't mind - the performance hit. - We should be able to improve on this once CSS Font Loading L3 becomes more - widely available. -*/ -/* ========== COLORS ========== */ -/** -* -* Material design color palettes. -* @see http://www.google.com/design/spec/style/color.html -* -**/ -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/* ========== Color Palettes ========== */ -/* colors.scss */ -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/* ========== IMAGES ========== */ -/* ========== Color & Themes ========== */ -/* ========== Typography ========== */ -/* ========== Components ========== */ -/* ========== Standard Buttons ========== */ -/* ========== Icon Toggles ========== */ -/* ========== Radio Buttons ========== */ -/* ========== Ripple effect ========== */ -/* ========== Layout ========== */ -/* ========== Content Tabs ========== */ -/* ========== Checkboxes ========== */ -/* ========== Switches ========== */ -/* ========== Spinner ========== */ -/* ========== Text fields ========== */ -/* ========== Card ========== */ -/* ========== Sliders ========== */ -/* ========== Progress ========== */ -/* ========== List ========== */ -/* ========== Item ========== */ -/* ========== Dropdown menu ========== */ -/* ========== Tooltips ========== */ -/* ========== Footer ========== */ -/* TEXTFIELD */ -/* SWITCH */ -/* SPINNER */ -/* RADIO */ -/* MENU */ -/* LIST */ -/* LAYOUT */ -/* ICON TOGGLE */ -/* FOOTER */ -/*mega-footer*/ -/*mini-footer*/ -/* CHECKBOX */ -/* CARD */ -/* Card dimensions */ -/* Cover image */ -/* BUTTON */ -/** - * - * Dimensions - * - */ -/* ANIMATION */ -/* PROGRESS */ -/* BADGE */ -/* SHADOWS */ -/* GRID */ -/* DATA TABLE */ -/* TOOLTIP */ -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/* Typography */ -/* Shadows */ -/* Animations */ -.mdl-radio { - position: relative; - font-size: 16px; - line-height: 24px; - display: inline-block; - box-sizing: border-box; - margin: 0; - padding-left: 0; } - .mdl-radio.is-upgraded { - padding-left: 24px; } - -.mdl-radio__button { - line-height: 24px; } - .mdl-radio.is-upgraded .mdl-radio__button { - position: absolute; - width: 0; - height: 0; - margin: 0; - padding: 0; - opacity: 0; - -ms-appearance: none; - -moz-appearance: none; - -webkit-appearance: none; - appearance: none; - border: none; } - -.mdl-radio__outer-circle { - position: absolute; - top: 4px; - left: 0; - display: inline-block; - box-sizing: border-box; - width: 16px; - height: 16px; - margin: 0; - cursor: pointer; - border: 2px solid rgba(0,0,0, 0.54); - border-radius: 50%; - z-index: 2; } - .mdl-radio.is-checked .mdl-radio__outer-circle { - border: 2px solid rgb(63,81,181); } - .mdl-radio.is-disabled .mdl-radio__outer-circle { - border: 2px solid rgba(0,0,0, 0.26); - cursor: auto; } - -.mdl-radio__inner-circle { - position: absolute; - z-index: 1; - margin: 0; - top: 8px; - left: 4px; - box-sizing: border-box; - width: 8px; - height: 8px; - cursor: pointer; - -webkit-transition-duration: 0.28s; - transition-duration: 0.28s; - -webkit-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); - -webkit-transition-property: -webkit-transform; - transition-property: transform; - -webkit-transform: scale3d(0, 0, 0); - transform: scale3d(0, 0, 0); - border-radius: 50%; - background: rgb(63,81,181); } - .mdl-radio.is-checked .mdl-radio__inner-circle { - -webkit-transform: scale3d(1, 1, 1); - transform: scale3d(1, 1, 1); } - .mdl-radio.is-disabled .mdl-radio__inner-circle { - background: rgba(0,0,0, 0.26); - cursor: auto; } - .mdl-radio.is-focused .mdl-radio__inner-circle { - box-shadow: 0 0 0px 10px rgba(0, 0, 0, 0.1); } - -.mdl-radio__label { - cursor: pointer; } - .mdl-radio.is-disabled .mdl-radio__label { - color: rgba(0,0,0, 0.26); - cursor: auto; } - -.mdl-radio__ripple-container { - position: absolute; - z-index: 2; - top: -9px; - left: -13px; - box-sizing: border-box; - width: 42px; - height: 42px; - border-radius: 50%; - cursor: pointer; - overflow: hidden; - -webkit-mask-image: -webkit-radial-gradient(circle, white, black); } - .mdl-radio__ripple-container .mdl-ripple { - background: rgb(63,81,181); } - .mdl-radio.is-disabled .mdl-radio__ripple-container { - cursor: auto; } - .mdl-radio.is-disabled .mdl-radio__ripple-container .mdl-ripple { - background: transparent; } - -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/*------------------------------------* $CONTENTS -\*------------------------------------*/ -/** - * STYLE GUIDE VARIABLES------------------Declarations of Sass variables - * -----Typography - * -----Colors - * -----Textfield - * -----Switch - * -----Spinner - * -----Radio - * -----Menu - * -----List - * -----Layout - * -----Icon toggles - * -----Footer - * -----Column - * -----Checkbox - * -----Card - * -----Button - * -----Animation - * -----Progress - * -----Badge - * -----Shadows - * -----Grid - * -----Data table - */ -/* ========== TYPOGRAPHY ========== */ -/* We're splitting fonts into "preferred" and "performance" in order to optimize - page loading. For important text, such as the body, we want it to load - immediately and not wait for the web font load, whereas for other sections, - such as headers and titles, we're OK with things taking a bit longer to load. - We do have some optional classes and parameters in the mixins, in case you - definitely want to make sure you're using the preferred font and don't mind - the performance hit. - We should be able to improve on this once CSS Font Loading L3 becomes more - widely available. -*/ -/* ========== COLORS ========== */ -/** -* -* Material design color palettes. -* @see http://www.google.com/design/spec/style/color.html -* -**/ -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/* ========== Color Palettes ========== */ -/* colors.scss */ -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/* ========== IMAGES ========== */ -/* ========== Color & Themes ========== */ -/* ========== Typography ========== */ -/* ========== Components ========== */ -/* ========== Standard Buttons ========== */ -/* ========== Icon Toggles ========== */ -/* ========== Radio Buttons ========== */ -/* ========== Ripple effect ========== */ -/* ========== Layout ========== */ -/* ========== Content Tabs ========== */ -/* ========== Checkboxes ========== */ -/* ========== Switches ========== */ -/* ========== Spinner ========== */ -/* ========== Text fields ========== */ -/* ========== Card ========== */ -/* ========== Sliders ========== */ -/* ========== Progress ========== */ -/* ========== List ========== */ -/* ========== Item ========== */ -/* ========== Dropdown menu ========== */ -/* ========== Tooltips ========== */ -/* ========== Footer ========== */ -/* TEXTFIELD */ -/* SWITCH */ -/* SPINNER */ -/* RADIO */ -/* MENU */ -/* LIST */ -/* LAYOUT */ -/* ICON TOGGLE */ -/* FOOTER */ -/*mega-footer*/ -/*mini-footer*/ -/* CHECKBOX */ -/* CARD */ -/* Card dimensions */ -/* Cover image */ -/* BUTTON */ -/** - * - * Dimensions - * - */ -/* ANIMATION */ -/* PROGRESS */ -/* BADGE */ -/* SHADOWS */ -/* GRID */ -/* DATA TABLE */ -/* TOOLTIP */ -_:-ms-input-placeholder, :root .mdl-slider.mdl-slider.is-upgraded { - -ms-appearance: none; - height: 32px; - margin: 0; } - -.mdl-slider { - width: calc(100% - 40px); - margin: 0 20px; } - .mdl-slider.is-upgraded { - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; - height: 2px; - background: transparent; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; - outline: 0; - padding: 0; - color: rgb(63,81,181); - -webkit-align-self: center; - -ms-flex-item-align: center; - align-self: center; - z-index: 1; - cursor: pointer; - /**************************** Tracks ****************************/ - /**************************** Thumbs ****************************/ - /**************************** 0-value ****************************/ - /**************************** Disabled ****************************/ } - .mdl-slider.is-upgraded::-moz-focus-outer { - border: 0; } - .mdl-slider.is-upgraded::-ms-tooltip { - display: none; } - .mdl-slider.is-upgraded::-webkit-slider-runnable-track { - background: transparent; } - .mdl-slider.is-upgraded::-moz-range-track { - background: transparent; - border: none; } - .mdl-slider.is-upgraded::-ms-track { - background: none; - color: transparent; - height: 2px; - width: 100%; - border: none; } - .mdl-slider.is-upgraded::-ms-fill-lower { - padding: 0; - background: linear-gradient(to right, transparent, transparent 16px, rgb(63,81,181) 16px, rgb(63,81,181) 0); } - .mdl-slider.is-upgraded::-ms-fill-upper { - padding: 0; - background: linear-gradient(to left, transparent, transparent 16px, rgba(0,0,0, 0.26) 16px, rgba(0,0,0, 0.26) 0); } - .mdl-slider.is-upgraded::-webkit-slider-thumb { - -webkit-appearance: none; - width: 12px; - height: 12px; - box-sizing: border-box; - border-radius: 50%; - background: rgb(63,81,181); - border: none; - -webkit-transition: -webkit-transform 0.18s cubic-bezier(0.4, 0, 0.2, 1), border 0.18s cubic-bezier(0.4, 0, 0.2, 1), box-shadow 0.18s cubic-bezier(0.4, 0, 0.2, 1), background 0.28s cubic-bezier(0.4, 0, 0.2, 1); - transition: transform 0.18s cubic-bezier(0.4, 0, 0.2, 1), border 0.18s cubic-bezier(0.4, 0, 0.2, 1), box-shadow 0.18s cubic-bezier(0.4, 0, 0.2, 1), background 0.28s cubic-bezier(0.4, 0, 0.2, 1); } - .mdl-slider.is-upgraded::-moz-range-thumb { - -moz-appearance: none; - width: 12px; - height: 12px; - box-sizing: border-box; - border-radius: 50%; - background-image: none; - background: rgb(63,81,181); - border: none; } - .mdl-slider.is-upgraded:focus:not(:active)::-webkit-slider-thumb { - box-shadow: 0 0 0 10px rgba(63,81,181, 0.26); } - .mdl-slider.is-upgraded:focus:not(:active)::-moz-range-thumb { - box-shadow: 0 0 0 10px rgba(63,81,181, 0.26); } - .mdl-slider.is-upgraded:active::-webkit-slider-thumb { - background-image: none; - background: rgb(63,81,181); - -webkit-transform: scale(1.5); - transform: scale(1.5); } - .mdl-slider.is-upgraded:active::-moz-range-thumb { - background-image: none; - background: rgb(63,81,181); - transform: scale(1.5); } - .mdl-slider.is-upgraded::-ms-thumb { - width: 32px; - height: 32px; - border: none; - border-radius: 50%; - background: rgb(63,81,181); - -ms-transform: scale(0.375); - transform: scale(0.375); - transition: transform 0.18s cubic-bezier(0.4, 0, 0.2, 1), background 0.28s cubic-bezier(0.4, 0, 0.2, 1); } - .mdl-slider.is-upgraded:focus:not(:active)::-ms-thumb { - background: radial-gradient(circle closest-side, rgb(63,81,181) 0%, rgb(63,81,181) 37.5%, rgba(63,81,181, 0.26) 37.5%, rgba(63,81,181, 0.26) 100%); - -ms-transform: scale(1); - transform: scale(1); } - .mdl-slider.is-upgraded:active::-ms-thumb { - background: rgb(63,81,181); - -ms-transform: scale(0.5625); - transform: scale(0.5625); } - .mdl-slider.is-upgraded.is-lowest-value::-webkit-slider-thumb { - border: 2px solid rgba(0,0,0, 0.26); - background: transparent; } - .mdl-slider.is-upgraded.is-lowest-value::-moz-range-thumb { - border: 2px solid rgba(0,0,0, 0.26); - background: transparent; } - .mdl-slider.is-upgraded.is-lowest-value + -.mdl-slider__background-flex > .mdl-slider__background-upper { - left: 6px; } - .mdl-slider.is-upgraded.is-lowest-value:focus:not(:active)::-webkit-slider-thumb { - box-shadow: 0 0 0 10px rgba(0,0,0, 0.12); - background: rgba(0,0,0, 0.12); } - .mdl-slider.is-upgraded.is-lowest-value:focus:not(:active)::-moz-range-thumb { - box-shadow: 0 0 0 10px rgba(0,0,0, 0.12); - background: rgba(0,0,0, 0.12); } - .mdl-slider.is-upgraded.is-lowest-value:active::-webkit-slider-thumb { - border: 1.6px solid rgba(0,0,0, 0.26); - -webkit-transform: scale(1.5); - transform: scale(1.5); } - .mdl-slider.is-upgraded.is-lowest-value:active + -.mdl-slider__background-flex > .mdl-slider__background-upper { - left: 9px; } - .mdl-slider.is-upgraded.is-lowest-value:active::-moz-range-thumb { - border: 1.5px solid rgba(0,0,0, 0.26); - transform: scale(1.5); } - .mdl-slider.is-upgraded.is-lowest-value::-ms-thumb { - background: radial-gradient(circle closest-side, transparent 0%, transparent 66.67%, rgba(0,0,0, 0.26) 66.67%, rgba(0,0,0, 0.26) 100%); } - .mdl-slider.is-upgraded.is-lowest-value:focus:not(:active)::-ms-thumb { - background: radial-gradient(circle closest-side, rgba(0,0,0, 0.12) 0%, rgba(0,0,0, 0.12) 25%, rgba(0,0,0, 0.26) 25%, rgba(0,0,0, 0.26) 37.5%, rgba(0,0,0, 0.12) 37.5%, rgba(0,0,0, 0.12) 100%); - -ms-transform: scale(1); - transform: scale(1); } - .mdl-slider.is-upgraded.is-lowest-value:active::-ms-thumb { - -ms-transform: scale(0.5625); - transform: scale(0.5625); - background: radial-gradient(circle closest-side, transparent 0%, transparent 77.78%, rgba(0,0,0, 0.26) 77.78%, rgba(0,0,0, 0.26) 100%); } - .mdl-slider.is-upgraded.is-lowest-value::-ms-fill-lower { - background: transparent; } - .mdl-slider.is-upgraded.is-lowest-value::-ms-fill-upper { - margin-left: 6px; } - .mdl-slider.is-upgraded.is-lowest-value:active::-ms-fill-upper { - margin-left: 9px; } - .mdl-slider.is-upgraded:disabled:focus::-webkit-slider-thumb, .mdl-slider.is-upgraded:disabled:active::-webkit-slider-thumb, .mdl-slider.is-upgraded:disabled::-webkit-slider-thumb { - -webkit-transform: scale(0.667); - transform: scale(0.667); - background: rgba(0,0,0, 0.26); } - .mdl-slider.is-upgraded:disabled:focus::-moz-range-thumb, .mdl-slider.is-upgraded:disabled:active::-moz-range-thumb, .mdl-slider.is-upgraded:disabled::-moz-range-thumb { - transform: scale(0.667); - background: rgba(0,0,0, 0.26); } - .mdl-slider.is-upgraded:disabled + -.mdl-slider__background-flex > .mdl-slider__background-lower { - background-color: rgba(0,0,0, 0.26); - left: -6px; } - .mdl-slider.is-upgraded:disabled + -.mdl-slider__background-flex > .mdl-slider__background-upper { - left: 6px; } - .mdl-slider.is-upgraded.is-lowest-value:disabled:focus::-webkit-slider-thumb, .mdl-slider.is-upgraded.is-lowest-value:disabled:active::-webkit-slider-thumb, .mdl-slider.is-upgraded.is-lowest-value:disabled::-webkit-slider-thumb { - border: 3px solid rgba(0,0,0, 0.26); - background: transparent; - -webkit-transform: scale(0.667); - transform: scale(0.667); } - .mdl-slider.is-upgraded.is-lowest-value:disabled:focus::-moz-range-thumb, .mdl-slider.is-upgraded.is-lowest-value:disabled:active::-moz-range-thumb, .mdl-slider.is-upgraded.is-lowest-value:disabled::-moz-range-thumb { - border: 3px solid rgba(0,0,0, 0.26); - background: transparent; - transform: scale(0.667); } - .mdl-slider.is-upgraded.is-lowest-value:disabled:active + -.mdl-slider__background-flex > .mdl-slider__background-upper { - left: 6px; } - .mdl-slider.is-upgraded:disabled:focus::-ms-thumb, .mdl-slider.is-upgraded:disabled:active::-ms-thumb, .mdl-slider.is-upgraded:disabled::-ms-thumb { - -ms-transform: scale(0.25); - transform: scale(0.25); - background: rgba(0,0,0, 0.26); } - .mdl-slider.is-upgraded.is-lowest-value:disabled:focus::-ms-thumb, .mdl-slider.is-upgraded.is-lowest-value:disabled:active::-ms-thumb, .mdl-slider.is-upgraded.is-lowest-value:disabled::-ms-thumb { - -ms-transform: scale(0.25); - transform: scale(0.25); - background: radial-gradient(circle closest-side, transparent 0%, transparent 50%, rgba(0,0,0, 0.26) 50%, rgba(0,0,0, 0.26) 100%); } - .mdl-slider.is-upgraded:disabled::-ms-fill-lower { - margin-right: 6px; - background: linear-gradient(to right, transparent, transparent 25px, rgba(0,0,0, 0.26) 25px, rgba(0,0,0, 0.26) 0); } - .mdl-slider.is-upgraded:disabled::-ms-fill-upper { - margin-left: 6px; } - .mdl-slider.is-upgraded.is-lowest-value:disabled:active::-ms-fill-upper { - margin-left: 6px; } - -.mdl-slider__ie-container { - height: 18px; - overflow: visible; - border: none; - margin: none; - padding: none; } - -.mdl-slider__container { - height: 18px; - position: relative; - background: none; - display: -webkit-box; - display: -webkit-flex; - display: -ms-flexbox; - display: flex; - -webkit-box-orient: horizontal; - -webkit-box-direction: normal; - -webkit-flex-direction: row; - -ms-flex-direction: row; - flex-direction: row; } - -.mdl-slider__background-flex { - background: transparent; - position: absolute; - height: 2px; - width: calc(100% - 52px); - top: 50%; - left: 0; - margin: 0 26px; - display: -webkit-box; - display: -webkit-flex; - display: -ms-flexbox; - display: flex; - overflow: hidden; - border: 0; - padding: 0; - -webkit-transform: translate(0, -1px); - -ms-transform: translate(0, -1px); - transform: translate(0, -1px); } - -.mdl-slider__background-lower { - background: rgb(63,81,181); - -webkit-box-flex: 0; - -webkit-flex: 0; - -ms-flex: 0; - flex: 0; - position: relative; - border: 0; - padding: 0; } - -.mdl-slider__background-upper { - background: rgba(0,0,0, 0.26); - -webkit-box-flex: 0; - -webkit-flex: 0; - -ms-flex: 0; - flex: 0; - position: relative; - border: 0; - padding: 0; - -webkit-transition: left 0.18s cubic-bezier(0.4, 0, 0.2, 1); - transition: left 0.18s cubic-bezier(0.4, 0, 0.2, 1); } - -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/*------------------------------------* $CONTENTS -\*------------------------------------*/ -/** - * STYLE GUIDE VARIABLES------------------Declarations of Sass variables - * -----Typography - * -----Colors - * -----Textfield - * -----Switch - * -----Spinner - * -----Radio - * -----Menu - * -----List - * -----Layout - * -----Icon toggles - * -----Footer - * -----Column - * -----Checkbox - * -----Card - * -----Button - * -----Animation - * -----Progress - * -----Badge - * -----Shadows - * -----Grid - * -----Data table - */ -/* ========== TYPOGRAPHY ========== */ -/* We're splitting fonts into "preferred" and "performance" in order to optimize - page loading. For important text, such as the body, we want it to load - immediately and not wait for the web font load, whereas for other sections, - such as headers and titles, we're OK with things taking a bit longer to load. - We do have some optional classes and parameters in the mixins, in case you - definitely want to make sure you're using the preferred font and don't mind - the performance hit. - We should be able to improve on this once CSS Font Loading L3 becomes more - widely available. -*/ -/* ========== COLORS ========== */ -/** -* -* Material design color palettes. -* @see http://www.google.com/design/spec/style/color.html -* -**/ -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/* ========== Color Palettes ========== */ -/* colors.scss */ -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/* ========== IMAGES ========== */ -/* ========== Color & Themes ========== */ -/* ========== Typography ========== */ -/* ========== Components ========== */ -/* ========== Standard Buttons ========== */ -/* ========== Icon Toggles ========== */ -/* ========== Radio Buttons ========== */ -/* ========== Ripple effect ========== */ -/* ========== Layout ========== */ -/* ========== Content Tabs ========== */ -/* ========== Checkboxes ========== */ -/* ========== Switches ========== */ -/* ========== Spinner ========== */ -/* ========== Text fields ========== */ -/* ========== Card ========== */ -/* ========== Sliders ========== */ -/* ========== Progress ========== */ -/* ========== List ========== */ -/* ========== Item ========== */ -/* ========== Dropdown menu ========== */ -/* ========== Tooltips ========== */ -/* ========== Footer ========== */ -/* TEXTFIELD */ -/* SWITCH */ -/* SPINNER */ -/* RADIO */ -/* MENU */ -/* LIST */ -/* LAYOUT */ -/* ICON TOGGLE */ -/* FOOTER */ -/*mega-footer*/ -/*mini-footer*/ -/* CHECKBOX */ -/* CARD */ -/* Card dimensions */ -/* Cover image */ -/* BUTTON */ -/** - * - * Dimensions - * - */ -/* ANIMATION */ -/* PROGRESS */ -/* BADGE */ -/* SHADOWS */ -/* GRID */ -/* DATA TABLE */ -/* TOOLTIP */ -.mdl-spinner { - display: inline-block; - position: relative; - width: 28px; - height: 28px; } - .mdl-spinner:not(.is-upgraded).is-active:after { - content: "Loading..."; } - .mdl-spinner.is-upgraded.is-active { - -webkit-animation: mdl-spinner__container-rotate 1568.23529412ms linear infinite; - animation: mdl-spinner__container-rotate 1568.23529412ms linear infinite; } - -@-webkit-keyframes mdl-spinner__container-rotate { - to { - -webkit-transform: rotate(360deg); - transform: rotate(360deg); } } - -@keyframes mdl-spinner__container-rotate { - to { - -webkit-transform: rotate(360deg); - transform: rotate(360deg); } } - -.mdl-spinner__layer { - position: absolute; - width: 100%; - height: 100%; - opacity: 0; } - -.mdl-spinner__layer-1 { - border-color: rgb(66,165,245); } - .mdl-spinner--single-color .mdl-spinner__layer-1 { - border-color: rgb(63,81,181); } - .mdl-spinner.is-active .mdl-spinner__layer-1 { - -webkit-animation: mdl-spinner__fill-unfill-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both, mdl-spinner__layer-1-fade-in-out 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both; - animation: mdl-spinner__fill-unfill-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both, mdl-spinner__layer-1-fade-in-out 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both; } - -.mdl-spinner__layer-2 { - border-color: rgb(244,67,54); } - .mdl-spinner--single-color .mdl-spinner__layer-2 { - border-color: rgb(63,81,181); } - .mdl-spinner.is-active .mdl-spinner__layer-2 { - -webkit-animation: mdl-spinner__fill-unfill-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both, mdl-spinner__layer-2-fade-in-out 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both; - animation: mdl-spinner__fill-unfill-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both, mdl-spinner__layer-2-fade-in-out 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both; } - -.mdl-spinner__layer-3 { - border-color: rgb(253,216,53); } - .mdl-spinner--single-color .mdl-spinner__layer-3 { - border-color: rgb(63,81,181); } - .mdl-spinner.is-active .mdl-spinner__layer-3 { - -webkit-animation: mdl-spinner__fill-unfill-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both, mdl-spinner__layer-3-fade-in-out 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both; - animation: mdl-spinner__fill-unfill-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both, mdl-spinner__layer-3-fade-in-out 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both; } - -.mdl-spinner__layer-4 { - border-color: rgb(76,175,80); } - .mdl-spinner--single-color .mdl-spinner__layer-4 { - border-color: rgb(63,81,181); } - .mdl-spinner.is-active .mdl-spinner__layer-4 { - -webkit-animation: mdl-spinner__fill-unfill-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both, mdl-spinner__layer-4-fade-in-out 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both; - animation: mdl-spinner__fill-unfill-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both, mdl-spinner__layer-4-fade-in-out 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both; } - -@-webkit-keyframes mdl-spinner__fill-unfill-rotate { - 12.5% { - -webkit-transform: rotate(135deg); - transform: rotate(135deg); } - 25% { - -webkit-transform: rotate(270deg); - transform: rotate(270deg); } - 37.5% { - -webkit-transform: rotate(405deg); - transform: rotate(405deg); } - 50% { - -webkit-transform: rotate(540deg); - transform: rotate(540deg); } - 62.5% { - -webkit-transform: rotate(675deg); - transform: rotate(675deg); } - 75% { - -webkit-transform: rotate(810deg); - transform: rotate(810deg); } - 87.5% { - -webkit-transform: rotate(945deg); - transform: rotate(945deg); } - to { - -webkit-transform: rotate(1080deg); - transform: rotate(1080deg); } } - -@keyframes mdl-spinner__fill-unfill-rotate { - 12.5% { - -webkit-transform: rotate(135deg); - transform: rotate(135deg); } - 25% { - -webkit-transform: rotate(270deg); - transform: rotate(270deg); } - 37.5% { - -webkit-transform: rotate(405deg); - transform: rotate(405deg); } - 50% { - -webkit-transform: rotate(540deg); - transform: rotate(540deg); } - 62.5% { - -webkit-transform: rotate(675deg); - transform: rotate(675deg); } - 75% { - -webkit-transform: rotate(810deg); - transform: rotate(810deg); } - 87.5% { - -webkit-transform: rotate(945deg); - transform: rotate(945deg); } - to { - -webkit-transform: rotate(1080deg); - transform: rotate(1080deg); } } - -/** -* HACK: Even though the intention is to have the current .mdl-spinner__layer-N -* at `opacity: 1`, we set it to `opacity: 0.99` instead since this forces Chrome -* to do proper subpixel rendering for the elements being animated. This is -* especially visible in Chrome 39 on Ubuntu 14.04. See: -* -* - https://github.com/Polymer/paper-spinner/issues/9 -* - https://code.google.com/p/chromium/issues/detail?id=436255 -*/ -@-webkit-keyframes mdl-spinner__layer-1-fade-in-out { - from { - opacity: 0.99; } - 25% { - opacity: 0.99; } - 26% { - opacity: 0; } - 89% { - opacity: 0; } - 90% { - opacity: 0.99; } - 100% { - opacity: 0.99; } } -@keyframes mdl-spinner__layer-1-fade-in-out { - from { - opacity: 0.99; } - 25% { - opacity: 0.99; } - 26% { - opacity: 0; } - 89% { - opacity: 0; } - 90% { - opacity: 0.99; } - 100% { - opacity: 0.99; } } - -@-webkit-keyframes mdl-spinner__layer-2-fade-in-out { - from { - opacity: 0; } - 15% { - opacity: 0; } - 25% { - opacity: 0.99; } - 50% { - opacity: 0.99; } - 51% { - opacity: 0; } } - -@keyframes mdl-spinner__layer-2-fade-in-out { - from { - opacity: 0; } - 15% { - opacity: 0; } - 25% { - opacity: 0.99; } - 50% { - opacity: 0.99; } - 51% { - opacity: 0; } } - -@-webkit-keyframes mdl-spinner__layer-3-fade-in-out { - from { - opacity: 0; } - 40% { - opacity: 0; } - 50% { - opacity: 0.99; } - 75% { - opacity: 0.99; } - 76% { - opacity: 0; } } - -@keyframes mdl-spinner__layer-3-fade-in-out { - from { - opacity: 0; } - 40% { - opacity: 0; } - 50% { - opacity: 0.99; } - 75% { - opacity: 0.99; } - 76% { - opacity: 0; } } - -@-webkit-keyframes mdl-spinner__layer-4-fade-in-out { - from { - opacity: 0; } - 65% { - opacity: 0; } - 75% { - opacity: 0.99; } - 90% { - opacity: 0.99; } - 100% { - opacity: 0; } } - -@keyframes mdl-spinner__layer-4-fade-in-out { - from { - opacity: 0; } - 65% { - opacity: 0; } - 75% { - opacity: 0.99; } - 90% { - opacity: 0.99; } - 100% { - opacity: 0; } } - -/** -* Patch the gap that appear between the two adjacent -* div.mdl-spinner__circle-clipper while the spinner is rotating -* (appears on Chrome 38, Safari 7.1, and IE 11). -* -* Update: the gap no longer appears on Chrome when .mdl-spinner__layer-N's -* opacity is 0.99, but still does on Safari and IE. -*/ -.mdl-spinner__gap-patch { - position: absolute; - box-sizing: border-box; - top: 0; - left: 45%; - width: 10%; - height: 100%; - overflow: hidden; - border-color: inherit; } - .mdl-spinner__gap-patch .mdl-spinner__circle { - width: 1000%; - left: -450%; } - -.mdl-spinner__circle-clipper { - display: inline-block; - position: relative; - width: 50%; - height: 100%; - overflow: hidden; - border-color: inherit; } - .mdl-spinner__circle-clipper .mdl-spinner__circle { - width: 200%; } - -.mdl-spinner__circle { - box-sizing: border-box; - height: 100%; - border-width: 3px; - border-style: solid; - border-color: inherit; - border-bottom-color: transparent !important; - border-radius: 50%; - -webkit-animation: none; - animation: none; - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; } - .mdl-spinner__left .mdl-spinner__circle { - border-right-color: transparent !important; - -webkit-transform: rotate(129deg); - -ms-transform: rotate(129deg); - transform: rotate(129deg); } - .mdl-spinner.is-active .mdl-spinner__left .mdl-spinner__circle { - -webkit-animation: mdl-spinner__left-spin 1333ms cubic-bezier(0.4, 0, 0.2, 1) infinite both; - animation: mdl-spinner__left-spin 1333ms cubic-bezier(0.4, 0, 0.2, 1) infinite both; } - .mdl-spinner__right .mdl-spinner__circle { - left: -100%; - border-left-color: transparent !important; - -webkit-transform: rotate(-129deg); - -ms-transform: rotate(-129deg); - transform: rotate(-129deg); } - .mdl-spinner.is-active .mdl-spinner__right .mdl-spinner__circle { - -webkit-animation: mdl-spinner__right-spin 1333ms cubic-bezier(0.4, 0, 0.2, 1) infinite both; - animation: mdl-spinner__right-spin 1333ms cubic-bezier(0.4, 0, 0.2, 1) infinite both; } - -@-webkit-keyframes mdl-spinner__left-spin { - from { - -webkit-transform: rotate(130deg); - transform: rotate(130deg); } - 50% { - -webkit-transform: rotate(-5deg); - transform: rotate(-5deg); } - to { - -webkit-transform: rotate(130deg); - transform: rotate(130deg); } } - -@keyframes mdl-spinner__left-spin { - from { - -webkit-transform: rotate(130deg); - transform: rotate(130deg); } - 50% { - -webkit-transform: rotate(-5deg); - transform: rotate(-5deg); } - to { - -webkit-transform: rotate(130deg); - transform: rotate(130deg); } } - -@-webkit-keyframes mdl-spinner__right-spin { - from { - -webkit-transform: rotate(-130deg); - transform: rotate(-130deg); } - 50% { - -webkit-transform: rotate(5deg); - transform: rotate(5deg); } - to { - -webkit-transform: rotate(-130deg); - transform: rotate(-130deg); } } - -@keyframes mdl-spinner__right-spin { - from { - -webkit-transform: rotate(-130deg); - transform: rotate(-130deg); } - 50% { - -webkit-transform: rotate(5deg); - transform: rotate(5deg); } - to { - -webkit-transform: rotate(-130deg); - transform: rotate(-130deg); } } - -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/*------------------------------------* $CONTENTS -\*------------------------------------*/ -/** - * STYLE GUIDE VARIABLES------------------Declarations of Sass variables - * -----Typography - * -----Colors - * -----Textfield - * -----Switch - * -----Spinner - * -----Radio - * -----Menu - * -----List - * -----Layout - * -----Icon toggles - * -----Footer - * -----Column - * -----Checkbox - * -----Card - * -----Button - * -----Animation - * -----Progress - * -----Badge - * -----Shadows - * -----Grid - * -----Data table - */ -/* ========== TYPOGRAPHY ========== */ -/* We're splitting fonts into "preferred" and "performance" in order to optimize - page loading. For important text, such as the body, we want it to load - immediately and not wait for the web font load, whereas for other sections, - such as headers and titles, we're OK with things taking a bit longer to load. - We do have some optional classes and parameters in the mixins, in case you - definitely want to make sure you're using the preferred font and don't mind - the performance hit. - We should be able to improve on this once CSS Font Loading L3 becomes more - widely available. -*/ -/* ========== COLORS ========== */ -/** -* -* Material design color palettes. -* @see http://www.google.com/design/spec/style/color.html -* -**/ -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/* ========== Color Palettes ========== */ -/* colors.scss */ -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/* ========== IMAGES ========== */ -/* ========== Color & Themes ========== */ -/* ========== Typography ========== */ -/* ========== Components ========== */ -/* ========== Standard Buttons ========== */ -/* ========== Icon Toggles ========== */ -/* ========== Radio Buttons ========== */ -/* ========== Ripple effect ========== */ -/* ========== Layout ========== */ -/* ========== Content Tabs ========== */ -/* ========== Checkboxes ========== */ -/* ========== Switches ========== */ -/* ========== Spinner ========== */ -/* ========== Text fields ========== */ -/* ========== Card ========== */ -/* ========== Sliders ========== */ -/* ========== Progress ========== */ -/* ========== List ========== */ -/* ========== Item ========== */ -/* ========== Dropdown menu ========== */ -/* ========== Tooltips ========== */ -/* ========== Footer ========== */ -/* TEXTFIELD */ -/* SWITCH */ -/* SPINNER */ -/* RADIO */ -/* MENU */ -/* LIST */ -/* LAYOUT */ -/* ICON TOGGLE */ -/* FOOTER */ -/*mega-footer*/ -/*mini-footer*/ -/* CHECKBOX */ -/* CARD */ -/* Card dimensions */ -/* Cover image */ -/* BUTTON */ -/** - * - * Dimensions - * - */ -/* ANIMATION */ -/* PROGRESS */ -/* BADGE */ -/* SHADOWS */ -/* GRID */ -/* DATA TABLE */ -/* TOOLTIP */ -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/* Typography */ -/* Shadows */ -/* Animations */ -.mdl-switch { - position: relative; - z-index: 1; - vertical-align: middle; - display: inline-block; - box-sizing: border-box; - width: 100%; - height: 24px; - margin: 0; - padding: 0; - overflow: visible; - -webkit-touch-callout: none; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; } - .mdl-switch.is-upgraded { - padding-left: 28px; } - -.mdl-switch__input { - line-height: 24px; } - .mdl-switch.is-upgraded .mdl-switch__input { - position: absolute; - width: 0; - height: 0; - margin: 0; - padding: 0; - opacity: 0; - -ms-appearance: none; - -moz-appearance: none; - -webkit-appearance: none; - appearance: none; - border: none; } - -.mdl-switch__track { - background: rgba(0,0,0, 0.26); - position: absolute; - left: 0; - top: 5px; - height: 14px; - width: 36px; - border-radius: 14px; - cursor: pointer; } - .mdl-switch.is-checked .mdl-switch__track { - background: rgba(63,81,181, 0.5); } - .mdl-switch.is-disabled .mdl-switch__track { - background: rgba(0,0,0, 0.12); - cursor: auto; } - -.mdl-switch__thumb { - background: rgb(250,250,250); - position: absolute; - left: 0; - top: 2px; - height: 20px; - width: 20px; - border-radius: 50%; - cursor: pointer; - box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12); - -webkit-transition-duration: 0.28s; - transition-duration: 0.28s; - -webkit-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); - -webkit-transition-property: left; - transition-property: left; } - .mdl-switch.is-checked .mdl-switch__thumb { - background: rgb(63,81,181); - left: 16px; - box-shadow: 0 3px 4px 0 rgba(0, 0, 0, 0.14), 0 3px 3px -2px rgba(0, 0, 0, 0.2), 0 1px 8px 0 rgba(0, 0, 0, 0.12); } - .mdl-switch.is-disabled .mdl-switch__thumb { - background: rgb(189,189,189); - cursor: auto; } - -.mdl-switch__focus-helper { - position: absolute; - top: 50%; - left: 50%; - -webkit-transform: translate(-4px, -4px); - -ms-transform: translate(-4px, -4px); - transform: translate(-4px, -4px); - display: inline-block; - box-sizing: border-box; - width: 8px; - height: 8px; - border-radius: 50%; - background-color: transparent; } - .mdl-switch.is-focused .mdl-switch__focus-helper { - box-shadow: 0 0 0px 20px rgba(0, 0, 0, 0.1); - background-color: rgba(0, 0, 0, 0.1); } - .mdl-switch.is-focused.is-checked .mdl-switch__focus-helper { - box-shadow: 0 0 0px 20px rgba(63,81,181, 0.26); - background-color: rgba(63,81,181, 0.26); } - -.mdl-switch__label { - position: relative; - cursor: pointer; - font-size: 16px; - line-height: 24px; - margin: 0; - left: 24px; } - .mdl-switch.is-disabled .mdl-switch__label { - color: rgb(189,189,189); - cursor: auto; } - -.mdl-switch__ripple-container { - position: absolute; - z-index: 2; - top: -12px; - left: -14px; - box-sizing: border-box; - width: 48px; - height: 48px; - border-radius: 50%; - cursor: pointer; - overflow: hidden; - -webkit-mask-image: -webkit-radial-gradient(circle, white, black); - -webkit-transition-duration: 0.40s; - transition-duration: 0.40s; - -webkit-transition-timing-function: step-end; - transition-timing-function: step-end; - -webkit-transition-property: left; - transition-property: left; } - .mdl-switch__ripple-container .mdl-ripple { - background: rgb(63,81,181); } - .mdl-switch.is-disabled .mdl-switch__ripple-container { - cursor: auto; } - .mdl-switch.is-disabled .mdl-switch__ripple-container .mdl-ripple { - background: transparent; } - .mdl-switch.is-checked .mdl-switch__ripple-container { - cursor: auto; - left: 2px; } - -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/*------------------------------------* $CONTENTS -\*------------------------------------*/ -/** - * STYLE GUIDE VARIABLES------------------Declarations of Sass variables - * -----Typography - * -----Colors - * -----Textfield - * -----Switch - * -----Spinner - * -----Radio - * -----Menu - * -----List - * -----Layout - * -----Icon toggles - * -----Footer - * -----Column - * -----Checkbox - * -----Card - * -----Button - * -----Animation - * -----Progress - * -----Badge - * -----Shadows - * -----Grid - * -----Data table - */ -/* ========== TYPOGRAPHY ========== */ -/* We're splitting fonts into "preferred" and "performance" in order to optimize - page loading. For important text, such as the body, we want it to load - immediately and not wait for the web font load, whereas for other sections, - such as headers and titles, we're OK with things taking a bit longer to load. - We do have some optional classes and parameters in the mixins, in case you - definitely want to make sure you're using the preferred font and don't mind - the performance hit. - We should be able to improve on this once CSS Font Loading L3 becomes more - widely available. -*/ -/* ========== COLORS ========== */ -/** -* -* Material design color palettes. -* @see http://www.google.com/design/spec/style/color.html -* -**/ -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/* ========== Color Palettes ========== */ -/* colors.scss */ -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/* ========== IMAGES ========== */ -/* ========== Color & Themes ========== */ -/* ========== Typography ========== */ -/* ========== Components ========== */ -/* ========== Standard Buttons ========== */ -/* ========== Icon Toggles ========== */ -/* ========== Radio Buttons ========== */ -/* ========== Ripple effect ========== */ -/* ========== Layout ========== */ -/* ========== Content Tabs ========== */ -/* ========== Checkboxes ========== */ -/* ========== Switches ========== */ -/* ========== Spinner ========== */ -/* ========== Text fields ========== */ -/* ========== Card ========== */ -/* ========== Sliders ========== */ -/* ========== Progress ========== */ -/* ========== List ========== */ -/* ========== Item ========== */ -/* ========== Dropdown menu ========== */ -/* ========== Tooltips ========== */ -/* ========== Footer ========== */ -/* TEXTFIELD */ -/* SWITCH */ -/* SPINNER */ -/* RADIO */ -/* MENU */ -/* LIST */ -/* LAYOUT */ -/* ICON TOGGLE */ -/* FOOTER */ -/*mega-footer*/ -/*mini-footer*/ -/* CHECKBOX */ -/* CARD */ -/* Card dimensions */ -/* Cover image */ -/* BUTTON */ -/** - * - * Dimensions - * - */ -/* ANIMATION */ -/* PROGRESS */ -/* BADGE */ -/* SHADOWS */ -/* GRID */ -/* DATA TABLE */ -/* TOOLTIP */ -.mdl-tabs { - display: block; - width: 100%; } - -.mdl-tabs__tab-bar { - display: -webkit-box; - display: -webkit-flex; - display: -ms-flexbox; - display: flex; - -webkit-box-orient: horizontal; - -webkit-box-direction: normal; - -webkit-flex-direction: row; - -ms-flex-direction: row; - flex-direction: row; - -webkit-box-pack: center; - -webkit-justify-content: center; - -ms-flex-pack: center; - justify-content: center; - -webkit-align-content: space-between; - -ms-flex-line-pack: justify; - align-content: space-between; - -webkit-box-align: start; - -webkit-align-items: flex-start; - -ms-flex-align: start; - align-items: flex-start; - height: 48px; - padding: 0 0 0 0; - margin: 0; - border-bottom: 1px solid rgb(224,224,224); } - -.mdl-tabs__tab { - margin: 0; - border: none; - padding: 0 24px 0 24px; - float: left; - position: relative; - display: block; - color: red; - text-decoration: none; - height: 48px; - line-height: 48px; - text-align: center; - font-weight: 500; - font-size: 14px; - text-transform: uppercase; - color: rgba(0,0,0, 0.54); - overflow: hidden; } - .mdl-tabs.is-upgraded .mdl-tabs__tab.is-active { - color: rgba(0,0,0, 0.87); } - .mdl-tabs.is-upgraded .mdl-tabs__tab.is-active:after { - height: 2px; - width: 100%; - display: block; - content: " "; - bottom: 0px; - left: 0px; - position: absolute; - background: rgb(63,81,181); - -webkit-animation: border-expand 0.2s cubic-bezier(0.4, 0, 0.4, 1) 0.01s alternate forwards; - animation: border-expand 0.2s cubic-bezier(0.4, 0, 0.4, 1) 0.01s alternate forwards; - -webkit-transition: all 1s cubic-bezier(0.4, 0, 1, 1); - transition: all 1s cubic-bezier(0.4, 0, 1, 1); } - .mdl-tabs__tab .mdl-tabs__ripple-container { - display: block; - position: absolute; - height: 100%; - width: 100%; - left: 0px; - top: 0px; - z-index: 1; - overflow: hidden; } - .mdl-tabs__tab .mdl-tabs__ripple-container .mdl-ripple { - background: rgb(63,81,181); } - -.mdl-tabs__panel { - display: block; } - .mdl-tabs.is-upgraded .mdl-tabs__panel { - display: none; } - .mdl-tabs.is-upgraded .mdl-tabs__panel.is-active { - display: block; } - -@-webkit-keyframes border-expand { - 0% { - opacity: 0; - width: 0; } - 100% { - opacity: 1; - width: 100%; } } - -@keyframes border-expand { - 0% { - opacity: 0; - width: 0; } - 100% { - opacity: 1; - width: 100%; } } - -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/*------------------------------------* $CONTENTS -\*------------------------------------*/ -/** - * STYLE GUIDE VARIABLES------------------Declarations of Sass variables - * -----Typography - * -----Colors - * -----Textfield - * -----Switch - * -----Spinner - * -----Radio - * -----Menu - * -----List - * -----Layout - * -----Icon toggles - * -----Footer - * -----Column - * -----Checkbox - * -----Card - * -----Button - * -----Animation - * -----Progress - * -----Badge - * -----Shadows - * -----Grid - * -----Data table - */ -/* ========== TYPOGRAPHY ========== */ -/* We're splitting fonts into "preferred" and "performance" in order to optimize - page loading. For important text, such as the body, we want it to load - immediately and not wait for the web font load, whereas for other sections, - such as headers and titles, we're OK with things taking a bit longer to load. - We do have some optional classes and parameters in the mixins, in case you - definitely want to make sure you're using the preferred font and don't mind - the performance hit. - We should be able to improve on this once CSS Font Loading L3 becomes more - widely available. -*/ -/* ========== COLORS ========== */ -/** -* -* Material design color palettes. -* @see http://www.google.com/design/spec/style/color.html -* -**/ -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/* ========== Color Palettes ========== */ -/* colors.scss */ -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/* ========== IMAGES ========== */ -/* ========== Color & Themes ========== */ -/* ========== Typography ========== */ -/* ========== Components ========== */ -/* ========== Standard Buttons ========== */ -/* ========== Icon Toggles ========== */ -/* ========== Radio Buttons ========== */ -/* ========== Ripple effect ========== */ -/* ========== Layout ========== */ -/* ========== Content Tabs ========== */ -/* ========== Checkboxes ========== */ -/* ========== Switches ========== */ -/* ========== Spinner ========== */ -/* ========== Text fields ========== */ -/* ========== Card ========== */ -/* ========== Sliders ========== */ -/* ========== Progress ========== */ -/* ========== List ========== */ -/* ========== Item ========== */ -/* ========== Dropdown menu ========== */ -/* ========== Tooltips ========== */ -/* ========== Footer ========== */ -/* TEXTFIELD */ -/* SWITCH */ -/* SPINNER */ -/* RADIO */ -/* MENU */ -/* LIST */ -/* LAYOUT */ -/* ICON TOGGLE */ -/* FOOTER */ -/*mega-footer*/ -/*mini-footer*/ -/* CHECKBOX */ -/* CARD */ -/* Card dimensions */ -/* Cover image */ -/* BUTTON */ -/** - * - * Dimensions - * - */ -/* ANIMATION */ -/* PROGRESS */ -/* BADGE */ -/* SHADOWS */ -/* GRID */ -/* DATA TABLE */ -/* TOOLTIP */ -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/* Typography */ -/* Shadows */ -/* Animations */ -.mdl-textfield { - position: relative; - font-size: 16px; - display: inline-block; - box-sizing: border-box; - width: 300px; - max-width: 100%; - margin: 0; - padding: 20px 0; } - .mdl-textfield .mdl-button { - position: absolute; - bottom: 20px; } - -.mdl-textfield--align-right { - text-align: right; } - -.mdl-textfield--full-width { - width: 100%; } - -.mdl-textfield--expandable { - min-width: 32px; - width: auto; - min-height: 32px; } - -.mdl-textfield__input { - border: none; - border-bottom: 1px solid rgba(0,0,0, 0.12); - display: block; - font-size: 16px; - margin: 0; - padding: 4px 0; - width: 100%; - background: none; - text-align: left; - color: inherit; } - .mdl-textfield.is-focused .mdl-textfield__input { - outline: none; } - .mdl-textfield.is-invalid .mdl-textfield__input { - border-color: rgb(222, 50, 38); - box-shadow: none; } - .mdl-textfield.is-disabled .mdl-textfield__input { - background-color: transparent; - border-bottom: 1px dotted rgba(0,0,0, 0.12); - color: rgba(0,0,0, 0.26); } - -.mdl-textfield textarea.mdl-textfield__input { - display: block; } - -.mdl-textfield__label { - bottom: 0; - color: rgba(0,0,0, 0.26); - font-size: 16px; - left: 0; - right: 0; - pointer-events: none; - position: absolute; - display: block; - top: 24px; - width: 100%; - overflow: hidden; - white-space: nowrap; - text-align: left; } - .mdl-textfield.is-dirty .mdl-textfield__label { - visibility: hidden; } - .mdl-textfield--floating-label .mdl-textfield__label { - -webkit-transition-duration: 0.2s; - transition-duration: 0.2s; - -webkit-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); } - .mdl-textfield.is-disabled.is-disabled .mdl-textfield__label { - color: rgba(0,0,0, 0.26); } - .mdl-textfield--floating-label.is-focused .mdl-textfield__label, - .mdl-textfield--floating-label.is-dirty .mdl-textfield__label { - color: rgb(63,81,181); - font-size: 12px; - top: 4px; - visibility: visible; } - .mdl-textfield--floating-label.is-focused .mdl-textfield__expandable-holder .mdl-textfield__label, - .mdl-textfield--floating-label.is-dirty .mdl-textfield__expandable-holder .mdl-textfield__label { - top: -16px; } - .mdl-textfield--floating-label.is-invalid .mdl-textfield__label { - color: rgb(222, 50, 38); - font-size: 12px; } - .mdl-textfield__label:after { - background-color: rgb(63,81,181); - bottom: 20px; - content: ''; - height: 2px; - left: 45%; - position: absolute; - -webkit-transition-duration: 0.2s; - transition-duration: 0.2s; - -webkit-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); - visibility: hidden; - width: 10px; } - .mdl-textfield.is-focused .mdl-textfield__label:after { - left: 0; - visibility: visible; - width: 100%; } - .mdl-textfield.is-invalid .mdl-textfield__label:after { - background-color: rgb(222, 50, 38); } - -.mdl-textfield__error { - color: rgb(222, 50, 38); - position: absolute; - font-size: 12px; - margin-top: 3px; - visibility: hidden; - display: block; } - .mdl-textfield.is-invalid .mdl-textfield__error { - visibility: visible; } - -.mdl-textfield__expandable-holder { - display: inline-block; - position: relative; - margin-left: 32px; - -webkit-transition-duration: 0.2s; - transition-duration: 0.2s; - -webkit-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); - display: inline-block; - max-width: 0.1px; } - .mdl-textfield.is-focused .mdl-textfield__expandable-holder, .mdl-textfield.is-dirty .mdl-textfield__expandable-holder { - max-width: 600px; } - .mdl-textfield__expandable-holder .mdl-textfield__label:after { - bottom: 0; } - -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/*------------------------------------* $CONTENTS -\*------------------------------------*/ -/** - * STYLE GUIDE VARIABLES------------------Declarations of Sass variables - * -----Typography - * -----Colors - * -----Textfield - * -----Switch - * -----Spinner - * -----Radio - * -----Menu - * -----List - * -----Layout - * -----Icon toggles - * -----Footer - * -----Column - * -----Checkbox - * -----Card - * -----Button - * -----Animation - * -----Progress - * -----Badge - * -----Shadows - * -----Grid - * -----Data table - */ -/* ========== TYPOGRAPHY ========== */ -/* We're splitting fonts into "preferred" and "performance" in order to optimize - page loading. For important text, such as the body, we want it to load - immediately and not wait for the web font load, whereas for other sections, - such as headers and titles, we're OK with things taking a bit longer to load. - We do have some optional classes and parameters in the mixins, in case you - definitely want to make sure you're using the preferred font and don't mind - the performance hit. - We should be able to improve on this once CSS Font Loading L3 becomes more - widely available. -*/ -/* ========== COLORS ========== */ -/** -* -* Material design color palettes. -* @see http://www.google.com/design/spec/style/color.html -* -**/ -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/* ========== Color Palettes ========== */ -/* colors.scss */ -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/* ========== IMAGES ========== */ -/* ========== Color & Themes ========== */ -/* ========== Typography ========== */ -/* ========== Components ========== */ -/* ========== Standard Buttons ========== */ -/* ========== Icon Toggles ========== */ -/* ========== Radio Buttons ========== */ -/* ========== Ripple effect ========== */ -/* ========== Layout ========== */ -/* ========== Content Tabs ========== */ -/* ========== Checkboxes ========== */ -/* ========== Switches ========== */ -/* ========== Spinner ========== */ -/* ========== Text fields ========== */ -/* ========== Card ========== */ -/* ========== Sliders ========== */ -/* ========== Progress ========== */ -/* ========== List ========== */ -/* ========== Item ========== */ -/* ========== Dropdown menu ========== */ -/* ========== Tooltips ========== */ -/* ========== Footer ========== */ -/* TEXTFIELD */ -/* SWITCH */ -/* SPINNER */ -/* RADIO */ -/* MENU */ -/* LIST */ -/* LAYOUT */ -/* ICON TOGGLE */ -/* FOOTER */ -/*mega-footer*/ -/*mini-footer*/ -/* CHECKBOX */ -/* CARD */ -/* Card dimensions */ -/* Cover image */ -/* BUTTON */ -/** - * - * Dimensions - * - */ -/* ANIMATION */ -/* PROGRESS */ -/* BADGE */ -/* SHADOWS */ -/* GRID */ -/* DATA TABLE */ -/* TOOLTIP */ -.mdl-tooltip { - -webkit-transform: scale(0); - -ms-transform: scale(0); - transform: scale(0); - -webkit-transform-origin: top center; - -ms-transform-origin: top center; - transform-origin: top center; - will-change: transform; - z-index: 999; - background: rgba(97,97,97, 0.9); - border-radius: 2px; - color: rgb(255,255,255); - display: inline-block; - font-size: 10px; - font-weight: 500; - line-height: 14px; - max-width: 170px; - position: fixed; - top: -500px; - left: -500px; - padding: 8px; - text-align: center; } - -.mdl-tooltip.is-active { - -webkit-animation: pulse 200ms cubic-bezier(0, 0, 0.2, 1) forwards; - animation: pulse 200ms cubic-bezier(0, 0, 0.2, 1) forwards; } - -.mdl-tooltip--large { - line-height: 14px; - font-size: 14px; - padding: 16px; } - -@-webkit-keyframes pulse { - 0% { - -webkit-transform: scale(0); - transform: scale(0); - opacity: 0; } - 50% { - -webkit-transform: scale(0.99); - transform: scale(0.99); } - 100% { - -webkit-transform: scale(1); - transform: scale(1); - opacity: 1; - visibility: visible; } } - -@keyframes pulse { - 0% { - -webkit-transform: scale(0); - transform: scale(0); - opacity: 0; } - 50% { - -webkit-transform: scale(0.99); - transform: scale(0.99); } - 100% { - -webkit-transform: scale(1); - transform: scale(1); - opacity: 1; - visibility: visible; } } - -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/*------------------------------------* $CONTENTS -\*------------------------------------*/ -/** - * STYLE GUIDE VARIABLES------------------Declarations of Sass variables - * -----Typography - * -----Colors - * -----Textfield - * -----Switch - * -----Spinner - * -----Radio - * -----Menu - * -----List - * -----Layout - * -----Icon toggles - * -----Footer - * -----Column - * -----Checkbox - * -----Card - * -----Button - * -----Animation - * -----Progress - * -----Badge - * -----Shadows - * -----Grid - * -----Data table - */ -/* ========== TYPOGRAPHY ========== */ -/* We're splitting fonts into "preferred" and "performance" in order to optimize - page loading. For important text, such as the body, we want it to load - immediately and not wait for the web font load, whereas for other sections, - such as headers and titles, we're OK with things taking a bit longer to load. - We do have some optional classes and parameters in the mixins, in case you - definitely want to make sure you're using the preferred font and don't mind - the performance hit. - We should be able to improve on this once CSS Font Loading L3 becomes more - widely available. -*/ -/* ========== COLORS ========== */ -/** -* -* Material design color palettes. -* @see http://www.google.com/design/spec/style/color.html -* -**/ -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/* ========== Color Palettes ========== */ -/* colors.scss */ -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/* ========== IMAGES ========== */ -/* ========== Color & Themes ========== */ -/* ========== Typography ========== */ -/* ========== Components ========== */ -/* ========== Standard Buttons ========== */ -/* ========== Icon Toggles ========== */ -/* ========== Radio Buttons ========== */ -/* ========== Ripple effect ========== */ -/* ========== Layout ========== */ -/* ========== Content Tabs ========== */ -/* ========== Checkboxes ========== */ -/* ========== Switches ========== */ -/* ========== Spinner ========== */ -/* ========== Text fields ========== */ -/* ========== Card ========== */ -/* ========== Sliders ========== */ -/* ========== Progress ========== */ -/* ========== List ========== */ -/* ========== Item ========== */ -/* ========== Dropdown menu ========== */ -/* ========== Tooltips ========== */ -/* ========== Footer ========== */ -/* TEXTFIELD */ -/* SWITCH */ -/* SPINNER */ -/* RADIO */ -/* MENU */ -/* LIST */ -/* LAYOUT */ -/* ICON TOGGLE */ -/* FOOTER */ -/*mega-footer*/ -/*mini-footer*/ -/* CHECKBOX */ -/* CARD */ -/* Card dimensions */ -/* Cover image */ -/* BUTTON */ -/** - * - * Dimensions - * - */ -/* ANIMATION */ -/* PROGRESS */ -/* BADGE */ -/* SHADOWS */ -/* GRID */ -/* DATA TABLE */ -/* TOOLTIP */ -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/* Typography */ -/* Shadows */ -/* Animations */ -.mdl-shadow--2dp { - box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12); } - -.mdl-shadow--3dp { - box-shadow: 0 3px 4px 0 rgba(0, 0, 0, 0.14), 0 3px 3px -2px rgba(0, 0, 0, 0.2), 0 1px 8px 0 rgba(0, 0, 0, 0.12); } - -.mdl-shadow--4dp { - box-shadow: 0 4px 5px 0 rgba(0, 0, 0, 0.14), 0 1px 10px 0 rgba(0, 0, 0, 0.12), 0 2px 4px -1px rgba(0, 0, 0, 0.2); } - -.mdl-shadow--6dp { - box-shadow: 0 6px 10px 0 rgba(0, 0, 0, 0.14), 0 1px 18px 0 rgba(0, 0, 0, 0.12), 0 3px 5px -1px rgba(0, 0, 0, 0.2); } - -.mdl-shadow--8dp { - box-shadow: 0 8px 10px 1px rgba(0, 0, 0, 0.14), 0 3px 14px 2px rgba(0, 0, 0, 0.12), 0 5px 5px -3px rgba(0, 0, 0, 0.2); } - -.mdl-shadow--16dp { - box-shadow: 0 16px 24px 2px rgba(0, 0, 0, 0.14), 0 6px 30px 5px rgba(0, 0, 0, 0.12), 0 8px 10px -5px rgba(0, 0, 0, 0.2); } - -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/* -* NOTE: Some rules here are applied using duplicate selectors. -* This is on purpose to increase their specificity when applied. -* For example: `.mdl-cell--1-col-phone.mdl-cell--1-col-phone` -*/ -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/*------------------------------------* $CONTENTS -\*------------------------------------*/ -/** - * STYLE GUIDE VARIABLES------------------Declarations of Sass variables - * -----Typography - * -----Colors - * -----Textfield - * -----Switch - * -----Spinner - * -----Radio - * -----Menu - * -----List - * -----Layout - * -----Icon toggles - * -----Footer - * -----Column - * -----Checkbox - * -----Card - * -----Button - * -----Animation - * -----Progress - * -----Badge - * -----Shadows - * -----Grid - * -----Data table - */ -/* ========== TYPOGRAPHY ========== */ -/* We're splitting fonts into "preferred" and "performance" in order to optimize - page loading. For important text, such as the body, we want it to load - immediately and not wait for the web font load, whereas for other sections, - such as headers and titles, we're OK with things taking a bit longer to load. - We do have some optional classes and parameters in the mixins, in case you - definitely want to make sure you're using the preferred font and don't mind - the performance hit. - We should be able to improve on this once CSS Font Loading L3 becomes more - widely available. -*/ -/* ========== COLORS ========== */ -/** -* -* Material design color palettes. -* @see http://www.google.com/design/spec/style/color.html -* -**/ -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/* ========== Color Palettes ========== */ -/* colors.scss */ -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/* ========== IMAGES ========== */ -/* ========== Color & Themes ========== */ -/* ========== Typography ========== */ -/* ========== Components ========== */ -/* ========== Standard Buttons ========== */ -/* ========== Icon Toggles ========== */ -/* ========== Radio Buttons ========== */ -/* ========== Ripple effect ========== */ -/* ========== Layout ========== */ -/* ========== Content Tabs ========== */ -/* ========== Checkboxes ========== */ -/* ========== Switches ========== */ -/* ========== Spinner ========== */ -/* ========== Text fields ========== */ -/* ========== Card ========== */ -/* ========== Sliders ========== */ -/* ========== Progress ========== */ -/* ========== List ========== */ -/* ========== Item ========== */ -/* ========== Dropdown menu ========== */ -/* ========== Tooltips ========== */ -/* ========== Footer ========== */ -/* TEXTFIELD */ -/* SWITCH */ -/* SPINNER */ -/* RADIO */ -/* MENU */ -/* LIST */ -/* LAYOUT */ -/* ICON TOGGLE */ -/* FOOTER */ -/*mega-footer*/ -/*mini-footer*/ -/* CHECKBOX */ -/* CARD */ -/* Card dimensions */ -/* Cover image */ -/* BUTTON */ -/** - * - * Dimensions - * - */ -/* ANIMATION */ -/* PROGRESS */ -/* BADGE */ -/* SHADOWS */ -/* GRID */ -/* DATA TABLE */ -/* TOOLTIP */ -.mdl-grid { - display: -webkit-box; - display: -webkit-flex; - display: -ms-flexbox; - display: flex; - -webkit-flex-flow: row wrap; - -ms-flex-flow: row wrap; - flex-flow: row wrap; - margin: 0 auto 0 auto; - -webkit-box-align: stretch; - -webkit-align-items: stretch; - -ms-flex-align: stretch; - align-items: stretch; } - .mdl-grid.mdl-grid--no-spacing { - padding: 0; } - -.mdl-cell { - box-sizing: border-box; } - -.mdl-cell--top { - -webkit-align-self: flex-start; - -ms-flex-item-align: start; - align-self: flex-start; } - -.mdl-cell--middle { - -webkit-align-self: center; - -ms-flex-item-align: center; - align-self: center; } - -.mdl-cell--bottom { - -webkit-align-self: flex-end; - -ms-flex-item-align: end; - align-self: flex-end; } - -.mdl-cell--stretch { - -webkit-align-self: stretch; - -ms-flex-item-align: stretch; - align-self: stretch; } - -.mdl-grid.mdl-grid--no-spacing > .mdl-cell { - margin: 0; } - -@media (max-width: 479px) { - .mdl-grid { - padding: 8px; } - .mdl-cell { - margin: 8px; - width: calc(100% - 16px); } - .mdl-grid--no-spacing > .mdl-cell { - width: 100%; } - .mdl-cell--hide-phone { - display: none !important; } - .mdl-cell--1-col, - .mdl-cell--1-col-phone.mdl-cell--1-col-phone { - width: calc(25% - 16px); } - .mdl-grid--no-spacing > .mdl-cell--1-col, .mdl-grid--no-spacing > - .mdl-cell--1-col-phone.mdl-cell--1-col-phone { - width: 25%; } - .mdl-cell--2-col, - .mdl-cell--2-col-phone.mdl-cell--2-col-phone { - width: calc(50% - 16px); } - .mdl-grid--no-spacing > .mdl-cell--2-col, .mdl-grid--no-spacing > - .mdl-cell--2-col-phone.mdl-cell--2-col-phone { - width: 50%; } - .mdl-cell--3-col, - .mdl-cell--3-col-phone.mdl-cell--3-col-phone { - width: calc(75% - 16px); } - .mdl-grid--no-spacing > .mdl-cell--3-col, .mdl-grid--no-spacing > - .mdl-cell--3-col-phone.mdl-cell--3-col-phone { - width: 75%; } - .mdl-cell--4-col, - .mdl-cell--4-col-phone.mdl-cell--4-col-phone { - width: calc(100% - 16px); } - .mdl-grid--no-spacing > .mdl-cell--4-col, .mdl-grid--no-spacing > - .mdl-cell--4-col-phone.mdl-cell--4-col-phone { - width: 100%; } - .mdl-cell--5-col, - .mdl-cell--5-col-phone.mdl-cell--5-col-phone { - width: calc(100% - 16px); } - .mdl-grid--no-spacing > .mdl-cell--5-col, .mdl-grid--no-spacing > - .mdl-cell--5-col-phone.mdl-cell--5-col-phone { - width: 100%; } - .mdl-cell--6-col, - .mdl-cell--6-col-phone.mdl-cell--6-col-phone { - width: calc(100% - 16px); } - .mdl-grid--no-spacing > .mdl-cell--6-col, .mdl-grid--no-spacing > - .mdl-cell--6-col-phone.mdl-cell--6-col-phone { - width: 100%; } - .mdl-cell--7-col, - .mdl-cell--7-col-phone.mdl-cell--7-col-phone { - width: calc(100% - 16px); } - .mdl-grid--no-spacing > .mdl-cell--7-col, .mdl-grid--no-spacing > - .mdl-cell--7-col-phone.mdl-cell--7-col-phone { - width: 100%; } - .mdl-cell--8-col, - .mdl-cell--8-col-phone.mdl-cell--8-col-phone { - width: calc(100% - 16px); } - .mdl-grid--no-spacing > .mdl-cell--8-col, .mdl-grid--no-spacing > - .mdl-cell--8-col-phone.mdl-cell--8-col-phone { - width: 100%; } - .mdl-cell--9-col, - .mdl-cell--9-col-phone.mdl-cell--9-col-phone { - width: calc(100% - 16px); } - .mdl-grid--no-spacing > .mdl-cell--9-col, .mdl-grid--no-spacing > - .mdl-cell--9-col-phone.mdl-cell--9-col-phone { - width: 100%; } - .mdl-cell--10-col, - .mdl-cell--10-col-phone.mdl-cell--10-col-phone { - width: calc(100% - 16px); } - .mdl-grid--no-spacing > .mdl-cell--10-col, .mdl-grid--no-spacing > - .mdl-cell--10-col-phone.mdl-cell--10-col-phone { - width: 100%; } - .mdl-cell--11-col, - .mdl-cell--11-col-phone.mdl-cell--11-col-phone { - width: calc(100% - 16px); } - .mdl-grid--no-spacing > .mdl-cell--11-col, .mdl-grid--no-spacing > - .mdl-cell--11-col-phone.mdl-cell--11-col-phone { - width: 100%; } - .mdl-cell--12-col, - .mdl-cell--12-col-phone.mdl-cell--12-col-phone { - width: calc(100% - 16px); } - .mdl-grid--no-spacing > .mdl-cell--12-col, .mdl-grid--no-spacing > - .mdl-cell--12-col-phone.mdl-cell--12-col-phone { - width: 100%; } } - -@media (min-width: 480px) and (max-width: 839px) { - .mdl-grid { - padding: 8px; } - .mdl-cell { - margin: 8px; - width: calc(50% - 16px); } - .mdl-grid--no-spacing > .mdl-cell { - width: 50%; } - .mdl-cell--hide-tablet { - display: none !important; } - .mdl-cell--1-col, - .mdl-cell--1-col-tablet.mdl-cell--1-col-tablet { - width: calc(12.5% - 16px); } - .mdl-grid--no-spacing > .mdl-cell--1-col, .mdl-grid--no-spacing > - .mdl-cell--1-col-tablet.mdl-cell--1-col-tablet { - width: 12.5%; } - .mdl-cell--2-col, - .mdl-cell--2-col-tablet.mdl-cell--2-col-tablet { - width: calc(25% - 16px); } - .mdl-grid--no-spacing > .mdl-cell--2-col, .mdl-grid--no-spacing > - .mdl-cell--2-col-tablet.mdl-cell--2-col-tablet { - width: 25%; } - .mdl-cell--3-col, - .mdl-cell--3-col-tablet.mdl-cell--3-col-tablet { - width: calc(37.5% - 16px); } - .mdl-grid--no-spacing > .mdl-cell--3-col, .mdl-grid--no-spacing > - .mdl-cell--3-col-tablet.mdl-cell--3-col-tablet { - width: 37.5%; } - .mdl-cell--4-col, - .mdl-cell--4-col-tablet.mdl-cell--4-col-tablet { - width: calc(50% - 16px); } - .mdl-grid--no-spacing > .mdl-cell--4-col, .mdl-grid--no-spacing > - .mdl-cell--4-col-tablet.mdl-cell--4-col-tablet { - width: 50%; } - .mdl-cell--5-col, - .mdl-cell--5-col-tablet.mdl-cell--5-col-tablet { - width: calc(62.5% - 16px); } - .mdl-grid--no-spacing > .mdl-cell--5-col, .mdl-grid--no-spacing > - .mdl-cell--5-col-tablet.mdl-cell--5-col-tablet { - width: 62.5%; } - .mdl-cell--6-col, - .mdl-cell--6-col-tablet.mdl-cell--6-col-tablet { - width: calc(75% - 16px); } - .mdl-grid--no-spacing > .mdl-cell--6-col, .mdl-grid--no-spacing > - .mdl-cell--6-col-tablet.mdl-cell--6-col-tablet { - width: 75%; } - .mdl-cell--7-col, - .mdl-cell--7-col-tablet.mdl-cell--7-col-tablet { - width: calc(87.5% - 16px); } - .mdl-grid--no-spacing > .mdl-cell--7-col, .mdl-grid--no-spacing > - .mdl-cell--7-col-tablet.mdl-cell--7-col-tablet { - width: 87.5%; } - .mdl-cell--8-col, - .mdl-cell--8-col-tablet.mdl-cell--8-col-tablet { - width: calc(100% - 16px); } - .mdl-grid--no-spacing > .mdl-cell--8-col, .mdl-grid--no-spacing > - .mdl-cell--8-col-tablet.mdl-cell--8-col-tablet { - width: 100%; } - .mdl-cell--9-col, - .mdl-cell--9-col-tablet.mdl-cell--9-col-tablet { - width: calc(100% - 16px); } - .mdl-grid--no-spacing > .mdl-cell--9-col, .mdl-grid--no-spacing > - .mdl-cell--9-col-tablet.mdl-cell--9-col-tablet { - width: 100%; } - .mdl-cell--10-col, - .mdl-cell--10-col-tablet.mdl-cell--10-col-tablet { - width: calc(100% - 16px); } - .mdl-grid--no-spacing > .mdl-cell--10-col, .mdl-grid--no-spacing > - .mdl-cell--10-col-tablet.mdl-cell--10-col-tablet { - width: 100%; } - .mdl-cell--11-col, - .mdl-cell--11-col-tablet.mdl-cell--11-col-tablet { - width: calc(100% - 16px); } - .mdl-grid--no-spacing > .mdl-cell--11-col, .mdl-grid--no-spacing > - .mdl-cell--11-col-tablet.mdl-cell--11-col-tablet { - width: 100%; } - .mdl-cell--12-col, - .mdl-cell--12-col-tablet.mdl-cell--12-col-tablet { - width: calc(100% - 16px); } - .mdl-grid--no-spacing > .mdl-cell--12-col, .mdl-grid--no-spacing > - .mdl-cell--12-col-tablet.mdl-cell--12-col-tablet { - width: 100%; } } - -@media (min-width: 840px) { - .mdl-grid { - padding: 8px; } - .mdl-cell { - margin: 8px; - width: calc(33.3333333333% - 16px); } - .mdl-grid--no-spacing > .mdl-cell { - width: 33.3333333333%; } - .mdl-cell--hide-desktop { - display: none !important; } - .mdl-cell--1-col, - .mdl-cell--1-col-desktop.mdl-cell--1-col-desktop { - width: calc(8.3333333333% - 16px); } - .mdl-grid--no-spacing > .mdl-cell--1-col, .mdl-grid--no-spacing > - .mdl-cell--1-col-desktop.mdl-cell--1-col-desktop { - width: 8.3333333333%; } - .mdl-cell--2-col, - .mdl-cell--2-col-desktop.mdl-cell--2-col-desktop { - width: calc(16.6666666667% - 16px); } - .mdl-grid--no-spacing > .mdl-cell--2-col, .mdl-grid--no-spacing > - .mdl-cell--2-col-desktop.mdl-cell--2-col-desktop { - width: 16.6666666667%; } - .mdl-cell--3-col, - .mdl-cell--3-col-desktop.mdl-cell--3-col-desktop { - width: calc(25% - 16px); } - .mdl-grid--no-spacing > .mdl-cell--3-col, .mdl-grid--no-spacing > - .mdl-cell--3-col-desktop.mdl-cell--3-col-desktop { - width: 25%; } - .mdl-cell--4-col, - .mdl-cell--4-col-desktop.mdl-cell--4-col-desktop { - width: calc(33.3333333333% - 16px); } - .mdl-grid--no-spacing > .mdl-cell--4-col, .mdl-grid--no-spacing > - .mdl-cell--4-col-desktop.mdl-cell--4-col-desktop { - width: 33.3333333333%; } - .mdl-cell--5-col, - .mdl-cell--5-col-desktop.mdl-cell--5-col-desktop { - width: calc(41.6666666667% - 16px); } - .mdl-grid--no-spacing > .mdl-cell--5-col, .mdl-grid--no-spacing > - .mdl-cell--5-col-desktop.mdl-cell--5-col-desktop { - width: 41.6666666667%; } - .mdl-cell--6-col, - .mdl-cell--6-col-desktop.mdl-cell--6-col-desktop { - width: calc(50% - 16px); } - .mdl-grid--no-spacing > .mdl-cell--6-col, .mdl-grid--no-spacing > - .mdl-cell--6-col-desktop.mdl-cell--6-col-desktop { - width: 50%; } - .mdl-cell--7-col, - .mdl-cell--7-col-desktop.mdl-cell--7-col-desktop { - width: calc(58.3333333333% - 16px); } - .mdl-grid--no-spacing > .mdl-cell--7-col, .mdl-grid--no-spacing > - .mdl-cell--7-col-desktop.mdl-cell--7-col-desktop { - width: 58.3333333333%; } - .mdl-cell--8-col, - .mdl-cell--8-col-desktop.mdl-cell--8-col-desktop { - width: calc(66.6666666667% - 16px); } - .mdl-grid--no-spacing > .mdl-cell--8-col, .mdl-grid--no-spacing > - .mdl-cell--8-col-desktop.mdl-cell--8-col-desktop { - width: 66.6666666667%; } - .mdl-cell--9-col, - .mdl-cell--9-col-desktop.mdl-cell--9-col-desktop { - width: calc(75% - 16px); } - .mdl-grid--no-spacing > .mdl-cell--9-col, .mdl-grid--no-spacing > - .mdl-cell--9-col-desktop.mdl-cell--9-col-desktop { - width: 75%; } - .mdl-cell--10-col, - .mdl-cell--10-col-desktop.mdl-cell--10-col-desktop { - width: calc(83.3333333333% - 16px); } - .mdl-grid--no-spacing > .mdl-cell--10-col, .mdl-grid--no-spacing > - .mdl-cell--10-col-desktop.mdl-cell--10-col-desktop { - width: 83.3333333333%; } - .mdl-cell--11-col, - .mdl-cell--11-col-desktop.mdl-cell--11-col-desktop { - width: calc(91.6666666667% - 16px); } - .mdl-grid--no-spacing > .mdl-cell--11-col, .mdl-grid--no-spacing > - .mdl-cell--11-col-desktop.mdl-cell--11-col-desktop { - width: 91.6666666667%; } - .mdl-cell--12-col, - .mdl-cell--12-col-desktop.mdl-cell--12-col-desktop { - width: calc(100% - 16px); } - .mdl-grid--no-spacing > .mdl-cell--12-col, .mdl-grid--no-spacing > - .mdl-cell--12-col-desktop.mdl-cell--12-col-desktop { - width: 100%; } } diff --git a/examples/scalajs-play-core-react/server/src/main/assets/material/material.js b/examples/scalajs-play-core-react/server/src/main/assets/material/material.js deleted file mode 100644 index c6d5c08..0000000 --- a/examples/scalajs-play-core-react/server/src/main/assets/material/material.js +++ /dev/null @@ -1,3865 +0,0 @@ -;(function() { -"use strict"; - -/** - * @license - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * A component handler interface using the revealing module design pattern. - * More details on this design pattern here: - * https://github.com/jasonmayes/mdl-component-design-pattern - * - * @author Jason Mayes. - */ -/* exported componentHandler */ - -// Pre-defining the componentHandler interface, for closure documentation and -// static verification. -var componentHandler = { - /** - * Searches existing DOM for elements of our component type and upgrades them - * if they have not already been upgraded. - * - * @param {string=} optJsClass the programatic name of the element class we - * need to create a new instance of. - * @param {string=} optCssClass the name of the CSS class elements of this - * type will have. - */ - upgradeDom: function(optJsClass, optCssClass) {}, - /** - * Upgrades a specific element rather than all in the DOM. - * - * @param {!Element} element The element we wish to upgrade. - * @param {string=} optJsClass Optional name of the class we want to upgrade - * the element to. - */ - upgradeElement: function(element, optJsClass) {}, - /** - * Upgrades a specific list of elements rather than all in the DOM. - * - * @param {!Element|!Array|!NodeList|!HTMLCollection} elements - * The elements we wish to upgrade. - */ - upgradeElements: function(elements) {}, - /** - * Upgrades all registered components found in the current DOM. This is - * automatically called on window load. - */ - upgradeAllRegistered: function() {}, - /** - * Allows user to be alerted to any upgrades that are performed for a given - * component type - * - * @param {string} jsClass The class name of the MDL component we wish - * to hook into for any upgrades performed. - * @param {function(!HTMLElement)} callback The function to call upon an - * upgrade. This function should expect 1 parameter - the HTMLElement which - * got upgraded. - */ - registerUpgradedCallback: function(jsClass, callback) {}, - /** - * Registers a class for future use and attempts to upgrade existing DOM. - * - * @param {componentHandler.ComponentConfigPublic} config the registration configuration - */ - register: function(config) {}, - /** - * Downgrade either a given node, an array of nodes, or a NodeList. - * - * @param {!Node|!Array|!NodeList} nodes - */ - downgradeElements: function(nodes) {} -}; - -componentHandler = (function() { - 'use strict'; - - /** @type {!Array} */ - var registeredComponents_ = []; - - /** @type {!Array} */ - var createdComponents_ = []; - - var downgradeMethod_ = 'mdlDowngrade'; - var componentConfigProperty_ = 'mdlComponentConfigInternal_'; - - /** - * Searches registered components for a class we are interested in using. - * Optionally replaces a match with passed object if specified. - * - * @param {string} name The name of a class we want to use. - * @param {componentHandler.ComponentConfig=} optReplace Optional object to replace match with. - * @return {!Object|boolean} - * @private - */ - function findRegisteredClass_(name, optReplace) { - for (var i = 0; i < registeredComponents_.length; i++) { - if (registeredComponents_[i].className === name) { - if (typeof optReplace !== 'undefined') { - registeredComponents_[i] = optReplace; - } - return registeredComponents_[i]; - } - } - return false; - } - - /** - * Returns an array of the classNames of the upgraded classes on the element. - * - * @param {!Element} element The element to fetch data from. - * @return {!Array} - * @private - */ - function getUpgradedListOfElement_(element) { - var dataUpgraded = element.getAttribute('data-upgraded'); - // Use `['']` as default value to conform the `,name,name...` style. - return dataUpgraded === null ? [''] : dataUpgraded.split(','); - } - - /** - * Returns true if the given element has already been upgraded for the given - * class. - * - * @param {!Element} element The element we want to check. - * @param {string} jsClass The class to check for. - * @returns {boolean} - * @private - */ - function isElementUpgraded_(element, jsClass) { - var upgradedList = getUpgradedListOfElement_(element); - return upgradedList.indexOf(jsClass) !== -1; - } - - /** - * Searches existing DOM for elements of our component type and upgrades them - * if they have not already been upgraded. - * - * @param {string=} optJsClass the programatic name of the element class we - * need to create a new instance of. - * @param {string=} optCssClass the name of the CSS class elements of this - * type will have. - */ - function upgradeDomInternal(optJsClass, optCssClass) { - if (typeof optJsClass === 'undefined' && - typeof optCssClass === 'undefined') { - for (var i = 0; i < registeredComponents_.length; i++) { - upgradeDomInternal(registeredComponents_[i].className, - registeredComponents_[i].cssClass); - } - } else { - var jsClass = /** @type {string} */ (optJsClass); - if (typeof optCssClass === 'undefined') { - var registeredClass = findRegisteredClass_(jsClass); - if (registeredClass) { - optCssClass = registeredClass.cssClass; - } - } - - var elements = document.querySelectorAll('.' + optCssClass); - for (var n = 0; n < elements.length; n++) { - upgradeElementInternal(elements[n], jsClass); - } - } - } - - /** - * Upgrades a specific element rather than all in the DOM. - * - * @param {!Element} element The element we wish to upgrade. - * @param {string=} optJsClass Optional name of the class we want to upgrade - * the element to. - */ - function upgradeElementInternal(element, optJsClass) { - // Verify argument type. - if (!(typeof element === 'object' && element instanceof Element)) { - throw new Error('Invalid argument provided to upgrade MDL element.'); - } - var upgradedList = getUpgradedListOfElement_(element); - var classesToUpgrade = []; - // If jsClass is not provided scan the registered components to find the - // ones matching the element's CSS classList. - if (!optJsClass) { - var classList = element.classList; - registeredComponents_.forEach(function(component) { - // Match CSS & Not to be upgraded & Not upgraded. - if (classList.contains(component.cssClass) && - classesToUpgrade.indexOf(component) === -1 && - !isElementUpgraded_(element, component.className)) { - classesToUpgrade.push(component); - } - }); - } else if (!isElementUpgraded_(element, optJsClass)) { - classesToUpgrade.push(findRegisteredClass_(optJsClass)); - } - - // Upgrade the element for each classes. - for (var i = 0, n = classesToUpgrade.length, registeredClass; i < n; i++) { - registeredClass = classesToUpgrade[i]; - if (registeredClass) { - // Mark element as upgraded. - upgradedList.push(registeredClass.className); - element.setAttribute('data-upgraded', upgradedList.join(',')); - var instance = new registeredClass.classConstructor(element); - instance[componentConfigProperty_] = registeredClass; - createdComponents_.push(instance); - // Call any callbacks the user has registered with this component type. - for (var j = 0, m = registeredClass.callbacks.length; j < m; j++) { - registeredClass.callbacks[j](element); - } - - if (registeredClass.widget) { - // Assign per element instance for control over API - element[registeredClass.className] = instance; - } - } else { - throw new Error( - 'Unable to find a registered component for the given class.'); - } - - var ev = document.createEvent('Events'); - ev.initEvent('mdl-componentupgraded', true, true); - element.dispatchEvent(ev); - } - } - - /** - * Upgrades a specific list of elements rather than all in the DOM. - * - * @param {!Element|!Array|!NodeList|!HTMLCollection} elements - * The elements we wish to upgrade. - */ - function upgradeElementsInternal(elements) { - if (!Array.isArray(elements)) { - if (typeof elements.item === 'function') { - elements = Array.prototype.slice.call(/** @type {Array} */ (elements)); - } else { - elements = [elements]; - } - } - for (var i = 0, n = elements.length, element; i < n; i++) { - element = elements[i]; - if (element instanceof HTMLElement) { - upgradeElementInternal(element); - if (element.children.length > 0) { - upgradeElementsInternal(element.children); - } - } - } - } - - /** - * Registers a class for future use and attempts to upgrade existing DOM. - * - * @param {componentHandler.ComponentConfigPublic} config - */ - function registerInternal(config) { - // In order to support both Closure-compiled and uncompiled code accessing - // this method, we need to allow for both the dot and array syntax for - // property access. You'll therefore see the `foo.bar || foo['bar']` - // pattern repeated across this method. - var widgetMissing = (typeof config.widget === 'undefined' && - typeof config['widget'] === 'undefined'); - var widget = true; - - if (!widgetMissing) { - widget = config.widget || config['widget']; - } - - var newConfig = /** @type {componentHandler.ComponentConfig} */ ({ - classConstructor: config.constructor || config['constructor'], - className: config.classAsString || config['classAsString'], - cssClass: config.cssClass || config['cssClass'], - widget: widget, - callbacks: [] - }); - - registeredComponents_.forEach(function(item) { - if (item.cssClass === newConfig.cssClass) { - throw new Error('The provided cssClass has already been registered: ' + item.cssClass); - } - if (item.className === newConfig.className) { - throw new Error('The provided className has already been registered'); - } - }); - - if (config.constructor.prototype - .hasOwnProperty(componentConfigProperty_)) { - throw new Error( - 'MDL component classes must not have ' + componentConfigProperty_ + - ' defined as a property.'); - } - - var found = findRegisteredClass_(config.classAsString, newConfig); - - if (!found) { - registeredComponents_.push(newConfig); - } - } - - /** - * Allows user to be alerted to any upgrades that are performed for a given - * component type - * - * @param {string} jsClass The class name of the MDL component we wish - * to hook into for any upgrades performed. - * @param {function(!HTMLElement)} callback The function to call upon an - * upgrade. This function should expect 1 parameter - the HTMLElement which - * got upgraded. - */ - function registerUpgradedCallbackInternal(jsClass, callback) { - var regClass = findRegisteredClass_(jsClass); - if (regClass) { - regClass.callbacks.push(callback); - } - } - - /** - * Upgrades all registered components found in the current DOM. This is - * automatically called on window load. - */ - function upgradeAllRegisteredInternal() { - for (var n = 0; n < registeredComponents_.length; n++) { - upgradeDomInternal(registeredComponents_[n].className); - } - } - - /** - * Finds a created component by a given DOM node. - * - * @param {!Node} node - * @return {*} - */ - function findCreatedComponentByNodeInternal(node) { - for (var n = 0; n < createdComponents_.length; n++) { - var component = createdComponents_[n]; - if (component.element_ === node) { - return component; - } - } - } - - /** - * Check the component for the downgrade method. - * Execute if found. - * Remove component from createdComponents list. - * - * @param {*} component - */ - function deconstructComponentInternal(component) { - if (component && - component[componentConfigProperty_] - .classConstructor.prototype - .hasOwnProperty(downgradeMethod_)) { - component[downgradeMethod_](); - var componentIndex = createdComponents_.indexOf(component); - createdComponents_.splice(componentIndex, 1); - - var upgrades = component.element_.getAttribute('data-upgraded').split(','); - var componentPlace = upgrades.indexOf( - component[componentConfigProperty_].classAsString); - upgrades.splice(componentPlace, 1); - component.element_.setAttribute('data-upgraded', upgrades.join(',')); - - var ev = document.createEvent('Events'); - ev.initEvent('mdl-componentdowngraded', true, true); - component.element_.dispatchEvent(ev); - } - } - - /** - * Downgrade either a given node, an array of nodes, or a NodeList. - * - * @param {!Node|!Array|!NodeList} nodes - */ - function downgradeNodesInternal(nodes) { - /** - * Auxiliary function to downgrade a single node. - * @param {!Node} node the node to be downgraded - */ - var downgradeNode = function(node) { - deconstructComponentInternal(findCreatedComponentByNodeInternal(node)); - }; - if (nodes instanceof Array || nodes instanceof NodeList) { - for (var n = 0; n < nodes.length; n++) { - downgradeNode(nodes[n]); - } - } else if (nodes instanceof Node) { - downgradeNode(nodes); - } else { - throw new Error('Invalid argument provided to downgrade MDL nodes.'); - } - } - - // Now return the functions that should be made public with their publicly - // facing names... - return { - upgradeDom: upgradeDomInternal, - upgradeElement: upgradeElementInternal, - upgradeElements: upgradeElementsInternal, - upgradeAllRegistered: upgradeAllRegisteredInternal, - registerUpgradedCallback: registerUpgradedCallbackInternal, - register: registerInternal, - downgradeElements: downgradeNodesInternal - }; -})(); - -/** - * Describes the type of a registered component type managed by - * componentHandler. Provided for benefit of the Closure compiler. - * - * @typedef {{ - * constructor: Function, - * classAsString: string, - * cssClass: string, - * widget: (string|boolean|undefined) - * }} - */ -componentHandler.ComponentConfigPublic; // jshint ignore:line - -/** - * Describes the type of a registered component type managed by - * componentHandler. Provided for benefit of the Closure compiler. - * - * @typedef {{ - * constructor: !Function, - * className: string, - * cssClass: string, - * widget: (string|boolean), - * callbacks: !Array - * }} - */ -componentHandler.ComponentConfig; // jshint ignore:line - -/** - * Created component (i.e., upgraded element) type as managed by - * componentHandler. Provided for benefit of the Closure compiler. - * - * @typedef {{ - * element_: !HTMLElement, - * className: string, - * classAsString: string, - * cssClass: string, - * widget: string - * }} - */ -componentHandler.Component; // jshint ignore:line - -// Export all symbols, for the benefit of Closure compiler. -// No effect on uncompiled code. -componentHandler['upgradeDom'] = componentHandler.upgradeDom; -componentHandler['upgradeElement'] = componentHandler.upgradeElement; -componentHandler['upgradeElements'] = componentHandler.upgradeElements; -componentHandler['upgradeAllRegistered'] = - componentHandler.upgradeAllRegistered; -componentHandler['registerUpgradedCallback'] = - componentHandler.registerUpgradedCallback; -componentHandler['register'] = componentHandler.register; -componentHandler['downgradeElements'] = componentHandler.downgradeElements; -window.componentHandler = componentHandler; -window['componentHandler'] = componentHandler; - -window.addEventListener('load', function() { - 'use strict'; - - /** - * Performs a "Cutting the mustard" test. If the browser supports the features - * tested, adds a mdl-js class to the element. It then upgrades all MDL - * components requiring JavaScript. - */ - if ('classList' in document.createElement('div') && - 'querySelector' in document && - 'addEventListener' in window && Array.prototype.forEach) { - document.documentElement.classList.add('mdl-js'); - componentHandler.upgradeAllRegistered(); - } else { - /** - * Dummy function to avoid JS errors. - */ - componentHandler.upgradeElement = function() {}; - /** - * Dummy function to avoid JS errors. - */ - componentHandler.register = function() {}; - } -}); - -// Source: https://github.com/darius/requestAnimationFrame/blob/master/requestAnimationFrame.js -// Adapted from https://gist.github.com/paulirish/1579671 which derived from -// http://paulirish.com/2011/requestanimationframe-for-smart-animating/ -// http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating -// requestAnimationFrame polyfill by Erik Möller. -// Fixes from Paul Irish, Tino Zijdel, Andrew Mao, Klemen Slavič, Darius Bacon -// MIT license -if (!Date.now) { - /** - * Date.now polyfill. - * @return {number} the current Date - */ - Date.now = function () { - return new Date().getTime(); - }; - Date['now'] = Date.now; -} -var vendors = [ - 'webkit', - 'moz' -]; -for (var i = 0; i < vendors.length && !window.requestAnimationFrame; ++i) { - var vp = vendors[i]; - window.requestAnimationFrame = window[vp + 'RequestAnimationFrame']; - window.cancelAnimationFrame = window[vp + 'CancelAnimationFrame'] || window[vp + 'CancelRequestAnimationFrame']; - window['requestAnimationFrame'] = window.requestAnimationFrame; - window['cancelAnimationFrame'] = window.cancelAnimationFrame; -} -if (/iP(ad|hone|od).*OS 6/.test(window.navigator.userAgent) || !window.requestAnimationFrame || !window.cancelAnimationFrame) { - var lastTime = 0; - /** - * requestAnimationFrame polyfill. - * @param {!Function} callback the callback function. - */ - window.requestAnimationFrame = function (callback) { - var now = Date.now(); - var nextTime = Math.max(lastTime + 16, now); - return setTimeout(function () { - callback(lastTime = nextTime); - }, nextTime - now); - }; - window.cancelAnimationFrame = clearTimeout; - window['requestAnimationFrame'] = window.requestAnimationFrame; - window['cancelAnimationFrame'] = window.cancelAnimationFrame; -} -/** - * @license - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/** - * Class constructor for Button MDL component. - * Implements MDL component design pattern defined at: - * https://github.com/jasonmayes/mdl-component-design-pattern - * - * @param {HTMLElement} element The element that will be upgraded. - */ -var MaterialButton = function MaterialButton(element) { - this.element_ = element; - // Initialize instance. - this.init(); -}; -window['MaterialButton'] = MaterialButton; -/** - * Store constants in one place so they can be updated easily. - * - * @enum {string | number} - * @private - */ -MaterialButton.prototype.Constant_ = {}; -/** - * Store strings for class names defined by this component that are used in - * JavaScript. This allows us to simply change it in one place should we - * decide to modify at a later date. - * - * @enum {string} - * @private - */ -MaterialButton.prototype.CssClasses_ = { - RIPPLE_EFFECT: 'mdl-js-ripple-effect', - RIPPLE_CONTAINER: 'mdl-button__ripple-container', - RIPPLE: 'mdl-ripple' -}; -/** - * Handle blur of element. - * - * @param {Event} event The event that fired. - * @private - */ -MaterialButton.prototype.blurHandler_ = function (event) { - if (event) { - this.element_.blur(); - } -}; -// Public methods. -/** - * Disable button. - * - * @public - */ -MaterialButton.prototype.disable = function () { - this.element_.disabled = true; -}; -MaterialButton.prototype['disable'] = MaterialButton.prototype.disable; -/** - * Enable button. - * - * @public - */ -MaterialButton.prototype.enable = function () { - this.element_.disabled = false; -}; -MaterialButton.prototype['enable'] = MaterialButton.prototype.enable; -/** - * Initialize element. - */ -MaterialButton.prototype.init = function () { - if (this.element_) { - if (this.element_.classList.contains(this.CssClasses_.RIPPLE_EFFECT)) { - var rippleContainer = document.createElement('span'); - rippleContainer.classList.add(this.CssClasses_.RIPPLE_CONTAINER); - this.rippleElement_ = document.createElement('span'); - this.rippleElement_.classList.add(this.CssClasses_.RIPPLE); - rippleContainer.appendChild(this.rippleElement_); - this.boundRippleBlurHandler = this.blurHandler_.bind(this); - this.rippleElement_.addEventListener('mouseup', this.boundRippleBlurHandler); - this.element_.appendChild(rippleContainer); - } - this.boundButtonBlurHandler = this.blurHandler_.bind(this); - this.element_.addEventListener('mouseup', this.boundButtonBlurHandler); - this.element_.addEventListener('mouseleave', this.boundButtonBlurHandler); - } -}; -/** - * Downgrade the element. - * - * @private - */ -MaterialButton.prototype.mdlDowngrade_ = function () { - if (this.rippleElement_) { - this.rippleElement_.removeEventListener('mouseup', this.boundRippleBlurHandler); - } - this.element_.removeEventListener('mouseup', this.boundButtonBlurHandler); - this.element_.removeEventListener('mouseleave', this.boundButtonBlurHandler); -}; -/** - * Public alias for the downgrade method. - * - * @public - */ -MaterialButton.prototype.mdlDowngrade = MaterialButton.prototype.mdlDowngrade_; -MaterialButton.prototype['mdlDowngrade'] = MaterialButton.prototype.mdlDowngrade; -// The component registers itself. It can assume componentHandler is available -// in the global scope. -componentHandler.register({ - constructor: MaterialButton, - classAsString: 'MaterialButton', - cssClass: 'mdl-js-button', - widget: true -}); -/** - * @license - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/** - * Class constructor for Checkbox MDL component. - * Implements MDL component design pattern defined at: - * https://github.com/jasonmayes/mdl-component-design-pattern - * - * @constructor - * @param {HTMLElement} element The element that will be upgraded. - */ -var MaterialCheckbox = function MaterialCheckbox(element) { - this.element_ = element; - // Initialize instance. - this.init(); -}; -window['MaterialCheckbox'] = MaterialCheckbox; -/** - * Store constants in one place so they can be updated easily. - * - * @enum {string | number} - * @private - */ -MaterialCheckbox.prototype.Constant_ = { TINY_TIMEOUT: 0.001 }; -/** - * Store strings for class names defined by this component that are used in - * JavaScript. This allows us to simply change it in one place should we - * decide to modify at a later date. - * - * @enum {string} - * @private - */ -MaterialCheckbox.prototype.CssClasses_ = { - INPUT: 'mdl-checkbox__input', - BOX_OUTLINE: 'mdl-checkbox__box-outline', - FOCUS_HELPER: 'mdl-checkbox__focus-helper', - TICK_OUTLINE: 'mdl-checkbox__tick-outline', - RIPPLE_EFFECT: 'mdl-js-ripple-effect', - RIPPLE_IGNORE_EVENTS: 'mdl-js-ripple-effect--ignore-events', - RIPPLE_CONTAINER: 'mdl-checkbox__ripple-container', - RIPPLE_CENTER: 'mdl-ripple--center', - RIPPLE: 'mdl-ripple', - IS_FOCUSED: 'is-focused', - IS_DISABLED: 'is-disabled', - IS_CHECKED: 'is-checked', - IS_UPGRADED: 'is-upgraded' -}; -/** - * Handle change of state. - * - * @param {Event} event The event that fired. - * @private - */ -MaterialCheckbox.prototype.onChange_ = function (event) { - this.updateClasses_(); -}; -/** - * Handle focus of element. - * - * @param {Event} event The event that fired. - * @private - */ -MaterialCheckbox.prototype.onFocus_ = function (event) { - this.element_.classList.add(this.CssClasses_.IS_FOCUSED); -}; -/** - * Handle lost focus of element. - * - * @param {Event} event The event that fired. - * @private - */ -MaterialCheckbox.prototype.onBlur_ = function (event) { - this.element_.classList.remove(this.CssClasses_.IS_FOCUSED); -}; -/** - * Handle mouseup. - * - * @param {Event} event The event that fired. - * @private - */ -MaterialCheckbox.prototype.onMouseUp_ = function (event) { - this.blur_(); -}; -/** - * Handle class updates. - * - * @private - */ -MaterialCheckbox.prototype.updateClasses_ = function () { - this.checkDisabled(); - this.checkToggleState(); -}; -/** - * Add blur. - * - * @private - */ -MaterialCheckbox.prototype.blur_ = function () { - // TODO: figure out why there's a focus event being fired after our blur, - // so that we can avoid this hack. - window.setTimeout(function () { - this.inputElement_.blur(); - }.bind(this), this.Constant_.TINY_TIMEOUT); -}; -// Public methods. -/** - * Check the inputs toggle state and update display. - * - * @public - */ -MaterialCheckbox.prototype.checkToggleState = function () { - if (this.inputElement_.checked) { - this.element_.classList.add(this.CssClasses_.IS_CHECKED); - } else { - this.element_.classList.remove(this.CssClasses_.IS_CHECKED); - } -}; -MaterialCheckbox.prototype['checkToggleState'] = MaterialCheckbox.prototype.checkToggleState; -/** - * Check the inputs disabled state and update display. - * - * @public - */ -MaterialCheckbox.prototype.checkDisabled = function () { - if (this.inputElement_.disabled) { - this.element_.classList.add(this.CssClasses_.IS_DISABLED); - } else { - this.element_.classList.remove(this.CssClasses_.IS_DISABLED); - } -}; -MaterialCheckbox.prototype['checkDisabled'] = MaterialCheckbox.prototype.checkDisabled; -/** - * Disable checkbox. - * - * @public - */ -MaterialCheckbox.prototype.disable = function () { - this.inputElement_.disabled = true; - this.updateClasses_(); -}; -MaterialCheckbox.prototype['disable'] = MaterialCheckbox.prototype.disable; -/** - * Enable checkbox. - * - * @public - */ -MaterialCheckbox.prototype.enable = function () { - this.inputElement_.disabled = false; - this.updateClasses_(); -}; -MaterialCheckbox.prototype['enable'] = MaterialCheckbox.prototype.enable; -/** - * Check checkbox. - * - * @public - */ -MaterialCheckbox.prototype.check = function () { - this.inputElement_.checked = true; - this.updateClasses_(); -}; -MaterialCheckbox.prototype['check'] = MaterialCheckbox.prototype.check; -/** - * Uncheck checkbox. - * - * @public - */ -MaterialCheckbox.prototype.uncheck = function () { - this.inputElement_.checked = false; - this.updateClasses_(); -}; -MaterialCheckbox.prototype['uncheck'] = MaterialCheckbox.prototype.uncheck; -/** - * Initialize element. - */ -MaterialCheckbox.prototype.init = function () { - if (this.element_) { - this.inputElement_ = this.element_.querySelector('.' + this.CssClasses_.INPUT); - var boxOutline = document.createElement('span'); - boxOutline.classList.add(this.CssClasses_.BOX_OUTLINE); - var tickContainer = document.createElement('span'); - tickContainer.classList.add(this.CssClasses_.FOCUS_HELPER); - var tickOutline = document.createElement('span'); - tickOutline.classList.add(this.CssClasses_.TICK_OUTLINE); - boxOutline.appendChild(tickOutline); - this.element_.appendChild(tickContainer); - this.element_.appendChild(boxOutline); - if (this.element_.classList.contains(this.CssClasses_.RIPPLE_EFFECT)) { - this.element_.classList.add(this.CssClasses_.RIPPLE_IGNORE_EVENTS); - this.rippleContainerElement_ = document.createElement('span'); - this.rippleContainerElement_.classList.add(this.CssClasses_.RIPPLE_CONTAINER); - this.rippleContainerElement_.classList.add(this.CssClasses_.RIPPLE_EFFECT); - this.rippleContainerElement_.classList.add(this.CssClasses_.RIPPLE_CENTER); - this.boundRippleMouseUp = this.onMouseUp_.bind(this); - this.rippleContainerElement_.addEventListener('mouseup', this.boundRippleMouseUp); - var ripple = document.createElement('span'); - ripple.classList.add(this.CssClasses_.RIPPLE); - this.rippleContainerElement_.appendChild(ripple); - this.element_.appendChild(this.rippleContainerElement_); - } - this.boundInputOnChange = this.onChange_.bind(this); - this.boundInputOnFocus = this.onFocus_.bind(this); - this.boundInputOnBlur = this.onBlur_.bind(this); - this.boundElementMouseUp = this.onMouseUp_.bind(this); - this.inputElement_.addEventListener('change', this.boundInputOnChange); - this.inputElement_.addEventListener('focus', this.boundInputOnFocus); - this.inputElement_.addEventListener('blur', this.boundInputOnBlur); - this.element_.addEventListener('mouseup', this.boundElementMouseUp); - this.updateClasses_(); - this.element_.classList.add(this.CssClasses_.IS_UPGRADED); - } -}; -/** - * Downgrade the component. - * - * @private - */ -MaterialCheckbox.prototype.mdlDowngrade_ = function () { - if (this.rippleContainerElement_) { - this.rippleContainerElement_.removeEventListener('mouseup', this.boundRippleMouseUp); - } - this.inputElement_.removeEventListener('change', this.boundInputOnChange); - this.inputElement_.removeEventListener('focus', this.boundInputOnFocus); - this.inputElement_.removeEventListener('blur', this.boundInputOnBlur); - this.element_.removeEventListener('mouseup', this.boundElementMouseUp); -}; -/** - * Public alias for the downgrade method. - * - * @public - */ -MaterialCheckbox.prototype.mdlDowngrade = MaterialCheckbox.prototype.mdlDowngrade_; -MaterialCheckbox.prototype['mdlDowngrade'] = MaterialCheckbox.prototype.mdlDowngrade; -// The component registers itself. It can assume componentHandler is available -// in the global scope. -componentHandler.register({ - constructor: MaterialCheckbox, - classAsString: 'MaterialCheckbox', - cssClass: 'mdl-js-checkbox', - widget: true -}); -/** - * @license - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/** - * Class constructor for icon toggle MDL component. - * Implements MDL component design pattern defined at: - * https://github.com/jasonmayes/mdl-component-design-pattern - * - * @constructor - * @param {HTMLElement} element The element that will be upgraded. - */ -var MaterialIconToggle = function MaterialIconToggle(element) { - this.element_ = element; - // Initialize instance. - this.init(); -}; -window['MaterialIconToggle'] = MaterialIconToggle; -/** - * Store constants in one place so they can be updated easily. - * - * @enum {string | number} - * @private - */ -MaterialIconToggle.prototype.Constant_ = { TINY_TIMEOUT: 0.001 }; -/** - * Store strings for class names defined by this component that are used in - * JavaScript. This allows us to simply change it in one place should we - * decide to modify at a later date. - * - * @enum {string} - * @private - */ -MaterialIconToggle.prototype.CssClasses_ = { - INPUT: 'mdl-icon-toggle__input', - JS_RIPPLE_EFFECT: 'mdl-js-ripple-effect', - RIPPLE_IGNORE_EVENTS: 'mdl-js-ripple-effect--ignore-events', - RIPPLE_CONTAINER: 'mdl-icon-toggle__ripple-container', - RIPPLE_CENTER: 'mdl-ripple--center', - RIPPLE: 'mdl-ripple', - IS_FOCUSED: 'is-focused', - IS_DISABLED: 'is-disabled', - IS_CHECKED: 'is-checked' -}; -/** - * Handle change of state. - * - * @param {Event} event The event that fired. - * @private - */ -MaterialIconToggle.prototype.onChange_ = function (event) { - this.updateClasses_(); -}; -/** - * Handle focus of element. - * - * @param {Event} event The event that fired. - * @private - */ -MaterialIconToggle.prototype.onFocus_ = function (event) { - this.element_.classList.add(this.CssClasses_.IS_FOCUSED); -}; -/** - * Handle lost focus of element. - * - * @param {Event} event The event that fired. - * @private - */ -MaterialIconToggle.prototype.onBlur_ = function (event) { - this.element_.classList.remove(this.CssClasses_.IS_FOCUSED); -}; -/** - * Handle mouseup. - * - * @param {Event} event The event that fired. - * @private - */ -MaterialIconToggle.prototype.onMouseUp_ = function (event) { - this.blur_(); -}; -/** - * Handle class updates. - * - * @private - */ -MaterialIconToggle.prototype.updateClasses_ = function () { - this.checkDisabled(); - this.checkToggleState(); -}; -/** - * Add blur. - * - * @private - */ -MaterialIconToggle.prototype.blur_ = function () { - // TODO: figure out why there's a focus event being fired after our blur, - // so that we can avoid this hack. - window.setTimeout(function () { - this.inputElement_.blur(); - }.bind(this), this.Constant_.TINY_TIMEOUT); -}; -// Public methods. -/** - * Check the inputs toggle state and update display. - * - * @public - */ -MaterialIconToggle.prototype.checkToggleState = function () { - if (this.inputElement_.checked) { - this.element_.classList.add(this.CssClasses_.IS_CHECKED); - } else { - this.element_.classList.remove(this.CssClasses_.IS_CHECKED); - } -}; -MaterialIconToggle.prototype['checkToggleState'] = MaterialIconToggle.prototype.checkToggleState; -/** - * Check the inputs disabled state and update display. - * - * @public - */ -MaterialIconToggle.prototype.checkDisabled = function () { - if (this.inputElement_.disabled) { - this.element_.classList.add(this.CssClasses_.IS_DISABLED); - } else { - this.element_.classList.remove(this.CssClasses_.IS_DISABLED); - } -}; -MaterialIconToggle.prototype['checkDisabled'] = MaterialIconToggle.prototype.checkDisabled; -/** - * Disable icon toggle. - * - * @public - */ -MaterialIconToggle.prototype.disable = function () { - this.inputElement_.disabled = true; - this.updateClasses_(); -}; -MaterialIconToggle.prototype['disable'] = MaterialIconToggle.prototype.disable; -/** - * Enable icon toggle. - * - * @public - */ -MaterialIconToggle.prototype.enable = function () { - this.inputElement_.disabled = false; - this.updateClasses_(); -}; -MaterialIconToggle.prototype['enable'] = MaterialIconToggle.prototype.enable; -/** - * Check icon toggle. - * - * @public - */ -MaterialIconToggle.prototype.check = function () { - this.inputElement_.checked = true; - this.updateClasses_(); -}; -MaterialIconToggle.prototype['check'] = MaterialIconToggle.prototype.check; -/** - * Uncheck icon toggle. - * - * @public - */ -MaterialIconToggle.prototype.uncheck = function () { - this.inputElement_.checked = false; - this.updateClasses_(); -}; -MaterialIconToggle.prototype['uncheck'] = MaterialIconToggle.prototype.uncheck; -/** - * Initialize element. - */ -MaterialIconToggle.prototype.init = function () { - if (this.element_) { - this.inputElement_ = this.element_.querySelector('.' + this.CssClasses_.INPUT); - if (this.element_.classList.contains(this.CssClasses_.JS_RIPPLE_EFFECT)) { - this.element_.classList.add(this.CssClasses_.RIPPLE_IGNORE_EVENTS); - this.rippleContainerElement_ = document.createElement('span'); - this.rippleContainerElement_.classList.add(this.CssClasses_.RIPPLE_CONTAINER); - this.rippleContainerElement_.classList.add(this.CssClasses_.JS_RIPPLE_EFFECT); - this.rippleContainerElement_.classList.add(this.CssClasses_.RIPPLE_CENTER); - this.boundRippleMouseUp = this.onMouseUp_.bind(this); - this.rippleContainerElement_.addEventListener('mouseup', this.boundRippleMouseUp); - var ripple = document.createElement('span'); - ripple.classList.add(this.CssClasses_.RIPPLE); - this.rippleContainerElement_.appendChild(ripple); - this.element_.appendChild(this.rippleContainerElement_); - } - this.boundInputOnChange = this.onChange_.bind(this); - this.boundInputOnFocus = this.onFocus_.bind(this); - this.boundInputOnBlur = this.onBlur_.bind(this); - this.boundElementOnMouseUp = this.onMouseUp_.bind(this); - this.inputElement_.addEventListener('change', this.boundInputOnChange); - this.inputElement_.addEventListener('focus', this.boundInputOnFocus); - this.inputElement_.addEventListener('blur', this.boundInputOnBlur); - this.element_.addEventListener('mouseup', this.boundElementOnMouseUp); - this.updateClasses_(); - this.element_.classList.add('is-upgraded'); - } -}; -/** - * Downgrade the component - * - * @private - */ -MaterialIconToggle.prototype.mdlDowngrade_ = function () { - if (this.rippleContainerElement_) { - this.rippleContainerElement_.removeEventListener('mouseup', this.boundRippleMouseUp); - } - this.inputElement_.removeEventListener('change', this.boundInputOnChange); - this.inputElement_.removeEventListener('focus', this.boundInputOnFocus); - this.inputElement_.removeEventListener('blur', this.boundInputOnBlur); - this.element_.removeEventListener('mouseup', this.boundElementOnMouseUp); -}; -/** - * Public alias for the downgrade method. - * - * @public - */ -MaterialIconToggle.prototype.mdlDowngrade = MaterialIconToggle.prototype.mdlDowngrade_; -MaterialIconToggle.prototype['mdlDowngrade'] = MaterialIconToggle.prototype.mdlDowngrade; -// The component registers itself. It can assume componentHandler is available -// in the global scope. -componentHandler.register({ - constructor: MaterialIconToggle, - classAsString: 'MaterialIconToggle', - cssClass: 'mdl-js-icon-toggle', - widget: true -}); -/** - * @license - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/** - * Class constructor for dropdown MDL component. - * Implements MDL component design pattern defined at: - * https://github.com/jasonmayes/mdl-component-design-pattern - * - * @constructor - * @param {HTMLElement} element The element that will be upgraded. - */ -var MaterialMenu = function MaterialMenu(element) { - this.element_ = element; - // Initialize instance. - this.init(); -}; -window['MaterialMenu'] = MaterialMenu; -/** - * Store constants in one place so they can be updated easily. - * - * @enum {string | number} - * @private - */ -MaterialMenu.prototype.Constant_ = { - // Total duration of the menu animation. - TRANSITION_DURATION_SECONDS: 0.3, - // The fraction of the total duration we want to use for menu item animations. - TRANSITION_DURATION_FRACTION: 0.8, - // How long the menu stays open after choosing an option (so the user can see - // the ripple). - CLOSE_TIMEOUT: 150 -}; -/** - * Keycodes, for code readability. - * - * @enum {number} - * @private - */ -MaterialMenu.prototype.Keycodes_ = { - ENTER: 13, - ESCAPE: 27, - SPACE: 32, - UP_ARROW: 38, - DOWN_ARROW: 40 -}; -/** - * Store strings for class names defined by this component that are used in - * JavaScript. This allows us to simply change it in one place should we - * decide to modify at a later date. - * - * @enum {string} - * @private - */ -MaterialMenu.prototype.CssClasses_ = { - CONTAINER: 'mdl-menu__container', - OUTLINE: 'mdl-menu__outline', - ITEM: 'mdl-menu__item', - ITEM_RIPPLE_CONTAINER: 'mdl-menu__item-ripple-container', - RIPPLE_EFFECT: 'mdl-js-ripple-effect', - RIPPLE_IGNORE_EVENTS: 'mdl-js-ripple-effect--ignore-events', - RIPPLE: 'mdl-ripple', - // Statuses - IS_UPGRADED: 'is-upgraded', - IS_VISIBLE: 'is-visible', - IS_ANIMATING: 'is-animating', - // Alignment options - BOTTOM_LEFT: 'mdl-menu--bottom-left', - // This is the default. - BOTTOM_RIGHT: 'mdl-menu--bottom-right', - TOP_LEFT: 'mdl-menu--top-left', - TOP_RIGHT: 'mdl-menu--top-right', - UNALIGNED: 'mdl-menu--unaligned' -}; -/** - * Initialize element. - */ -MaterialMenu.prototype.init = function () { - if (this.element_) { - // Create container for the menu. - var container = document.createElement('div'); - container.classList.add(this.CssClasses_.CONTAINER); - this.element_.parentElement.insertBefore(container, this.element_); - this.element_.parentElement.removeChild(this.element_); - container.appendChild(this.element_); - this.container_ = container; - // Create outline for the menu (shadow and background). - var outline = document.createElement('div'); - outline.classList.add(this.CssClasses_.OUTLINE); - this.outline_ = outline; - container.insertBefore(outline, this.element_); - // Find the "for" element and bind events to it. - var forElId = this.element_.getAttribute('for'); - var forEl = null; - if (forElId) { - forEl = document.getElementById(forElId); - if (forEl) { - this.forElement_ = forEl; - forEl.addEventListener('click', this.handleForClick_.bind(this)); - forEl.addEventListener('keydown', this.handleForKeyboardEvent_.bind(this)); - } - } - var items = this.element_.querySelectorAll('.' + this.CssClasses_.ITEM); - this.boundItemKeydown_ = this.handleItemKeyboardEvent_.bind(this); - this.boundItemClick_ = this.handleItemClick_.bind(this); - for (var i = 0; i < items.length; i++) { - // Add a listener to each menu item. - items[i].addEventListener('click', this.boundItemClick_); - // Add a tab index to each menu item. - items[i].tabIndex = '-1'; - // Add a keyboard listener to each menu item. - items[i].addEventListener('keydown', this.boundItemKeydown_); - } - // Add ripple classes to each item, if the user has enabled ripples. - if (this.element_.classList.contains(this.CssClasses_.RIPPLE_EFFECT)) { - this.element_.classList.add(this.CssClasses_.RIPPLE_IGNORE_EVENTS); - for (i = 0; i < items.length; i++) { - var item = items[i]; - var rippleContainer = document.createElement('span'); - rippleContainer.classList.add(this.CssClasses_.ITEM_RIPPLE_CONTAINER); - var ripple = document.createElement('span'); - ripple.classList.add(this.CssClasses_.RIPPLE); - rippleContainer.appendChild(ripple); - item.appendChild(rippleContainer); - item.classList.add(this.CssClasses_.RIPPLE_EFFECT); - } - } - // Copy alignment classes to the container, so the outline can use them. - if (this.element_.classList.contains(this.CssClasses_.BOTTOM_LEFT)) { - this.outline_.classList.add(this.CssClasses_.BOTTOM_LEFT); - } - if (this.element_.classList.contains(this.CssClasses_.BOTTOM_RIGHT)) { - this.outline_.classList.add(this.CssClasses_.BOTTOM_RIGHT); - } - if (this.element_.classList.contains(this.CssClasses_.TOP_LEFT)) { - this.outline_.classList.add(this.CssClasses_.TOP_LEFT); - } - if (this.element_.classList.contains(this.CssClasses_.TOP_RIGHT)) { - this.outline_.classList.add(this.CssClasses_.TOP_RIGHT); - } - if (this.element_.classList.contains(this.CssClasses_.UNALIGNED)) { - this.outline_.classList.add(this.CssClasses_.UNALIGNED); - } - container.classList.add(this.CssClasses_.IS_UPGRADED); - } -}; -/** - * Handles a click on the "for" element, by positioning the menu and then - * toggling it. - * - * @param {Event} evt The event that fired. - * @private - */ -MaterialMenu.prototype.handleForClick_ = function (evt) { - if (this.element_ && this.forElement_) { - var rect = this.forElement_.getBoundingClientRect(); - var forRect = this.forElement_.parentElement.getBoundingClientRect(); - if (this.element_.classList.contains(this.CssClasses_.UNALIGNED)) { - } else if (this.element_.classList.contains(this.CssClasses_.BOTTOM_RIGHT)) { - // Position below the "for" element, aligned to its right. - this.container_.style.right = forRect.right - rect.right + 'px'; - this.container_.style.top = this.forElement_.offsetTop + this.forElement_.offsetHeight + 'px'; - } else if (this.element_.classList.contains(this.CssClasses_.TOP_LEFT)) { - // Position above the "for" element, aligned to its left. - this.container_.style.left = this.forElement_.offsetLeft + 'px'; - this.container_.style.bottom = forRect.bottom - rect.top + 'px'; - } else if (this.element_.classList.contains(this.CssClasses_.TOP_RIGHT)) { - // Position above the "for" element, aligned to its right. - this.container_.style.right = forRect.right - rect.right + 'px'; - this.container_.style.bottom = forRect.bottom - rect.top + 'px'; - } else { - // Default: position below the "for" element, aligned to its left. - this.container_.style.left = this.forElement_.offsetLeft + 'px'; - this.container_.style.top = this.forElement_.offsetTop + this.forElement_.offsetHeight + 'px'; - } - } - this.toggle(evt); -}; -/** - * Handles a keyboard event on the "for" element. - * - * @param {Event} evt The event that fired. - * @private - */ -MaterialMenu.prototype.handleForKeyboardEvent_ = function (evt) { - if (this.element_ && this.container_ && this.forElement_) { - var items = this.element_.querySelectorAll('.' + this.CssClasses_.ITEM + ':not([disabled])'); - if (items && items.length > 0 && this.container_.classList.contains(this.CssClasses_.IS_VISIBLE)) { - if (evt.keyCode === this.Keycodes_.UP_ARROW) { - evt.preventDefault(); - items[items.length - 1].focus(); - } else if (evt.keyCode === this.Keycodes_.DOWN_ARROW) { - evt.preventDefault(); - items[0].focus(); - } - } - } -}; -/** - * Handles a keyboard event on an item. - * - * @param {Event} evt The event that fired. - * @private - */ -MaterialMenu.prototype.handleItemKeyboardEvent_ = function (evt) { - if (this.element_ && this.container_) { - var items = this.element_.querySelectorAll('.' + this.CssClasses_.ITEM + ':not([disabled])'); - if (items && items.length > 0 && this.container_.classList.contains(this.CssClasses_.IS_VISIBLE)) { - var currentIndex = Array.prototype.slice.call(items).indexOf(evt.target); - if (evt.keyCode === this.Keycodes_.UP_ARROW) { - evt.preventDefault(); - if (currentIndex > 0) { - items[currentIndex - 1].focus(); - } else { - items[items.length - 1].focus(); - } - } else if (evt.keyCode === this.Keycodes_.DOWN_ARROW) { - evt.preventDefault(); - if (items.length > currentIndex + 1) { - items[currentIndex + 1].focus(); - } else { - items[0].focus(); - } - } else if (evt.keyCode === this.Keycodes_.SPACE || evt.keyCode === this.Keycodes_.ENTER) { - evt.preventDefault(); - // Send mousedown and mouseup to trigger ripple. - var e = new MouseEvent('mousedown'); - evt.target.dispatchEvent(e); - e = new MouseEvent('mouseup'); - evt.target.dispatchEvent(e); - // Send click. - evt.target.click(); - } else if (evt.keyCode === this.Keycodes_.ESCAPE) { - evt.preventDefault(); - this.hide(); - } - } - } -}; -/** - * Handles a click event on an item. - * - * @param {Event} evt The event that fired. - * @private - */ -MaterialMenu.prototype.handleItemClick_ = function (evt) { - if (evt.target.hasAttribute('disabled')) { - evt.stopPropagation(); - } else { - // Wait some time before closing menu, so the user can see the ripple. - this.closing_ = true; - window.setTimeout(function (evt) { - this.hide(); - this.closing_ = false; - }.bind(this), this.Constant_.CLOSE_TIMEOUT); - } -}; -/** - * Calculates the initial clip (for opening the menu) or final clip (for closing - * it), and applies it. This allows us to animate from or to the correct point, - * that is, the point it's aligned to in the "for" element. - * - * @param {number} height Height of the clip rectangle - * @param {number} width Width of the clip rectangle - * @private - */ -MaterialMenu.prototype.applyClip_ = function (height, width) { - if (this.element_.classList.contains(this.CssClasses_.UNALIGNED)) { - // Do not clip. - this.element_.style.clip = ''; - } else if (this.element_.classList.contains(this.CssClasses_.BOTTOM_RIGHT)) { - // Clip to the top right corner of the menu. - this.element_.style.clip = 'rect(0 ' + width + 'px ' + '0 ' + width + 'px)'; - } else if (this.element_.classList.contains(this.CssClasses_.TOP_LEFT)) { - // Clip to the bottom left corner of the menu. - this.element_.style.clip = 'rect(' + height + 'px 0 ' + height + 'px 0)'; - } else if (this.element_.classList.contains(this.CssClasses_.TOP_RIGHT)) { - // Clip to the bottom right corner of the menu. - this.element_.style.clip = 'rect(' + height + 'px ' + width + 'px ' + height + 'px ' + width + 'px)'; - } else { - // Default: do not clip (same as clipping to the top left corner). - this.element_.style.clip = ''; - } -}; -/** - * Adds an event listener to clean up after the animation ends. - * - * @private - */ -MaterialMenu.prototype.addAnimationEndListener_ = function () { - var cleanup = function () { - this.element_.removeEventListener('transitionend', cleanup); - this.element_.removeEventListener('webkitTransitionEnd', cleanup); - this.element_.classList.remove(this.CssClasses_.IS_ANIMATING); - }.bind(this); - // Remove animation class once the transition is done. - this.element_.addEventListener('transitionend', cleanup); - this.element_.addEventListener('webkitTransitionEnd', cleanup); -}; -/** - * Displays the menu. - * - * @public - */ -MaterialMenu.prototype.show = function (evt) { - if (this.element_ && this.container_ && this.outline_) { - // Measure the inner element. - var height = this.element_.getBoundingClientRect().height; - var width = this.element_.getBoundingClientRect().width; - // Apply the inner element's size to the container and outline. - this.container_.style.width = width + 'px'; - this.container_.style.height = height + 'px'; - this.outline_.style.width = width + 'px'; - this.outline_.style.height = height + 'px'; - var transitionDuration = this.Constant_.TRANSITION_DURATION_SECONDS * this.Constant_.TRANSITION_DURATION_FRACTION; - // Calculate transition delays for individual menu items, so that they fade - // in one at a time. - var items = this.element_.querySelectorAll('.' + this.CssClasses_.ITEM); - for (var i = 0; i < items.length; i++) { - var itemDelay = null; - if (this.element_.classList.contains(this.CssClasses_.TOP_LEFT) || this.element_.classList.contains(this.CssClasses_.TOP_RIGHT)) { - itemDelay = (height - items[i].offsetTop - items[i].offsetHeight) / height * transitionDuration + 's'; - } else { - itemDelay = items[i].offsetTop / height * transitionDuration + 's'; - } - items[i].style.transitionDelay = itemDelay; - } - // Apply the initial clip to the text before we start animating. - this.applyClip_(height, width); - // Wait for the next frame, turn on animation, and apply the final clip. - // Also make it visible. This triggers the transitions. - window.requestAnimationFrame(function () { - this.element_.classList.add(this.CssClasses_.IS_ANIMATING); - this.element_.style.clip = 'rect(0 ' + width + 'px ' + height + 'px 0)'; - this.container_.classList.add(this.CssClasses_.IS_VISIBLE); - }.bind(this)); - // Clean up after the animation is complete. - this.addAnimationEndListener_(); - // Add a click listener to the document, to close the menu. - var callback = function (e) { - // Check to see if the document is processing the same event that - // displayed the menu in the first place. If so, do nothing. - // Also check to see if the menu is in the process of closing itself, and - // do nothing in that case. - // Also check if the clicked element is a menu item - // if so, do nothing. - if (e !== evt && !this.closing_ && e.target.parentNode !== this.element_) { - document.removeEventListener('click', callback); - this.hide(); - } - }.bind(this); - document.addEventListener('click', callback); - } -}; -MaterialMenu.prototype['show'] = MaterialMenu.prototype.show; -/** - * Hides the menu. - * - * @public - */ -MaterialMenu.prototype.hide = function () { - if (this.element_ && this.container_ && this.outline_) { - var items = this.element_.querySelectorAll('.' + this.CssClasses_.ITEM); - // Remove all transition delays; menu items fade out concurrently. - for (var i = 0; i < items.length; i++) { - items[i].style.transitionDelay = null; - } - // Measure the inner element. - var rect = this.element_.getBoundingClientRect(); - var height = rect.height; - var width = rect.width; - // Turn on animation, and apply the final clip. Also make invisible. - // This triggers the transitions. - this.element_.classList.add(this.CssClasses_.IS_ANIMATING); - this.applyClip_(height, width); - this.container_.classList.remove(this.CssClasses_.IS_VISIBLE); - // Clean up after the animation is complete. - this.addAnimationEndListener_(); - } -}; -MaterialMenu.prototype['hide'] = MaterialMenu.prototype.hide; -/** - * Displays or hides the menu, depending on current state. - * - * @public - */ -MaterialMenu.prototype.toggle = function (evt) { - if (this.container_.classList.contains(this.CssClasses_.IS_VISIBLE)) { - this.hide(); - } else { - this.show(evt); - } -}; -MaterialMenu.prototype['toggle'] = MaterialMenu.prototype.toggle; -/** - * Downgrade the component. - * - * @private - */ -MaterialMenu.prototype.mdlDowngrade_ = function () { - var items = this.element_.querySelectorAll('.' + this.CssClasses_.ITEM); - for (var i = 0; i < items.length; i++) { - items[i].removeEventListener('click', this.boundItemClick_); - items[i].removeEventListener('keydown', this.boundItemKeydown_); - } -}; -/** - * Public alias for the downgrade method. - * - * @public - */ -MaterialMenu.prototype.mdlDowngrade = MaterialMenu.prototype.mdlDowngrade_; -MaterialMenu.prototype['mdlDowngrade'] = MaterialMenu.prototype.mdlDowngrade; -// The component registers itself. It can assume componentHandler is available -// in the global scope. -componentHandler.register({ - constructor: MaterialMenu, - classAsString: 'MaterialMenu', - cssClass: 'mdl-js-menu', - widget: true -}); -/** - * @license - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/** - * Class constructor for Progress MDL component. - * Implements MDL component design pattern defined at: - * https://github.com/jasonmayes/mdl-component-design-pattern - * - * @constructor - * @param {HTMLElement} element The element that will be upgraded. - */ -var MaterialProgress = function MaterialProgress(element) { - this.element_ = element; - // Initialize instance. - this.init(); -}; -window['MaterialProgress'] = MaterialProgress; -/** - * Store constants in one place so they can be updated easily. - * - * @enum {string | number} - * @private - */ -MaterialProgress.prototype.Constant_ = {}; -/** - * Store strings for class names defined by this component that are used in - * JavaScript. This allows us to simply change it in one place should we - * decide to modify at a later date. - * - * @enum {string} - * @private - */ -MaterialProgress.prototype.CssClasses_ = { INDETERMINATE_CLASS: 'mdl-progress__indeterminate' }; -/** - * Set the current progress of the progressbar. - * - * @param {number} p Percentage of the progress (0-100) - * @public - */ -MaterialProgress.prototype.setProgress = function (p) { - if (this.element_.classList.contains(this.CssClasses_.INDETERMINATE_CLASS)) { - return; - } - this.progressbar_.style.width = p + '%'; -}; -MaterialProgress.prototype['setProgress'] = MaterialProgress.prototype.setProgress; -/** - * Set the current progress of the buffer. - * - * @param {number} p Percentage of the buffer (0-100) - * @public - */ -MaterialProgress.prototype.setBuffer = function (p) { - this.bufferbar_.style.width = p + '%'; - this.auxbar_.style.width = 100 - p + '%'; -}; -MaterialProgress.prototype['setBuffer'] = MaterialProgress.prototype.setBuffer; -/** - * Initialize element. - */ -MaterialProgress.prototype.init = function () { - if (this.element_) { - var el = document.createElement('div'); - el.className = 'progressbar bar bar1'; - this.element_.appendChild(el); - this.progressbar_ = el; - el = document.createElement('div'); - el.className = 'bufferbar bar bar2'; - this.element_.appendChild(el); - this.bufferbar_ = el; - el = document.createElement('div'); - el.className = 'auxbar bar bar3'; - this.element_.appendChild(el); - this.auxbar_ = el; - this.progressbar_.style.width = '0%'; - this.bufferbar_.style.width = '100%'; - this.auxbar_.style.width = '0%'; - this.element_.classList.add('is-upgraded'); - } -}; -/** - * Downgrade the component - * - * @private - */ -MaterialProgress.prototype.mdlDowngrade_ = function () { - while (this.element_.firstChild) { - this.element_.removeChild(this.element_.firstChild); - } -}; -/** - * Public alias for the downgrade method. - * - * @public - */ -MaterialProgress.prototype.mdlDowngrade = MaterialProgress.prototype.mdlDowngrade_; -MaterialProgress.prototype['mdlDowngrade'] = MaterialProgress.prototype.mdlDowngrade; -// The component registers itself. It can assume componentHandler is available -// in the global scope. -componentHandler.register({ - constructor: MaterialProgress, - classAsString: 'MaterialProgress', - cssClass: 'mdl-js-progress', - widget: true -}); -/** - * @license - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/** - * Class constructor for Radio MDL component. - * Implements MDL component design pattern defined at: - * https://github.com/jasonmayes/mdl-component-design-pattern - * - * @constructor - * @param {HTMLElement} element The element that will be upgraded. - */ -var MaterialRadio = function MaterialRadio(element) { - this.element_ = element; - // Initialize instance. - this.init(); -}; -window['MaterialRadio'] = MaterialRadio; -/** - * Store constants in one place so they can be updated easily. - * - * @enum {string | number} - * @private - */ -MaterialRadio.prototype.Constant_ = { TINY_TIMEOUT: 0.001 }; -/** - * Store strings for class names defined by this component that are used in - * JavaScript. This allows us to simply change it in one place should we - * decide to modify at a later date. - * - * @enum {string} - * @private - */ -MaterialRadio.prototype.CssClasses_ = { - IS_FOCUSED: 'is-focused', - IS_DISABLED: 'is-disabled', - IS_CHECKED: 'is-checked', - IS_UPGRADED: 'is-upgraded', - JS_RADIO: 'mdl-js-radio', - RADIO_BTN: 'mdl-radio__button', - RADIO_OUTER_CIRCLE: 'mdl-radio__outer-circle', - RADIO_INNER_CIRCLE: 'mdl-radio__inner-circle', - RIPPLE_EFFECT: 'mdl-js-ripple-effect', - RIPPLE_IGNORE_EVENTS: 'mdl-js-ripple-effect--ignore-events', - RIPPLE_CONTAINER: 'mdl-radio__ripple-container', - RIPPLE_CENTER: 'mdl-ripple--center', - RIPPLE: 'mdl-ripple' -}; -/** - * Handle change of state. - * - * @param {Event} event The event that fired. - * @private - */ -MaterialRadio.prototype.onChange_ = function (event) { - // Since other radio buttons don't get change events, we need to look for - // them to update their classes. - var radios = document.getElementsByClassName(this.CssClasses_.JS_RADIO); - for (var i = 0; i < radios.length; i++) { - var button = radios[i].querySelector('.' + this.CssClasses_.RADIO_BTN); - // Different name == different group, so no point updating those. - if (button.getAttribute('name') === this.btnElement_.getAttribute('name')) { - radios[i]['MaterialRadio'].updateClasses_(); - } - } -}; -/** - * Handle focus. - * - * @param {Event} event The event that fired. - * @private - */ -MaterialRadio.prototype.onFocus_ = function (event) { - this.element_.classList.add(this.CssClasses_.IS_FOCUSED); -}; -/** - * Handle lost focus. - * - * @param {Event} event The event that fired. - * @private - */ -MaterialRadio.prototype.onBlur_ = function (event) { - this.element_.classList.remove(this.CssClasses_.IS_FOCUSED); -}; -/** - * Handle mouseup. - * - * @param {Event} event The event that fired. - * @private - */ -MaterialRadio.prototype.onMouseup_ = function (event) { - this.blur_(); -}; -/** - * Update classes. - * - * @private - */ -MaterialRadio.prototype.updateClasses_ = function () { - this.checkDisabled(); - this.checkToggleState(); -}; -/** - * Add blur. - * - * @private - */ -MaterialRadio.prototype.blur_ = function () { - // TODO: figure out why there's a focus event being fired after our blur, - // so that we can avoid this hack. - window.setTimeout(function () { - this.btnElement_.blur(); - }.bind(this), this.Constant_.TINY_TIMEOUT); -}; -// Public methods. -/** - * Check the components disabled state. - * - * @public - */ -MaterialRadio.prototype.checkDisabled = function () { - if (this.btnElement_.disabled) { - this.element_.classList.add(this.CssClasses_.IS_DISABLED); - } else { - this.element_.classList.remove(this.CssClasses_.IS_DISABLED); - } -}; -MaterialRadio.prototype['checkDisabled'] = MaterialRadio.prototype.checkDisabled; -/** - * Check the components toggled state. - * - * @public - */ -MaterialRadio.prototype.checkToggleState = function () { - if (this.btnElement_.checked) { - this.element_.classList.add(this.CssClasses_.IS_CHECKED); - } else { - this.element_.classList.remove(this.CssClasses_.IS_CHECKED); - } -}; -MaterialRadio.prototype['checkToggleState'] = MaterialRadio.prototype.checkToggleState; -/** - * Disable radio. - * - * @public - */ -MaterialRadio.prototype.disable = function () { - this.btnElement_.disabled = true; - this.updateClasses_(); -}; -MaterialRadio.prototype['disable'] = MaterialRadio.prototype.disable; -/** - * Enable radio. - * - * @public - */ -MaterialRadio.prototype.enable = function () { - this.btnElement_.disabled = false; - this.updateClasses_(); -}; -MaterialRadio.prototype['enable'] = MaterialRadio.prototype.enable; -/** - * Check radio. - * - * @public - */ -MaterialRadio.prototype.check = function () { - this.btnElement_.checked = true; - this.updateClasses_(); -}; -MaterialRadio.prototype['check'] = MaterialRadio.prototype.check; -/** - * Uncheck radio. - * - * @public - */ -MaterialRadio.prototype.uncheck = function () { - this.btnElement_.checked = false; - this.updateClasses_(); -}; -MaterialRadio.prototype['uncheck'] = MaterialRadio.prototype.uncheck; -/** - * Initialize element. - */ -MaterialRadio.prototype.init = function () { - if (this.element_) { - this.btnElement_ = this.element_.querySelector('.' + this.CssClasses_.RADIO_BTN); - this.boundChangeHandler_ = this.onChange_.bind(this); - this.boundFocusHandler_ = this.onChange_.bind(this); - this.boundBlurHandler_ = this.onBlur_.bind(this); - this.boundMouseUpHandler_ = this.onMouseup_.bind(this); - var outerCircle = document.createElement('span'); - outerCircle.classList.add(this.CssClasses_.RADIO_OUTER_CIRCLE); - var innerCircle = document.createElement('span'); - innerCircle.classList.add(this.CssClasses_.RADIO_INNER_CIRCLE); - this.element_.appendChild(outerCircle); - this.element_.appendChild(innerCircle); - var rippleContainer; - if (this.element_.classList.contains(this.CssClasses_.RIPPLE_EFFECT)) { - this.element_.classList.add(this.CssClasses_.RIPPLE_IGNORE_EVENTS); - rippleContainer = document.createElement('span'); - rippleContainer.classList.add(this.CssClasses_.RIPPLE_CONTAINER); - rippleContainer.classList.add(this.CssClasses_.RIPPLE_EFFECT); - rippleContainer.classList.add(this.CssClasses_.RIPPLE_CENTER); - rippleContainer.addEventListener('mouseup', this.boundMouseUpHandler_); - var ripple = document.createElement('span'); - ripple.classList.add(this.CssClasses_.RIPPLE); - rippleContainer.appendChild(ripple); - this.element_.appendChild(rippleContainer); - } - this.btnElement_.addEventListener('change', this.boundChangeHandler_); - this.btnElement_.addEventListener('focus', this.boundFocusHandler_); - this.btnElement_.addEventListener('blur', this.boundBlurHandler_); - this.element_.addEventListener('mouseup', this.boundMouseUpHandler_); - this.updateClasses_(); - this.element_.classList.add(this.CssClasses_.IS_UPGRADED); - } -}; -/** - * Downgrade the element. - * - * @private - */ -MaterialRadio.prototype.mdlDowngrade_ = function () { - var rippleContainer = this.element_.querySelector('.' + this.CssClasses_.RIPPLE_CONTAINER); - this.btnElement_.removeEventListener('change', this.boundChangeHandler_); - this.btnElement_.removeEventListener('focus', this.boundFocusHandler_); - this.btnElement_.removeEventListener('blur', this.boundBlurHandler_); - this.element_.removeEventListener('mouseup', this.boundMouseUpHandler_); - if (rippleContainer) { - rippleContainer.removeEventListener('mouseup', this.boundMouseUpHandler_); - this.element_.removeChild(rippleContainer); - } -}; -/** - * Public alias for the downgrade method. - * - * @public - */ -MaterialRadio.prototype.mdlDowngrade = MaterialRadio.prototype.mdlDowngrade_; -MaterialRadio.prototype['mdlDowngrade'] = MaterialRadio.prototype.mdlDowngrade; -// The component registers itself. It can assume componentHandler is available -// in the global scope. -componentHandler.register({ - constructor: MaterialRadio, - classAsString: 'MaterialRadio', - cssClass: 'mdl-js-radio', - widget: true -}); -/** - * @license - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/** - * Class constructor for Slider MDL component. - * Implements MDL component design pattern defined at: - * https://github.com/jasonmayes/mdl-component-design-pattern - * - * @constructor - * @param {HTMLElement} element The element that will be upgraded. - */ -var MaterialSlider = function MaterialSlider(element) { - this.element_ = element; - // Browser feature detection. - this.isIE_ = window.navigator.msPointerEnabled; - // Initialize instance. - this.init(); -}; -window['MaterialSlider'] = MaterialSlider; -/** - * Store constants in one place so they can be updated easily. - * - * @enum {string | number} - * @private - */ -MaterialSlider.prototype.Constant_ = {}; -/** - * Store strings for class names defined by this component that are used in - * JavaScript. This allows us to simply change it in one place should we - * decide to modify at a later date. - * - * @enum {string} - * @private - */ -MaterialSlider.prototype.CssClasses_ = { - IE_CONTAINER: 'mdl-slider__ie-container', - SLIDER_CONTAINER: 'mdl-slider__container', - BACKGROUND_FLEX: 'mdl-slider__background-flex', - BACKGROUND_LOWER: 'mdl-slider__background-lower', - BACKGROUND_UPPER: 'mdl-slider__background-upper', - IS_LOWEST_VALUE: 'is-lowest-value', - IS_UPGRADED: 'is-upgraded' -}; -/** - * Handle input on element. - * - * @param {Event} event The event that fired. - * @private - */ -MaterialSlider.prototype.onInput_ = function (event) { - this.updateValueStyles_(); -}; -/** - * Handle change on element. - * - * @param {Event} event The event that fired. - * @private - */ -MaterialSlider.prototype.onChange_ = function (event) { - this.updateValueStyles_(); -}; -/** - * Handle mouseup on element. - * - * @param {Event} event The event that fired. - * @private - */ -MaterialSlider.prototype.onMouseUp_ = function (event) { - event.target.blur(); -}; -/** - * Handle mousedown on container element. - * This handler is purpose is to not require the use to click - * exactly on the 2px slider element, as FireFox seems to be very - * strict about this. - * - * @param {Event} event The event that fired. - * @private - * @suppress {missingProperties} - */ -MaterialSlider.prototype.onContainerMouseDown_ = function (event) { - // If this click is not on the parent element (but rather some child) - // ignore. It may still bubble up. - if (event.target !== this.element_.parentElement) { - return; - } - // Discard the original event and create a new event that - // is on the slider element. - event.preventDefault(); - var newEvent = new MouseEvent('mousedown', { - target: event.target, - buttons: event.buttons, - clientX: event.clientX, - clientY: this.element_.getBoundingClientRect().y - }); - this.element_.dispatchEvent(newEvent); -}; -/** - * Handle updating of values. - * - * @private - */ -MaterialSlider.prototype.updateValueStyles_ = function () { - // Calculate and apply percentages to div structure behind slider. - var fraction = (this.element_.value - this.element_.min) / (this.element_.max - this.element_.min); - if (fraction === 0) { - this.element_.classList.add(this.CssClasses_.IS_LOWEST_VALUE); - } else { - this.element_.classList.remove(this.CssClasses_.IS_LOWEST_VALUE); - } - if (!this.isIE_) { - this.backgroundLower_.style.flex = fraction; - this.backgroundLower_.style.webkitFlex = fraction; - this.backgroundUpper_.style.flex = 1 - fraction; - this.backgroundUpper_.style.webkitFlex = 1 - fraction; - } -}; -// Public methods. -/** - * Disable slider. - * - * @public - */ -MaterialSlider.prototype.disable = function () { - this.element_.disabled = true; -}; -MaterialSlider.prototype['disable'] = MaterialSlider.prototype.disable; -/** - * Enable slider. - * - * @public - */ -MaterialSlider.prototype.enable = function () { - this.element_.disabled = false; -}; -MaterialSlider.prototype['enable'] = MaterialSlider.prototype.enable; -/** - * Update slider value. - * - * @param {number} value The value to which to set the control (optional). - * @public - */ -MaterialSlider.prototype.change = function (value) { - if (typeof value !== 'undefined') { - this.element_.value = value; - } - this.updateValueStyles_(); -}; -MaterialSlider.prototype['change'] = MaterialSlider.prototype.change; -/** - * Initialize element. - */ -MaterialSlider.prototype.init = function () { - if (this.element_) { - if (this.isIE_) { - // Since we need to specify a very large height in IE due to - // implementation limitations, we add a parent here that trims it down to - // a reasonable size. - var containerIE = document.createElement('div'); - containerIE.classList.add(this.CssClasses_.IE_CONTAINER); - this.element_.parentElement.insertBefore(containerIE, this.element_); - this.element_.parentElement.removeChild(this.element_); - containerIE.appendChild(this.element_); - } else { - // For non-IE browsers, we need a div structure that sits behind the - // slider and allows us to style the left and right sides of it with - // different colors. - var container = document.createElement('div'); - container.classList.add(this.CssClasses_.SLIDER_CONTAINER); - this.element_.parentElement.insertBefore(container, this.element_); - this.element_.parentElement.removeChild(this.element_); - container.appendChild(this.element_); - var backgroundFlex = document.createElement('div'); - backgroundFlex.classList.add(this.CssClasses_.BACKGROUND_FLEX); - container.appendChild(backgroundFlex); - this.backgroundLower_ = document.createElement('div'); - this.backgroundLower_.classList.add(this.CssClasses_.BACKGROUND_LOWER); - backgroundFlex.appendChild(this.backgroundLower_); - this.backgroundUpper_ = document.createElement('div'); - this.backgroundUpper_.classList.add(this.CssClasses_.BACKGROUND_UPPER); - backgroundFlex.appendChild(this.backgroundUpper_); - } - this.boundInputHandler = this.onInput_.bind(this); - this.boundChangeHandler = this.onChange_.bind(this); - this.boundMouseUpHandler = this.onMouseUp_.bind(this); - this.boundContainerMouseDownHandler = this.onContainerMouseDown_.bind(this); - this.element_.addEventListener('input', this.boundInputHandler); - this.element_.addEventListener('change', this.boundChangeHandler); - this.element_.addEventListener('mouseup', this.boundMouseUpHandler); - this.element_.parentElement.addEventListener('mousedown', this.boundContainerMouseDownHandler); - this.updateValueStyles_(); - this.element_.classList.add(this.CssClasses_.IS_UPGRADED); - } -}; -/** - * Downgrade the component - * - * @private - */ -MaterialSlider.prototype.mdlDowngrade_ = function () { - this.element_.removeEventListener('input', this.boundInputHandler); - this.element_.removeEventListener('change', this.boundChangeHandler); - this.element_.removeEventListener('mouseup', this.boundMouseUpHandler); - this.element_.parentElement.removeEventListener('mousedown', this.boundContainerMouseDownHandler); -}; -/** - * Public alias for the downgrade method. - * - * @public - */ -MaterialSlider.prototype.mdlDowngrade = MaterialSlider.prototype.mdlDowngrade_; -MaterialSlider.prototype['mdlDowngrade'] = MaterialSlider.prototype.mdlDowngrade; -// The component registers itself. It can assume componentHandler is available -// in the global scope. -componentHandler.register({ - constructor: MaterialSlider, - classAsString: 'MaterialSlider', - cssClass: 'mdl-js-slider', - widget: true -}); -/** - * @license - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/** - * Class constructor for Spinner MDL component. - * Implements MDL component design pattern defined at: - * https://github.com/jasonmayes/mdl-component-design-pattern - * - * @param {HTMLElement} element The element that will be upgraded. - * @constructor - */ -var MaterialSpinner = function MaterialSpinner(element) { - this.element_ = element; - // Initialize instance. - this.init(); -}; -window['MaterialSpinner'] = MaterialSpinner; -/** - * Store constants in one place so they can be updated easily. - * - * @enum {string | number} - * @private - */ -MaterialSpinner.prototype.Constant_ = { MDL_SPINNER_LAYER_COUNT: 4 }; -/** - * Store strings for class names defined by this component that are used in - * JavaScript. This allows us to simply change it in one place should we - * decide to modify at a later date. - * - * @enum {string} - * @private - */ -MaterialSpinner.prototype.CssClasses_ = { - MDL_SPINNER_LAYER: 'mdl-spinner__layer', - MDL_SPINNER_CIRCLE_CLIPPER: 'mdl-spinner__circle-clipper', - MDL_SPINNER_CIRCLE: 'mdl-spinner__circle', - MDL_SPINNER_GAP_PATCH: 'mdl-spinner__gap-patch', - MDL_SPINNER_LEFT: 'mdl-spinner__left', - MDL_SPINNER_RIGHT: 'mdl-spinner__right' -}; -/** - * Auxiliary method to create a spinner layer. - * - * @param {number} index Index of the layer to be created. - * @public - */ -MaterialSpinner.prototype.createLayer = function (index) { - var layer = document.createElement('div'); - layer.classList.add(this.CssClasses_.MDL_SPINNER_LAYER); - layer.classList.add(this.CssClasses_.MDL_SPINNER_LAYER + '-' + index); - var leftClipper = document.createElement('div'); - leftClipper.classList.add(this.CssClasses_.MDL_SPINNER_CIRCLE_CLIPPER); - leftClipper.classList.add(this.CssClasses_.MDL_SPINNER_LEFT); - var gapPatch = document.createElement('div'); - gapPatch.classList.add(this.CssClasses_.MDL_SPINNER_GAP_PATCH); - var rightClipper = document.createElement('div'); - rightClipper.classList.add(this.CssClasses_.MDL_SPINNER_CIRCLE_CLIPPER); - rightClipper.classList.add(this.CssClasses_.MDL_SPINNER_RIGHT); - var circleOwners = [ - leftClipper, - gapPatch, - rightClipper - ]; - for (var i = 0; i < circleOwners.length; i++) { - var circle = document.createElement('div'); - circle.classList.add(this.CssClasses_.MDL_SPINNER_CIRCLE); - circleOwners[i].appendChild(circle); - } - layer.appendChild(leftClipper); - layer.appendChild(gapPatch); - layer.appendChild(rightClipper); - this.element_.appendChild(layer); -}; -MaterialSpinner.prototype['createLayer'] = MaterialSpinner.prototype.createLayer; -/** - * Stops the spinner animation. - * Public method for users who need to stop the spinner for any reason. - * - * @public - */ -MaterialSpinner.prototype.stop = function () { - this.element_.classList.remove('is-active'); -}; -MaterialSpinner.prototype['stop'] = MaterialSpinner.prototype.stop; -/** - * Starts the spinner animation. - * Public method for users who need to manually start the spinner for any reason - * (instead of just adding the 'is-active' class to their markup). - * - * @public - */ -MaterialSpinner.prototype.start = function () { - this.element_.classList.add('is-active'); -}; -MaterialSpinner.prototype['start'] = MaterialSpinner.prototype.start; -/** - * Initialize element. - */ -MaterialSpinner.prototype.init = function () { - if (this.element_) { - for (var i = 1; i <= this.Constant_.MDL_SPINNER_LAYER_COUNT; i++) { - this.createLayer(i); - } - this.element_.classList.add('is-upgraded'); - } -}; -// The component registers itself. It can assume componentHandler is available -// in the global scope. -componentHandler.register({ - constructor: MaterialSpinner, - classAsString: 'MaterialSpinner', - cssClass: 'mdl-js-spinner', - widget: true -}); -/** - * @license - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/** - * Class constructor for Checkbox MDL component. - * Implements MDL component design pattern defined at: - * https://github.com/jasonmayes/mdl-component-design-pattern - * - * @constructor - * @param {HTMLElement} element The element that will be upgraded. - */ -var MaterialSwitch = function MaterialSwitch(element) { - this.element_ = element; - // Initialize instance. - this.init(); -}; -window['MaterialSwitch'] = MaterialSwitch; -/** - * Store constants in one place so they can be updated easily. - * - * @enum {string | number} - * @private - */ -MaterialSwitch.prototype.Constant_ = { TINY_TIMEOUT: 0.001 }; -/** - * Store strings for class names defined by this component that are used in - * JavaScript. This allows us to simply change it in one place should we - * decide to modify at a later date. - * - * @enum {string} - * @private - */ -MaterialSwitch.prototype.CssClasses_ = { - INPUT: 'mdl-switch__input', - TRACK: 'mdl-switch__track', - THUMB: 'mdl-switch__thumb', - FOCUS_HELPER: 'mdl-switch__focus-helper', - RIPPLE_EFFECT: 'mdl-js-ripple-effect', - RIPPLE_IGNORE_EVENTS: 'mdl-js-ripple-effect--ignore-events', - RIPPLE_CONTAINER: 'mdl-switch__ripple-container', - RIPPLE_CENTER: 'mdl-ripple--center', - RIPPLE: 'mdl-ripple', - IS_FOCUSED: 'is-focused', - IS_DISABLED: 'is-disabled', - IS_CHECKED: 'is-checked' -}; -/** - * Handle change of state. - * - * @param {Event} event The event that fired. - * @private - */ -MaterialSwitch.prototype.onChange_ = function (event) { - this.updateClasses_(); -}; -/** - * Handle focus of element. - * - * @param {Event} event The event that fired. - * @private - */ -MaterialSwitch.prototype.onFocus_ = function (event) { - this.element_.classList.add(this.CssClasses_.IS_FOCUSED); -}; -/** - * Handle lost focus of element. - * - * @param {Event} event The event that fired. - * @private - */ -MaterialSwitch.prototype.onBlur_ = function (event) { - this.element_.classList.remove(this.CssClasses_.IS_FOCUSED); -}; -/** - * Handle mouseup. - * - * @param {Event} event The event that fired. - * @private - */ -MaterialSwitch.prototype.onMouseUp_ = function (event) { - this.blur_(); -}; -/** - * Handle class updates. - * - * @private - */ -MaterialSwitch.prototype.updateClasses_ = function () { - this.checkDisabled(); - this.checkToggleState(); -}; -/** - * Add blur. - * - * @private - */ -MaterialSwitch.prototype.blur_ = function () { - // TODO: figure out why there's a focus event being fired after our blur, - // so that we can avoid this hack. - window.setTimeout(function () { - this.inputElement_.blur(); - }.bind(this), this.Constant_.TINY_TIMEOUT); -}; -// Public methods. -/** - * Check the components disabled state. - * - * @public - */ -MaterialSwitch.prototype.checkDisabled = function () { - if (this.inputElement_.disabled) { - this.element_.classList.add(this.CssClasses_.IS_DISABLED); - } else { - this.element_.classList.remove(this.CssClasses_.IS_DISABLED); - } -}; -MaterialSwitch.prototype['checkDisabled'] = MaterialSwitch.prototype.checkDisabled; -/** - * Check the components toggled state. - * - * @public - */ -MaterialSwitch.prototype.checkToggleState = function () { - if (this.inputElement_.checked) { - this.element_.classList.add(this.CssClasses_.IS_CHECKED); - } else { - this.element_.classList.remove(this.CssClasses_.IS_CHECKED); - } -}; -MaterialSwitch.prototype['checkToggleState'] = MaterialSwitch.prototype.checkToggleState; -/** - * Disable switch. - * - * @public - */ -MaterialSwitch.prototype.disable = function () { - this.inputElement_.disabled = true; - this.updateClasses_(); -}; -MaterialSwitch.prototype['disable'] = MaterialSwitch.prototype.disable; -/** - * Enable switch. - * - * @public - */ -MaterialSwitch.prototype.enable = function () { - this.inputElement_.disabled = false; - this.updateClasses_(); -}; -MaterialSwitch.prototype['enable'] = MaterialSwitch.prototype.enable; -/** - * Activate switch. - * - * @public - */ -MaterialSwitch.prototype.on = function () { - this.inputElement_.checked = true; - this.updateClasses_(); -}; -MaterialSwitch.prototype['on'] = MaterialSwitch.prototype.on; -/** - * Deactivate switch. - * - * @public - */ -MaterialSwitch.prototype.off = function () { - this.inputElement_.checked = false; - this.updateClasses_(); -}; -MaterialSwitch.prototype['off'] = MaterialSwitch.prototype.off; -/** - * Initialize element. - */ -MaterialSwitch.prototype.init = function () { - if (this.element_) { - this.inputElement_ = this.element_.querySelector('.' + this.CssClasses_.INPUT); - var track = document.createElement('div'); - track.classList.add(this.CssClasses_.TRACK); - var thumb = document.createElement('div'); - thumb.classList.add(this.CssClasses_.THUMB); - var focusHelper = document.createElement('span'); - focusHelper.classList.add(this.CssClasses_.FOCUS_HELPER); - thumb.appendChild(focusHelper); - this.element_.appendChild(track); - this.element_.appendChild(thumb); - this.boundMouseUpHandler = this.onMouseUp_.bind(this); - if (this.element_.classList.contains(this.CssClasses_.RIPPLE_EFFECT)) { - this.element_.classList.add(this.CssClasses_.RIPPLE_IGNORE_EVENTS); - this.rippleContainerElement_ = document.createElement('span'); - this.rippleContainerElement_.classList.add(this.CssClasses_.RIPPLE_CONTAINER); - this.rippleContainerElement_.classList.add(this.CssClasses_.RIPPLE_EFFECT); - this.rippleContainerElement_.classList.add(this.CssClasses_.RIPPLE_CENTER); - this.rippleContainerElement_.addEventListener('mouseup', this.boundMouseUpHandler); - var ripple = document.createElement('span'); - ripple.classList.add(this.CssClasses_.RIPPLE); - this.rippleContainerElement_.appendChild(ripple); - this.element_.appendChild(this.rippleContainerElement_); - } - this.boundChangeHandler = this.onChange_.bind(this); - this.boundFocusHandler = this.onFocus_.bind(this); - this.boundBlurHandler = this.onBlur_.bind(this); - this.inputElement_.addEventListener('change', this.boundChangeHandler); - this.inputElement_.addEventListener('focus', this.boundFocusHandler); - this.inputElement_.addEventListener('blur', this.boundBlurHandler); - this.element_.addEventListener('mouseup', this.boundMouseUpHandler); - this.updateClasses_(); - this.element_.classList.add('is-upgraded'); - } -}; -/** - * Downgrade the component. - * - * @private - */ -MaterialSwitch.prototype.mdlDowngrade_ = function () { - if (this.rippleContainerElement_) { - this.rippleContainerElement_.removeEventListener('mouseup', this.boundMouseUpHandler); - } - this.inputElement_.removeEventListener('change', this.boundChangeHandler); - this.inputElement_.removeEventListener('focus', this.boundFocusHandler); - this.inputElement_.removeEventListener('blur', this.boundBlurHandler); - this.element_.removeEventListener('mouseup', this.boundMouseUpHandler); -}; -/** - * Public alias for the downgrade method. - * - * @public - */ -MaterialSwitch.prototype.mdlDowngrade = MaterialSwitch.prototype.mdlDowngrade_; -MaterialSwitch.prototype['mdlDowngrade'] = MaterialSwitch.prototype.mdlDowngrade; -// The component registers itself. It can assume componentHandler is available -// in the global scope. -componentHandler.register({ - constructor: MaterialSwitch, - classAsString: 'MaterialSwitch', - cssClass: 'mdl-js-switch', - widget: true -}); -/** - * @license - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/** - * Class constructor for Tabs MDL component. - * Implements MDL component design pattern defined at: - * https://github.com/jasonmayes/mdl-component-design-pattern - * - * @constructor - * @param {HTMLElement} element The element that will be upgraded. - */ -var MaterialTabs = function MaterialTabs(element) { - // Stores the HTML element. - this.element_ = element; - // Initialize instance. - this.init(); -}; -window['MaterialTabs'] = MaterialTabs; -/** - * Store constants in one place so they can be updated easily. - * - * @enum {string} - * @private - */ -MaterialTabs.prototype.Constant_ = {}; -/** - * Store strings for class names defined by this component that are used in - * JavaScript. This allows us to simply change it in one place should we - * decide to modify at a later date. - * - * @enum {string} - * @private - */ -MaterialTabs.prototype.CssClasses_ = { - TAB_CLASS: 'mdl-tabs__tab', - PANEL_CLASS: 'mdl-tabs__panel', - ACTIVE_CLASS: 'is-active', - UPGRADED_CLASS: 'is-upgraded', - MDL_JS_RIPPLE_EFFECT: 'mdl-js-ripple-effect', - MDL_RIPPLE_CONTAINER: 'mdl-tabs__ripple-container', - MDL_RIPPLE: 'mdl-ripple', - MDL_JS_RIPPLE_EFFECT_IGNORE_EVENTS: 'mdl-js-ripple-effect--ignore-events' -}; -/** - * Handle clicks to a tabs component - * - * @private - */ -MaterialTabs.prototype.initTabs_ = function () { - if (this.element_.classList.contains(this.CssClasses_.MDL_JS_RIPPLE_EFFECT)) { - this.element_.classList.add(this.CssClasses_.MDL_JS_RIPPLE_EFFECT_IGNORE_EVENTS); - } - // Select element tabs, document panels - this.tabs_ = this.element_.querySelectorAll('.' + this.CssClasses_.TAB_CLASS); - this.panels_ = this.element_.querySelectorAll('.' + this.CssClasses_.PANEL_CLASS); - // Create new tabs for each tab element - for (var i = 0; i < this.tabs_.length; i++) { - new MaterialTab(this.tabs_[i], this); - } - this.element_.classList.add(this.CssClasses_.UPGRADED_CLASS); -}; -/** - * Reset tab state, dropping active classes - * - * @private - */ -MaterialTabs.prototype.resetTabState_ = function () { - for (var k = 0; k < this.tabs_.length; k++) { - this.tabs_[k].classList.remove(this.CssClasses_.ACTIVE_CLASS); - } -}; -/** - * Reset panel state, droping active classes - * - * @private - */ -MaterialTabs.prototype.resetPanelState_ = function () { - for (var j = 0; j < this.panels_.length; j++) { - this.panels_[j].classList.remove(this.CssClasses_.ACTIVE_CLASS); - } -}; -/** - * Initialize element. - */ -MaterialTabs.prototype.init = function () { - if (this.element_) { - this.initTabs_(); - } -}; -/** - * Constructor for an individual tab. - * - * @constructor - * @param {HTMLElement} tab The HTML element for the tab. - * @param {MaterialTabs} ctx The MaterialTabs object that owns the tab. - */ -function MaterialTab(tab, ctx) { - if (tab) { - if (ctx.element_.classList.contains(ctx.CssClasses_.MDL_JS_RIPPLE_EFFECT)) { - var rippleContainer = document.createElement('span'); - rippleContainer.classList.add(ctx.CssClasses_.MDL_RIPPLE_CONTAINER); - rippleContainer.classList.add(ctx.CssClasses_.MDL_JS_RIPPLE_EFFECT); - var ripple = document.createElement('span'); - ripple.classList.add(ctx.CssClasses_.MDL_RIPPLE); - rippleContainer.appendChild(ripple); - tab.appendChild(rippleContainer); - } - tab.addEventListener('click', function (e) { - e.preventDefault(); - var href = tab.href.split('#')[1]; - var panel = ctx.element_.querySelector('#' + href); - ctx.resetTabState_(); - ctx.resetPanelState_(); - tab.classList.add(ctx.CssClasses_.ACTIVE_CLASS); - panel.classList.add(ctx.CssClasses_.ACTIVE_CLASS); - }); - } -} -// The component registers itself. It can assume componentHandler is available -// in the global scope. -componentHandler.register({ - constructor: MaterialTabs, - classAsString: 'MaterialTabs', - cssClass: 'mdl-js-tabs' -}); -/** - * @license - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/** - * Class constructor for Textfield MDL component. - * Implements MDL component design pattern defined at: - * https://github.com/jasonmayes/mdl-component-design-pattern - * - * @constructor - * @param {HTMLElement} element The element that will be upgraded. - */ -var MaterialTextfield = function MaterialTextfield(element) { - this.element_ = element; - this.maxRows = this.Constant_.NO_MAX_ROWS; - // Initialize instance. - this.init(); -}; -window['MaterialTextfield'] = MaterialTextfield; -/** - * Store constants in one place so they can be updated easily. - * - * @enum {string | number} - * @private - */ -MaterialTextfield.prototype.Constant_ = { - NO_MAX_ROWS: -1, - MAX_ROWS_ATTRIBUTE: 'maxrows' -}; -/** - * Store strings for class names defined by this component that are used in - * JavaScript. This allows us to simply change it in one place should we - * decide to modify at a later date. - * - * @enum {string} - * @private - */ -MaterialTextfield.prototype.CssClasses_ = { - LABEL: 'mdl-textfield__label', - INPUT: 'mdl-textfield__input', - IS_DIRTY: 'is-dirty', - IS_FOCUSED: 'is-focused', - IS_DISABLED: 'is-disabled', - IS_INVALID: 'is-invalid', - IS_UPGRADED: 'is-upgraded' -}; -/** - * Handle input being entered. - * - * @param {Event} event The event that fired. - * @private - */ -MaterialTextfield.prototype.onKeyDown_ = function (event) { - var currentRowCount = event.target.value.split('\n').length; - if (event.keyCode === 13) { - if (currentRowCount >= this.maxRows) { - event.preventDefault(); - } - } -}; -/** - * Handle focus. - * - * @param {Event} event The event that fired. - * @private - */ -MaterialTextfield.prototype.onFocus_ = function (event) { - this.element_.classList.add(this.CssClasses_.IS_FOCUSED); -}; -/** - * Handle lost focus. - * - * @param {Event} event The event that fired. - * @private - */ -MaterialTextfield.prototype.onBlur_ = function (event) { - this.element_.classList.remove(this.CssClasses_.IS_FOCUSED); -}; -/** - * Handle class updates. - * - * @private - */ -MaterialTextfield.prototype.updateClasses_ = function () { - this.checkDisabled(); - this.checkValidity(); - this.checkDirty(); -}; -// Public methods. -/** - * Check the disabled state and update field accordingly. - * - * @public - */ -MaterialTextfield.prototype.checkDisabled = function () { - if (this.input_.disabled) { - this.element_.classList.add(this.CssClasses_.IS_DISABLED); - } else { - this.element_.classList.remove(this.CssClasses_.IS_DISABLED); - } -}; -MaterialTextfield.prototype['checkDisabled'] = MaterialTextfield.prototype.checkDisabled; -/** - * Check the validity state and update field accordingly. - * - * @public - */ -MaterialTextfield.prototype.checkValidity = function () { - if (this.input_.validity) { - if (this.input_.validity.valid) { - this.element_.classList.remove(this.CssClasses_.IS_INVALID); - } else { - this.element_.classList.add(this.CssClasses_.IS_INVALID); - } - } -}; -MaterialTextfield.prototype['checkValidity'] = MaterialTextfield.prototype.checkValidity; -/** - * Check the dirty state and update field accordingly. - * - * @public - */ -MaterialTextfield.prototype.checkDirty = function () { - if (this.input_.value && this.input_.value.length > 0) { - this.element_.classList.add(this.CssClasses_.IS_DIRTY); - } else { - this.element_.classList.remove(this.CssClasses_.IS_DIRTY); - } -}; -MaterialTextfield.prototype['checkDirty'] = MaterialTextfield.prototype.checkDirty; -/** - * Disable text field. - * - * @public - */ -MaterialTextfield.prototype.disable = function () { - this.input_.disabled = true; - this.updateClasses_(); -}; -MaterialTextfield.prototype['disable'] = MaterialTextfield.prototype.disable; -/** - * Enable text field. - * - * @public - */ -MaterialTextfield.prototype.enable = function () { - this.input_.disabled = false; - this.updateClasses_(); -}; -MaterialTextfield.prototype['enable'] = MaterialTextfield.prototype.enable; -/** - * Update text field value. - * - * @param {string} value The value to which to set the control (optional). - * @public - */ -MaterialTextfield.prototype.change = function (value) { - this.input_.value = value || ''; - this.updateClasses_(); -}; -MaterialTextfield.prototype['change'] = MaterialTextfield.prototype.change; -/** - * Initialize element. - */ -MaterialTextfield.prototype.init = function () { - if (this.element_) { - this.label_ = this.element_.querySelector('.' + this.CssClasses_.LABEL); - this.input_ = this.element_.querySelector('.' + this.CssClasses_.INPUT); - if (this.input_) { - if (this.input_.hasAttribute(this.Constant_.MAX_ROWS_ATTRIBUTE)) { - this.maxRows = parseInt(this.input_.getAttribute(this.Constant_.MAX_ROWS_ATTRIBUTE), 10); - if (isNaN(this.maxRows)) { - this.maxRows = this.Constant_.NO_MAX_ROWS; - } - } - this.boundUpdateClassesHandler = this.updateClasses_.bind(this); - this.boundFocusHandler = this.onFocus_.bind(this); - this.boundBlurHandler = this.onBlur_.bind(this); - this.input_.addEventListener('input', this.boundUpdateClassesHandler); - this.input_.addEventListener('focus', this.boundFocusHandler); - this.input_.addEventListener('blur', this.boundBlurHandler); - if (this.maxRows !== this.Constant_.NO_MAX_ROWS) { - // TODO: This should handle pasting multi line text. - // Currently doesn't. - this.boundKeyDownHandler = this.onKeyDown_.bind(this); - this.input_.addEventListener('keydown', this.boundKeyDownHandler); - } - var invalid = this.element_.classList.contains(this.CssClasses_.IS_INVALID); - this.updateClasses_(); - this.element_.classList.add(this.CssClasses_.IS_UPGRADED); - if (invalid) { - this.element_.classList.add(this.CssClasses_.IS_INVALID); - } - } - } -}; -/** - * Downgrade the component - * - * @private - */ -MaterialTextfield.prototype.mdlDowngrade_ = function () { - this.input_.removeEventListener('input', this.boundUpdateClassesHandler); - this.input_.removeEventListener('focus', this.boundFocusHandler); - this.input_.removeEventListener('blur', this.boundBlurHandler); - if (this.boundKeyDownHandler) { - this.input_.removeEventListener('keydown', this.boundKeyDownHandler); - } -}; -/** - * Public alias for the downgrade method. - * - * @public - */ -MaterialTextfield.prototype.mdlDowngrade = MaterialTextfield.prototype.mdlDowngrade_; -MaterialTextfield.prototype['mdlDowngrade'] = MaterialTextfield.prototype.mdlDowngrade; -// The component registers itself. It can assume componentHandler is available -// in the global scope. -componentHandler.register({ - constructor: MaterialTextfield, - classAsString: 'MaterialTextfield', - cssClass: 'mdl-js-textfield', - widget: true -}); -/** - * @license - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/** - * Class constructor for Tooltip MDL component. - * Implements MDL component design pattern defined at: - * https://github.com/jasonmayes/mdl-component-design-pattern - * - * @constructor - * @param {HTMLElement} element The element that will be upgraded. - */ -var MaterialTooltip = function MaterialTooltip(element) { - this.element_ = element; - // Initialize instance. - this.init(); -}; -window['MaterialTooltip'] = MaterialTooltip; -/** - * Store constants in one place so they can be updated easily. - * - * @enum {string | number} - * @private - */ -MaterialTooltip.prototype.Constant_ = {}; -/** - * Store strings for class names defined by this component that are used in - * JavaScript. This allows us to simply change it in one place should we - * decide to modify at a later date. - * - * @enum {string} - * @private - */ -MaterialTooltip.prototype.CssClasses_ = { IS_ACTIVE: 'is-active' }; -/** - * Handle mouseenter for tooltip. - * - * @param {Event} event The event that fired. - * @private - */ -MaterialTooltip.prototype.handleMouseEnter_ = function (event) { - event.stopPropagation(); - var props = event.target.getBoundingClientRect(); - var left = props.left + props.width / 2; - var marginLeft = -1 * (this.element_.offsetWidth / 2); - if (left + marginLeft < 0) { - this.element_.style.left = 0; - this.element_.style.marginLeft = 0; - } else { - this.element_.style.left = left + 'px'; - this.element_.style.marginLeft = marginLeft + 'px'; - } - this.element_.style.top = props.top + props.height + 10 + 'px'; - this.element_.classList.add(this.CssClasses_.IS_ACTIVE); - window.addEventListener('scroll', this.boundMouseLeaveHandler, false); - window.addEventListener('touchmove', this.boundMouseLeaveHandler, false); -}; -/** - * Handle mouseleave for tooltip. - * - * @param {Event} event The event that fired. - * @private - */ -MaterialTooltip.prototype.handleMouseLeave_ = function (event) { - event.stopPropagation(); - this.element_.classList.remove(this.CssClasses_.IS_ACTIVE); - window.removeEventListener('scroll', this.boundMouseLeaveHandler); - window.removeEventListener('touchmove', this.boundMouseLeaveHandler, false); -}; -/** - * Initialize element. - */ -MaterialTooltip.prototype.init = function () { - if (this.element_) { - var forElId = this.element_.getAttribute('for'); - if (forElId) { - this.forElement_ = document.getElementById(forElId); - } - if (this.forElement_) { - // Tabindex needs to be set for `blur` events to be emitted - if (!this.forElement_.hasAttribute('tabindex')) { - this.forElement_.setAttribute('tabindex', '0'); - } - this.boundMouseEnterHandler = this.handleMouseEnter_.bind(this); - this.boundMouseLeaveHandler = this.handleMouseLeave_.bind(this); - this.forElement_.addEventListener('mouseenter', this.boundMouseEnterHandler, false); - this.forElement_.addEventListener('click', this.boundMouseEnterHandler, false); - this.forElement_.addEventListener('blur', this.boundMouseLeaveHandler); - this.forElement_.addEventListener('touchstart', this.boundMouseEnterHandler, false); - this.forElement_.addEventListener('mouseleave', this.boundMouseLeaveHandler); - } - } -}; -/** - * Downgrade the component - * - * @private - */ -MaterialTooltip.prototype.mdlDowngrade_ = function () { - if (this.forElement_) { - this.forElement_.removeEventListener('mouseenter', this.boundMouseEnterHandler, false); - this.forElement_.removeEventListener('click', this.boundMouseEnterHandler, false); - this.forElement_.removeEventListener('touchstart', this.boundMouseEnterHandler, false); - this.forElement_.removeEventListener('mouseleave', this.boundMouseLeaveHandler); - } -}; -/** - * Public alias for the downgrade method. - * - * @public - */ -MaterialTooltip.prototype.mdlDowngrade = MaterialTooltip.prototype.mdlDowngrade_; -MaterialTooltip.prototype['mdlDowngrade'] = MaterialTooltip.prototype.mdlDowngrade; -// The component registers itself. It can assume componentHandler is available -// in the global scope. -componentHandler.register({ - constructor: MaterialTooltip, - classAsString: 'MaterialTooltip', - cssClass: 'mdl-tooltip' -}); -/** - * @license - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/** - * Class constructor for Layout MDL component. - * Implements MDL component design pattern defined at: - * https://github.com/jasonmayes/mdl-component-design-pattern - * - * @constructor - * @param {HTMLElement} element The element that will be upgraded. - */ -var MaterialLayout = function MaterialLayout(element) { - this.element_ = element; - // Initialize instance. - this.init(); -}; -window['MaterialLayout'] = MaterialLayout; -/** - * Store constants in one place so they can be updated easily. - * - * @enum {string | number} - * @private - */ -MaterialLayout.prototype.Constant_ = { - MAX_WIDTH: '(max-width: 1024px)', - TAB_SCROLL_PIXELS: 100, - MENU_ICON: 'menu', - CHEVRON_LEFT: 'chevron_left', - CHEVRON_RIGHT: 'chevron_right' -}; -/** - * Modes. - * - * @enum {number} - * @private - */ -MaterialLayout.prototype.Mode_ = { - STANDARD: 0, - SEAMED: 1, - WATERFALL: 2, - SCROLL: 3 -}; -/** - * Store strings for class names defined by this component that are used in - * JavaScript. This allows us to simply change it in one place should we - * decide to modify at a later date. - * - * @enum {string} - * @private - */ -MaterialLayout.prototype.CssClasses_ = { - CONTAINER: 'mdl-layout__container', - HEADER: 'mdl-layout__header', - DRAWER: 'mdl-layout__drawer', - CONTENT: 'mdl-layout__content', - DRAWER_BTN: 'mdl-layout__drawer-button', - ICON: 'material-icons', - JS_RIPPLE_EFFECT: 'mdl-js-ripple-effect', - RIPPLE_CONTAINER: 'mdl-layout__tab-ripple-container', - RIPPLE: 'mdl-ripple', - RIPPLE_IGNORE_EVENTS: 'mdl-js-ripple-effect--ignore-events', - HEADER_SEAMED: 'mdl-layout__header--seamed', - HEADER_WATERFALL: 'mdl-layout__header--waterfall', - HEADER_SCROLL: 'mdl-layout__header--scroll', - FIXED_HEADER: 'mdl-layout--fixed-header', - OBFUSCATOR: 'mdl-layout__obfuscator', - TAB_BAR: 'mdl-layout__tab-bar', - TAB_CONTAINER: 'mdl-layout__tab-bar-container', - TAB: 'mdl-layout__tab', - TAB_BAR_BUTTON: 'mdl-layout__tab-bar-button', - TAB_BAR_LEFT_BUTTON: 'mdl-layout__tab-bar-left-button', - TAB_BAR_RIGHT_BUTTON: 'mdl-layout__tab-bar-right-button', - PANEL: 'mdl-layout__tab-panel', - HAS_DRAWER: 'has-drawer', - HAS_TABS: 'has-tabs', - HAS_SCROLLING_HEADER: 'has-scrolling-header', - CASTING_SHADOW: 'is-casting-shadow', - IS_COMPACT: 'is-compact', - IS_SMALL_SCREEN: 'is-small-screen', - IS_DRAWER_OPEN: 'is-visible', - IS_ACTIVE: 'is-active', - IS_UPGRADED: 'is-upgraded', - IS_ANIMATING: 'is-animating', - ON_LARGE_SCREEN: 'mdl-layout--large-screen-only', - ON_SMALL_SCREEN: 'mdl-layout--small-screen-only' -}; -/** - * Handles scrolling on the content. - * - * @private - */ -MaterialLayout.prototype.contentScrollHandler_ = function () { - if (this.header_.classList.contains(this.CssClasses_.IS_ANIMATING)) { - return; - } - if (this.content_.scrollTop > 0 && !this.header_.classList.contains(this.CssClasses_.IS_COMPACT)) { - this.header_.classList.add(this.CssClasses_.CASTING_SHADOW); - this.header_.classList.add(this.CssClasses_.IS_COMPACT); - this.header_.classList.add(this.CssClasses_.IS_ANIMATING); - } else if (this.content_.scrollTop <= 0 && this.header_.classList.contains(this.CssClasses_.IS_COMPACT)) { - this.header_.classList.remove(this.CssClasses_.CASTING_SHADOW); - this.header_.classList.remove(this.CssClasses_.IS_COMPACT); - this.header_.classList.add(this.CssClasses_.IS_ANIMATING); - } -}; -/** - * Handles changes in screen size. - * - * @private - */ -MaterialLayout.prototype.screenSizeHandler_ = function () { - if (this.screenSizeMediaQuery_.matches) { - this.element_.classList.add(this.CssClasses_.IS_SMALL_SCREEN); - } else { - this.element_.classList.remove(this.CssClasses_.IS_SMALL_SCREEN); - // Collapse drawer (if any) when moving to a large screen size. - if (this.drawer_) { - this.drawer_.classList.remove(this.CssClasses_.IS_DRAWER_OPEN); - this.obfuscator_.classList.remove(this.CssClasses_.IS_DRAWER_OPEN); - } - } -}; -/** - * Handles toggling of the drawer. - * - * @private - */ -MaterialLayout.prototype.drawerToggleHandler_ = function () { - this.drawer_.classList.toggle(this.CssClasses_.IS_DRAWER_OPEN); - this.obfuscator_.classList.toggle(this.CssClasses_.IS_DRAWER_OPEN); -}; -/** - * Handles (un)setting the `is-animating` class - * - * @private - */ -MaterialLayout.prototype.headerTransitionEndHandler_ = function () { - this.header_.classList.remove(this.CssClasses_.IS_ANIMATING); -}; -/** - * Handles expanding the header on click - * - * @private - */ -MaterialLayout.prototype.headerClickHandler_ = function () { - if (this.header_.classList.contains(this.CssClasses_.IS_COMPACT)) { - this.header_.classList.remove(this.CssClasses_.IS_COMPACT); - this.header_.classList.add(this.CssClasses_.IS_ANIMATING); - } -}; -/** - * Reset tab state, dropping active classes - * - * @private - */ -MaterialLayout.prototype.resetTabState_ = function (tabBar) { - for (var k = 0; k < tabBar.length; k++) { - tabBar[k].classList.remove(this.CssClasses_.IS_ACTIVE); - } -}; -/** - * Reset panel state, droping active classes - * - * @private - */ -MaterialLayout.prototype.resetPanelState_ = function (panels) { - for (var j = 0; j < panels.length; j++) { - panels[j].classList.remove(this.CssClasses_.IS_ACTIVE); - } -}; -/** - * Initialize element. - */ -MaterialLayout.prototype.init = function () { - if (this.element_) { - var container = document.createElement('div'); - container.classList.add(this.CssClasses_.CONTAINER); - this.element_.parentElement.insertBefore(container, this.element_); - this.element_.parentElement.removeChild(this.element_); - container.appendChild(this.element_); - var directChildren = this.element_.childNodes; - var numChildren = directChildren.length; - for (var c = 0; c < numChildren; c++) { - var child = directChildren[c]; - if (child.classList && child.classList.contains(this.CssClasses_.HEADER)) { - this.header_ = child; - } - if (child.classList && child.classList.contains(this.CssClasses_.DRAWER)) { - this.drawer_ = child; - } - if (child.classList && child.classList.contains(this.CssClasses_.CONTENT)) { - this.content_ = child; - } - } - if (this.header_) { - this.tabBar_ = this.header_.querySelector('.' + this.CssClasses_.TAB_BAR); - } - var mode = this.Mode_.STANDARD; - if (this.header_) { - if (this.header_.classList.contains(this.CssClasses_.HEADER_SEAMED)) { - mode = this.Mode_.SEAMED; - } else if (this.header_.classList.contains(this.CssClasses_.HEADER_WATERFALL)) { - mode = this.Mode_.WATERFALL; - this.header_.addEventListener('transitionend', this.headerTransitionEndHandler_.bind(this)); - this.header_.addEventListener('click', this.headerClickHandler_.bind(this)); - } else if (this.header_.classList.contains(this.CssClasses_.HEADER_SCROLL)) { - mode = this.Mode_.SCROLL; - container.classList.add(this.CssClasses_.HAS_SCROLLING_HEADER); - } - if (mode === this.Mode_.STANDARD) { - this.header_.classList.add(this.CssClasses_.CASTING_SHADOW); - if (this.tabBar_) { - this.tabBar_.classList.add(this.CssClasses_.CASTING_SHADOW); - } - } else if (mode === this.Mode_.SEAMED || mode === this.Mode_.SCROLL) { - this.header_.classList.remove(this.CssClasses_.CASTING_SHADOW); - if (this.tabBar_) { - this.tabBar_.classList.remove(this.CssClasses_.CASTING_SHADOW); - } - } else if (mode === this.Mode_.WATERFALL) { - // Add and remove shadows depending on scroll position. - // Also add/remove auxiliary class for styling of the compact version of - // the header. - this.content_.addEventListener('scroll', this.contentScrollHandler_.bind(this)); - this.contentScrollHandler_(); - } - } - // Add drawer toggling button to our layout, if we have an openable drawer. - if (this.drawer_) { - var drawerButton = this.element_.querySelector('.' + this.CssClasses_.DRAWER_BTN); - if (!drawerButton) { - drawerButton = document.createElement('div'); - drawerButton.classList.add(this.CssClasses_.DRAWER_BTN); - var drawerButtonIcon = document.createElement('i'); - drawerButtonIcon.classList.add(this.CssClasses_.ICON); - drawerButtonIcon.textContent = this.Constant_.MENU_ICON; - drawerButton.appendChild(drawerButtonIcon); - } - if (this.drawer_.classList.contains(this.CssClasses_.ON_LARGE_SCREEN)) { - //If drawer has ON_LARGE_SCREEN class then add it to the drawer toggle button as well. - drawerButton.classList.add(this.CssClasses_.ON_LARGE_SCREEN); - } else if (this.drawer_.classList.contains(this.CssClasses_.ON_SMALL_SCREEN)) { - //If drawer has ON_SMALL_SCREEN class then add it to the drawer toggle button as well. - drawerButton.classList.add(this.CssClasses_.ON_SMALL_SCREEN); - } - drawerButton.addEventListener('click', this.drawerToggleHandler_.bind(this)); - // Add a class if the layout has a drawer, for altering the left padding. - // Adds the HAS_DRAWER to the elements since this.header_ may or may - // not be present. - this.element_.classList.add(this.CssClasses_.HAS_DRAWER); - // If we have a fixed header, add the button to the header rather than - // the layout. - if (this.element_.classList.contains(this.CssClasses_.FIXED_HEADER)) { - this.header_.insertBefore(drawerButton, this.header_.firstChild); - } else { - this.element_.insertBefore(drawerButton, this.content_); - } - var obfuscator = document.createElement('div'); - obfuscator.classList.add(this.CssClasses_.OBFUSCATOR); - this.element_.appendChild(obfuscator); - obfuscator.addEventListener('click', this.drawerToggleHandler_.bind(this)); - this.obfuscator_ = obfuscator; - } - // Keep an eye on screen size, and add/remove auxiliary class for styling - // of small screens. - this.screenSizeMediaQuery_ = window.matchMedia(this.Constant_.MAX_WIDTH); - this.screenSizeMediaQuery_.addListener(this.screenSizeHandler_.bind(this)); - this.screenSizeHandler_(); - // Initialize tabs, if any. - if (this.header_ && this.tabBar_) { - this.element_.classList.add(this.CssClasses_.HAS_TABS); - var tabContainer = document.createElement('div'); - tabContainer.classList.add(this.CssClasses_.TAB_CONTAINER); - this.header_.insertBefore(tabContainer, this.tabBar_); - this.header_.removeChild(this.tabBar_); - var leftButton = document.createElement('div'); - leftButton.classList.add(this.CssClasses_.TAB_BAR_BUTTON); - leftButton.classList.add(this.CssClasses_.TAB_BAR_LEFT_BUTTON); - var leftButtonIcon = document.createElement('i'); - leftButtonIcon.classList.add(this.CssClasses_.ICON); - leftButtonIcon.textContent = this.Constant_.CHEVRON_LEFT; - leftButton.appendChild(leftButtonIcon); - leftButton.addEventListener('click', function () { - this.tabBar_.scrollLeft -= this.Constant_.TAB_SCROLL_PIXELS; - }.bind(this)); - var rightButton = document.createElement('div'); - rightButton.classList.add(this.CssClasses_.TAB_BAR_BUTTON); - rightButton.classList.add(this.CssClasses_.TAB_BAR_RIGHT_BUTTON); - var rightButtonIcon = document.createElement('i'); - rightButtonIcon.classList.add(this.CssClasses_.ICON); - rightButtonIcon.textContent = this.Constant_.CHEVRON_RIGHT; - rightButton.appendChild(rightButtonIcon); - rightButton.addEventListener('click', function () { - this.tabBar_.scrollLeft += this.Constant_.TAB_SCROLL_PIXELS; - }.bind(this)); - tabContainer.appendChild(leftButton); - tabContainer.appendChild(this.tabBar_); - tabContainer.appendChild(rightButton); - // Add and remove buttons depending on scroll position. - var tabScrollHandler = function () { - if (this.tabBar_.scrollLeft > 0) { - leftButton.classList.add(this.CssClasses_.IS_ACTIVE); - } else { - leftButton.classList.remove(this.CssClasses_.IS_ACTIVE); - } - if (this.tabBar_.scrollLeft < this.tabBar_.scrollWidth - this.tabBar_.offsetWidth) { - rightButton.classList.add(this.CssClasses_.IS_ACTIVE); - } else { - rightButton.classList.remove(this.CssClasses_.IS_ACTIVE); - } - }.bind(this); - this.tabBar_.addEventListener('scroll', tabScrollHandler); - tabScrollHandler(); - if (this.tabBar_.classList.contains(this.CssClasses_.JS_RIPPLE_EFFECT)) { - this.tabBar_.classList.add(this.CssClasses_.RIPPLE_IGNORE_EVENTS); - } - // Select element tabs, document panels - var tabs = this.tabBar_.querySelectorAll('.' + this.CssClasses_.TAB); - var panels = this.content_.querySelectorAll('.' + this.CssClasses_.PANEL); - // Create new tabs for each tab element - for (var i = 0; i < tabs.length; i++) { - new MaterialLayoutTab(tabs[i], tabs, panels, this); - } - } - this.element_.classList.add(this.CssClasses_.IS_UPGRADED); - } -}; -/** - * Constructor for an individual tab. - * - * @constructor - * @param {HTMLElement} tab The HTML element for the tab. - * @param {!Array} tabs Array with HTML elements for all tabs. - * @param {!Array} panels Array with HTML elements for all panels. - * @param {MaterialLayout} layout The MaterialLayout object that owns the tab. - */ -function MaterialLayoutTab(tab, tabs, panels, layout) { - if (layout.tabBar_.classList.contains(layout.CssClasses_.JS_RIPPLE_EFFECT)) { - var rippleContainer = document.createElement('span'); - rippleContainer.classList.add(layout.CssClasses_.RIPPLE_CONTAINER); - rippleContainer.classList.add(layout.CssClasses_.JS_RIPPLE_EFFECT); - var ripple = document.createElement('span'); - ripple.classList.add(layout.CssClasses_.RIPPLE); - rippleContainer.appendChild(ripple); - tab.appendChild(rippleContainer); - } - tab.addEventListener('click', function (e) { - e.preventDefault(); - var href = tab.href.split('#')[1]; - var panel = layout.content_.querySelector('#' + href); - layout.resetTabState_(tabs); - layout.resetPanelState_(panels); - tab.classList.add(layout.CssClasses_.IS_ACTIVE); - panel.classList.add(layout.CssClasses_.IS_ACTIVE); - }); -} -// The component registers itself. It can assume componentHandler is available -// in the global scope. -componentHandler.register({ - constructor: MaterialLayout, - classAsString: 'MaterialLayout', - cssClass: 'mdl-js-layout' -}); -/** - * @license - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/** - * Class constructor for Data Table Card MDL component. - * Implements MDL component design pattern defined at: - * https://github.com/jasonmayes/mdl-component-design-pattern - * - * @constructor - * @param {HTMLElement} element The element that will be upgraded. - */ -var MaterialDataTable = function MaterialDataTable(element) { - this.element_ = element; - // Initialize instance. - this.init(); -}; -window['MaterialDataTable'] = MaterialDataTable; -/** - * Store constants in one place so they can be updated easily. - * - * @enum {string | number} - * @private - */ -MaterialDataTable.prototype.Constant_ = {}; -/** - * Store strings for class names defined by this component that are used in - * JavaScript. This allows us to simply change it in one place should we - * decide to modify at a later date. - * - * @enum {string} - * @private - */ -MaterialDataTable.prototype.CssClasses_ = { - DATA_TABLE: 'mdl-data-table', - SELECTABLE: 'mdl-data-table--selectable', - SELECT_ELEMENT: 'mdl-data-table__select', - IS_SELECTED: 'is-selected', - IS_UPGRADED: 'is-upgraded' -}; -/** - * Generates and returns a function that toggles the selection state of a - * single row (or multiple rows). - * - * @param {Element} checkbox Checkbox that toggles the selection state. - * @param {HTMLElement} row Row to toggle when checkbox changes. - * @param {(Array|NodeList)=} opt_rows Rows to toggle when checkbox changes. - * @private - */ -MaterialDataTable.prototype.selectRow_ = function (checkbox, row, opt_rows) { - if (row) { - return function () { - if (checkbox.checked) { - row.classList.add(this.CssClasses_.IS_SELECTED); - } else { - row.classList.remove(this.CssClasses_.IS_SELECTED); - } - }.bind(this); - } - if (opt_rows) { - return function () { - var i; - var el; - if (checkbox.checked) { - for (i = 0; i < opt_rows.length; i++) { - el = opt_rows[i].querySelector('td').querySelector('.mdl-checkbox'); - el['MaterialCheckbox'].check(); - opt_rows[i].classList.add(this.CssClasses_.IS_SELECTED); - } - } else { - for (i = 0; i < opt_rows.length; i++) { - el = opt_rows[i].querySelector('td').querySelector('.mdl-checkbox'); - el['MaterialCheckbox'].uncheck(); - opt_rows[i].classList.remove(this.CssClasses_.IS_SELECTED); - } - } - }.bind(this); - } -}; -/** - * Creates a checkbox for a single or or multiple rows and hooks up the - * event handling. - * - * @param {HTMLElement} row Row to toggle when checkbox changes. - * @param {(Array|NodeList)=} opt_rows Rows to toggle when checkbox changes. - * @private - */ -MaterialDataTable.prototype.createCheckbox_ = function (row, opt_rows) { - var label = document.createElement('label'); - var labelClasses = [ - 'mdl-checkbox', - 'mdl-js-checkbox', - 'mdl-js-ripple-effect', - this.CssClasses_.SELECT_ELEMENT - ]; - label.className = labelClasses.join(' '); - var checkbox = document.createElement('input'); - checkbox.type = 'checkbox'; - checkbox.classList.add('mdl-checkbox__input'); - checkbox.addEventListener('change', this.selectRow_(checkbox, row, opt_rows)); - label.appendChild(checkbox); - componentHandler.upgradeElement(label, 'MaterialCheckbox'); - return label; -}; -/** - * Initialize element. - */ -MaterialDataTable.prototype.init = function () { - if (this.element_) { - var firstHeader = this.element_.querySelector('th'); - var rows = this.element_.querySelector('tbody').querySelectorAll('tr'); - if (this.element_.classList.contains(this.CssClasses_.SELECTABLE)) { - var th = document.createElement('th'); - var headerCheckbox = this.createCheckbox_(null, rows); - th.appendChild(headerCheckbox); - firstHeader.parentElement.insertBefore(th, firstHeader); - for (var i = 0; i < rows.length; i++) { - var firstCell = rows[i].querySelector('td'); - if (firstCell) { - var td = document.createElement('td'); - var rowCheckbox = this.createCheckbox_(rows[i]); - td.appendChild(rowCheckbox); - rows[i].insertBefore(td, firstCell); - } - } - } - this.element_.classList.add(this.CssClasses_.IS_UPGRADED); - } -}; -// The component registers itself. It can assume componentHandler is available -// in the global scope. -componentHandler.register({ - constructor: MaterialDataTable, - classAsString: 'MaterialDataTable', - cssClass: 'mdl-js-data-table' -}); -/** - * @license - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/** - * Class constructor for Ripple MDL component. - * Implements MDL component design pattern defined at: - * https://github.com/jasonmayes/mdl-component-design-pattern - * - * @constructor - * @param {HTMLElement} element The element that will be upgraded. - */ -var MaterialRipple = function MaterialRipple(element) { - this.element_ = element; - // Initialize instance. - this.init(); -}; -window['MaterialRipple'] = MaterialRipple; -/** - * Store constants in one place so they can be updated easily. - * - * @enum {string | number} - * @private - */ -MaterialRipple.prototype.Constant_ = { - INITIAL_SCALE: 'scale(0.0001, 0.0001)', - INITIAL_SIZE: '1px', - INITIAL_OPACITY: '0.4', - FINAL_OPACITY: '0', - FINAL_SCALE: '' -}; -/** - * Store strings for class names defined by this component that are used in - * JavaScript. This allows us to simply change it in one place should we - * decide to modify at a later date. - * - * @enum {string} - * @private - */ -MaterialRipple.prototype.CssClasses_ = { - RIPPLE_CENTER: 'mdl-ripple--center', - RIPPLE_EFFECT_IGNORE_EVENTS: 'mdl-js-ripple-effect--ignore-events', - RIPPLE: 'mdl-ripple', - IS_ANIMATING: 'is-animating', - IS_VISIBLE: 'is-visible' -}; -/** - * Handle mouse / finger down on element. - * - * @param {Event} event The event that fired. - * @private - */ -MaterialRipple.prototype.downHandler_ = function (event) { - if (!this.rippleElement_.style.width && !this.rippleElement_.style.height) { - var rect = this.element_.getBoundingClientRect(); - this.boundHeight = rect.height; - this.boundWidth = rect.width; - this.rippleSize_ = Math.sqrt(rect.width * rect.width + rect.height * rect.height) * 2 + 2; - this.rippleElement_.style.width = this.rippleSize_ + 'px'; - this.rippleElement_.style.height = this.rippleSize_ + 'px'; - } - this.rippleElement_.classList.add(this.CssClasses_.IS_VISIBLE); - if (event.type === 'mousedown' && this.ignoringMouseDown_) { - this.ignoringMouseDown_ = false; - } else { - if (event.type === 'touchstart') { - this.ignoringMouseDown_ = true; - } - var frameCount = this.getFrameCount(); - if (frameCount > 0) { - return; - } - this.setFrameCount(1); - var bound = event.currentTarget.getBoundingClientRect(); - var x; - var y; - // Check if we are handling a keyboard click. - if (event.clientX === 0 && event.clientY === 0) { - x = Math.round(bound.width / 2); - y = Math.round(bound.height / 2); - } else { - var clientX = event.clientX ? event.clientX : event.touches[0].clientX; - var clientY = event.clientY ? event.clientY : event.touches[0].clientY; - x = Math.round(clientX - bound.left); - y = Math.round(clientY - bound.top); - } - this.setRippleXY(x, y); - this.setRippleStyles(true); - window.requestAnimationFrame(this.animFrameHandler.bind(this)); - } -}; -/** - * Handle mouse / finger up on element. - * - * @param {Event} event The event that fired. - * @private - */ -MaterialRipple.prototype.upHandler_ = function (event) { - // Don't fire for the artificial "mouseup" generated by a double-click. - if (event && event.detail !== 2) { - this.rippleElement_.classList.remove(this.CssClasses_.IS_VISIBLE); - } - // Allow a repaint to occur before removing this class, so the animation - // shows for tap events, which seem to trigger a mouseup too soon after - // mousedown. - window.setTimeout(function () { - this.rippleElement_.classList.remove(this.CssClasses_.IS_VISIBLE); - }.bind(this), 0); -}; -/** - * Initialize element. - */ -MaterialRipple.prototype.init = function () { - if (this.element_) { - var recentering = this.element_.classList.contains(this.CssClasses_.RIPPLE_CENTER); - if (!this.element_.classList.contains(this.CssClasses_.RIPPLE_EFFECT_IGNORE_EVENTS)) { - this.rippleElement_ = this.element_.querySelector('.' + this.CssClasses_.RIPPLE); - this.frameCount_ = 0; - this.rippleSize_ = 0; - this.x_ = 0; - this.y_ = 0; - // Touch start produces a compat mouse down event, which would cause a - // second ripples. To avoid that, we use this property to ignore the first - // mouse down after a touch start. - this.ignoringMouseDown_ = false; - this.boundDownHandler = this.downHandler_.bind(this); - this.element_.addEventListener('mousedown', this.boundDownHandler); - this.element_.addEventListener('touchstart', this.boundDownHandler); - this.boundUpHandler = this.upHandler_.bind(this); - this.element_.addEventListener('mouseup', this.boundUpHandler); - this.element_.addEventListener('mouseleave', this.boundUpHandler); - this.element_.addEventListener('touchend', this.boundUpHandler); - this.element_.addEventListener('blur', this.boundUpHandler); - /** - * Getter for frameCount_. - * @return {number} the frame count. - */ - this.getFrameCount = function () { - return this.frameCount_; - }; - /** - * Setter for frameCount_. - * @param {number} fC the frame count. - */ - this.setFrameCount = function (fC) { - this.frameCount_ = fC; - }; - /** - * Getter for rippleElement_. - * @return {Element} the ripple element. - */ - this.getRippleElement = function () { - return this.rippleElement_; - }; - /** - * Sets the ripple X and Y coordinates. - * @param {number} newX the new X coordinate - * @param {number} newY the new Y coordinate - */ - this.setRippleXY = function (newX, newY) { - this.x_ = newX; - this.y_ = newY; - }; - /** - * Sets the ripple styles. - * @param {boolean} start whether or not this is the start frame. - */ - this.setRippleStyles = function (start) { - if (this.rippleElement_ !== null) { - var transformString; - var scale; - var size; - var offset = 'translate(' + this.x_ + 'px, ' + this.y_ + 'px)'; - if (start) { - scale = this.Constant_.INITIAL_SCALE; - size = this.Constant_.INITIAL_SIZE; - } else { - scale = this.Constant_.FINAL_SCALE; - size = this.rippleSize_ + 'px'; - if (recentering) { - offset = 'translate(' + this.boundWidth / 2 + 'px, ' + this.boundHeight / 2 + 'px)'; - } - } - transformString = 'translate(-50%, -50%) ' + offset + scale; - this.rippleElement_.style.webkitTransform = transformString; - this.rippleElement_.style.msTransform = transformString; - this.rippleElement_.style.transform = transformString; - if (start) { - this.rippleElement_.classList.remove(this.CssClasses_.IS_ANIMATING); - } else { - this.rippleElement_.classList.add(this.CssClasses_.IS_ANIMATING); - } - } - }; - /** - * Handles an animation frame. - */ - this.animFrameHandler = function () { - if (this.frameCount_-- > 0) { - window.requestAnimationFrame(this.animFrameHandler.bind(this)); - } else { - this.setRippleStyles(false); - } - }; - } - } -}; -/** - * Downgrade the component - * - * @private - */ -MaterialRipple.prototype.mdlDowngrade_ = function () { - this.element_.removeEventListener('mousedown', this.boundDownHandler); - this.element_.removeEventListener('touchstart', this.boundDownHandler); - this.element_.removeEventListener('mouseup', this.boundUpHandler); - this.element_.removeEventListener('mouseleave', this.boundUpHandler); - this.element_.removeEventListener('touchend', this.boundUpHandler); - this.element_.removeEventListener('blur', this.boundUpHandler); -}; -/** - * Public alias for the downgrade method. - * - * @public - */ -MaterialRipple.prototype.mdlDowngrade = MaterialRipple.prototype.mdlDowngrade_; -MaterialRipple.prototype['mdlDowngrade'] = MaterialRipple.prototype.mdlDowngrade; -// The component registers itself. It can assume componentHandler is available -// in the global scope. -componentHandler.register({ - constructor: MaterialRipple, - classAsString: 'MaterialRipple', - cssClass: 'mdl-js-ripple-effect', - widget: false -}); -}()); diff --git a/examples/scalajs-play-core-react/server/src/main/assets/material/material.min.css b/examples/scalajs-play-core-react/server/src/main/assets/material/material.min.css deleted file mode 100644 index 7a2fb0a..0000000 --- a/examples/scalajs-play-core-react/server/src/main/assets/material/material.min.css +++ /dev/null @@ -1,9 +0,0 @@ -/** - * material-design-lite - Material Design Components in CSS, JS and HTML - * @version v1.0.6 - * @license Apache-2.0 - * @copyright 2015 Google, Inc. - * @link https://github.com/google/material-design-lite - */ -@charset "UTF-8";html{color:rgba(0,0,0,.87)}::-moz-selection{background:#b3d4fc;text-shadow:none}::selection{background:#b3d4fc;text-shadow:none}hr{display:block;height:1px;border:0;border-top:1px solid #ccc;margin:1em 0;padding:0}audio,canvas,iframe,img,svg,video{vertical-align:middle}fieldset{border:0;margin:0;padding:0}textarea{resize:vertical}.browserupgrade{margin:.2em 0;background:#ccc;color:#000;padding:.2em 0}.hidden{display:none!important}.visuallyhidden{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.visuallyhidden.focusable:active,.visuallyhidden.focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}.invisible{visibility:hidden}.clearfix:before,.clearfix:after{content:" ";display:table}.clearfix:after{clear:both}@media print{*,*:before,*:after,*:first-letter,*:first-line{background:0 0!important;color:#000!important;box-shadow:none!important;text-shadow:none!important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href)")"}abbr[title]:after{content:" (" attr(title)")"}a[href^="#"]:after,a[href^="javascript:"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100%!important}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}}a,.mdl-accordion,.mdl-button,.mdl-card,.mdl-checkbox,.mdl-dropdown-menu,.mdl-icon-toggle,.mdl-item,.mdl-radio,.mdl-slider,.mdl-switch,.mdl-tabs__tab{-webkit-tap-highlight-color:transparent;-webkit-tap-highlight-color:rgba(255,255,255,0)}html{width:100%;height:100%;-ms-touch-action:manipulation;touch-action:manipulation}body{width:100%;min-height:100%;margin:0}main{display:block}*[hidden]{display:none!important}html,body{font-family:"Helvetica","Arial",sans-serif;font-size:14px;font-weight:400;line-height:20px}h1,h2,h3,h4,h5,h6,p{padding:0}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small{font-family:"Roboto","Helvetica","Arial",sans-serif;font-weight:400;line-height:1.35;letter-spacing:-.02em;opacity:.54;font-size:.6em}h1{font-size:56px;line-height:1.35;letter-spacing:-.02em;margin:24px 0}h1,h2{font-family:"Roboto","Helvetica","Arial",sans-serif;font-weight:400}h2{font-size:45px;line-height:48px}h2,h3{margin:24px 0}h3{font-size:34px;line-height:40px}h3,h4{font-family:"Roboto","Helvetica","Arial",sans-serif;font-weight:400}h4{font-size:24px;line-height:32px;-moz-osx-font-smoothing:grayscale;margin:24px 0 16px}h5{font-size:20px;font-weight:500;line-height:1;letter-spacing:.02em}h5,h6{font-family:"Roboto","Helvetica","Arial",sans-serif;margin:24px 0 16px}h6{font-size:16px;letter-spacing:.04em}h6,p{font-weight:400;line-height:24px}p{font-size:14px;letter-spacing:0;margin:0 0 16px}a{color:#ff4081;font-weight:500}blockquote{font-family:"Roboto","Helvetica","Arial",sans-serif;position:relative;font-size:24px;font-weight:300;font-style:italic;line-height:1.35;letter-spacing:.08em}blockquote:before{position:absolute;left:-.5em;content:'“'}blockquote:after{content:'”';margin-left:-.05em}mark{background-color:#f4ff81}dt{font-weight:700}address{font-size:12px;line-height:1;font-style:normal}address,ul,ol{font-weight:400;letter-spacing:0}ul,ol{font-size:14px;line-height:24px}.mdl-typography--display-4,.mdl-typography--display-4-color-contrast{font-family:"Roboto","Helvetica","Arial",sans-serif;font-size:112px;font-weight:300;line-height:1;letter-spacing:-.04em}.mdl-typography--display-4-color-contrast{opacity:.54}.mdl-typography--display-3,.mdl-typography--display-3-color-contrast{font-family:"Roboto","Helvetica","Arial",sans-serif;font-size:56px;font-weight:400;line-height:1.35;letter-spacing:-.02em}.mdl-typography--display-3-color-contrast{opacity:.54}.mdl-typography--display-2,.mdl-typography--display-2-color-contrast{font-family:"Roboto","Helvetica","Arial",sans-serif;font-size:45px;font-weight:400;line-height:48px}.mdl-typography--display-2-color-contrast{opacity:.54}.mdl-typography--display-1,.mdl-typography--display-1-color-contrast{font-family:"Roboto","Helvetica","Arial",sans-serif;font-size:34px;font-weight:400;line-height:40px}.mdl-typography--display-1-color-contrast{opacity:.54}.mdl-typography--headline,.mdl-typography--headline-color-contrast{font-family:"Roboto","Helvetica","Arial",sans-serif;font-size:24px;font-weight:400;line-height:32px;-moz-osx-font-smoothing:grayscale}.mdl-typography--headline-color-contrast{opacity:.87}.mdl-typography--title,.mdl-typography--title-color-contrast{font-family:"Roboto","Helvetica","Arial",sans-serif;font-size:20px;font-weight:500;line-height:1;letter-spacing:.02em}.mdl-typography--title-color-contrast{opacity:.87}.mdl-typography--subhead,.mdl-typography--subhead-color-contrast{font-family:"Roboto","Helvetica","Arial",sans-serif;font-size:16px;font-weight:400;line-height:24px;letter-spacing:.04em}.mdl-typography--subhead-color-contrast{opacity:.87}.mdl-typography--body-2,.mdl-typography--body-2-color-contrast{font-size:14px;font-weight:700;line-height:24px;letter-spacing:0}.mdl-typography--body-2-color-contrast{opacity:.87}.mdl-typography--body-1,.mdl-typography--body-1-color-contrast{font-size:14px;font-weight:400;line-height:24px;letter-spacing:0}.mdl-typography--body-1-color-contrast{opacity:.87}.mdl-typography--body-2-force-preferred-font,.mdl-typography--body-2-force-preferred-font-color-contrast{font-family:"Roboto","Helvetica","Arial",sans-serif;font-size:14px;font-weight:500;line-height:24px;letter-spacing:0}.mdl-typography--body-2-force-preferred-font-color-contrast{opacity:.87}.mdl-typography--body-1-force-preferred-font,.mdl-typography--body-1-force-preferred-font-color-contrast{font-family:"Roboto","Helvetica","Arial",sans-serif;font-size:14px;font-weight:400;line-height:24px;letter-spacing:0}.mdl-typography--body-1-force-preferred-font-color-contrast{opacity:.87}.mdl-typography--caption,.mdl-typography--caption-force-preferred-font{font-size:12px;font-weight:400;line-height:1;letter-spacing:0}.mdl-typography--caption-force-preferred-font{font-family:"Roboto","Helvetica","Arial",sans-serif}.mdl-typography--caption-color-contrast,.mdl-typography--caption-force-preferred-font-color-contrast{font-size:12px;font-weight:400;line-height:1;letter-spacing:0;opacity:.54}.mdl-typography--caption-force-preferred-font-color-contrast,.mdl-typography--menu{font-family:"Roboto","Helvetica","Arial",sans-serif}.mdl-typography--menu{font-size:14px;font-weight:500;line-height:1;letter-spacing:0}.mdl-typography--menu-color-contrast{opacity:.87}.mdl-typography--menu-color-contrast,.mdl-typography--button,.mdl-typography--button-color-contrast{font-family:"Roboto","Helvetica","Arial",sans-serif;font-size:14px;font-weight:500;line-height:1;letter-spacing:0}.mdl-typography--button,.mdl-typography--button-color-contrast{text-transform:uppercase}.mdl-typography--button-color-contrast{opacity:.87}.mdl-typography--text-left{text-align:left}.mdl-typography--text-right{text-align:right}.mdl-typography--text-center{text-align:center}.mdl-typography--text-justify{text-align:justify}.mdl-typography--text-nowrap{white-space:nowrap}.mdl-typography--text-lowercase{text-transform:lowercase}.mdl-typography--text-uppercase{text-transform:uppercase}.mdl-typography--text-capitalize{text-transform:capitalize}.mdl-typography--font-thin{font-weight:200!important}.mdl-typography--font-light{font-weight:300!important}.mdl-typography--font-regular{font-weight:400!important}.mdl-typography--font-medium{font-weight:500!important}.mdl-typography--font-bold{font-weight:700!important}.mdl-typography--font-black{font-weight:900!important}.mdl-color-text--red{color:#f44336 !important}.mdl-color--red{background-color:#f44336 !important}.mdl-color-text--red-50{color:#ffebee !important}.mdl-color--red-50{background-color:#ffebee !important}.mdl-color-text--red-100{color:#ffcdd2 !important}.mdl-color--red-100{background-color:#ffcdd2 !important}.mdl-color-text--red-200{color:#ef9a9a !important}.mdl-color--red-200{background-color:#ef9a9a !important}.mdl-color-text--red-300{color:#e57373 !important}.mdl-color--red-300{background-color:#e57373 !important}.mdl-color-text--red-400{color:#ef5350 !important}.mdl-color--red-400{background-color:#ef5350 !important}.mdl-color-text--red-500{color:#f44336 !important}.mdl-color--red-500{background-color:#f44336 !important}.mdl-color-text--red-600{color:#e53935 !important}.mdl-color--red-600{background-color:#e53935 !important}.mdl-color-text--red-700{color:#d32f2f !important}.mdl-color--red-700{background-color:#d32f2f !important}.mdl-color-text--red-800{color:#c62828 !important}.mdl-color--red-800{background-color:#c62828 !important}.mdl-color-text--red-900{color:#b71c1c !important}.mdl-color--red-900{background-color:#b71c1c !important}.mdl-color-text--red-A100{color:#ff8a80 !important}.mdl-color--red-A100{background-color:#ff8a80 !important}.mdl-color-text--red-A200{color:#ff5252 !important}.mdl-color--red-A200{background-color:#ff5252 !important}.mdl-color-text--red-A400{color:#ff1744 !important}.mdl-color--red-A400{background-color:#ff1744 !important}.mdl-color-text--red-A700{color:#d50000 !important}.mdl-color--red-A700{background-color:#d50000 !important}.mdl-color-text--pink{color:#e91e63 !important}.mdl-color--pink{background-color:#e91e63 !important}.mdl-color-text--pink-50{color:#fce4ec !important}.mdl-color--pink-50{background-color:#fce4ec !important}.mdl-color-text--pink-100{color:#f8bbd0 !important}.mdl-color--pink-100{background-color:#f8bbd0 !important}.mdl-color-text--pink-200{color:#f48fb1 !important}.mdl-color--pink-200{background-color:#f48fb1 !important}.mdl-color-text--pink-300{color:#f06292 !important}.mdl-color--pink-300{background-color:#f06292 !important}.mdl-color-text--pink-400{color:#ec407a !important}.mdl-color--pink-400{background-color:#ec407a !important}.mdl-color-text--pink-500{color:#e91e63 !important}.mdl-color--pink-500{background-color:#e91e63 !important}.mdl-color-text--pink-600{color:#d81b60 !important}.mdl-color--pink-600{background-color:#d81b60 !important}.mdl-color-text--pink-700{color:#c2185b !important}.mdl-color--pink-700{background-color:#c2185b !important}.mdl-color-text--pink-800{color:#ad1457 !important}.mdl-color--pink-800{background-color:#ad1457 !important}.mdl-color-text--pink-900{color:#880e4f !important}.mdl-color--pink-900{background-color:#880e4f !important}.mdl-color-text--pink-A100{color:#ff80ab !important}.mdl-color--pink-A100{background-color:#ff80ab !important}.mdl-color-text--pink-A200{color:#ff4081 !important}.mdl-color--pink-A200{background-color:#ff4081 !important}.mdl-color-text--pink-A400{color:#f50057 !important}.mdl-color--pink-A400{background-color:#f50057 !important}.mdl-color-text--pink-A700{color:#c51162 !important}.mdl-color--pink-A700{background-color:#c51162 !important}.mdl-color-text--purple{color:#9c27b0 !important}.mdl-color--purple{background-color:#9c27b0 !important}.mdl-color-text--purple-50{color:#f3e5f5 !important}.mdl-color--purple-50{background-color:#f3e5f5 !important}.mdl-color-text--purple-100{color:#e1bee7 !important}.mdl-color--purple-100{background-color:#e1bee7 !important}.mdl-color-text--purple-200{color:#ce93d8 !important}.mdl-color--purple-200{background-color:#ce93d8 !important}.mdl-color-text--purple-300{color:#ba68c8 !important}.mdl-color--purple-300{background-color:#ba68c8 !important}.mdl-color-text--purple-400{color:#ab47bc !important}.mdl-color--purple-400{background-color:#ab47bc !important}.mdl-color-text--purple-500{color:#9c27b0 !important}.mdl-color--purple-500{background-color:#9c27b0 !important}.mdl-color-text--purple-600{color:#8e24aa !important}.mdl-color--purple-600{background-color:#8e24aa !important}.mdl-color-text--purple-700{color:#7b1fa2 !important}.mdl-color--purple-700{background-color:#7b1fa2 !important}.mdl-color-text--purple-800{color:#6a1b9a !important}.mdl-color--purple-800{background-color:#6a1b9a !important}.mdl-color-text--purple-900{color:#4a148c !important}.mdl-color--purple-900{background-color:#4a148c !important}.mdl-color-text--purple-A100{color:#ea80fc !important}.mdl-color--purple-A100{background-color:#ea80fc !important}.mdl-color-text--purple-A200{color:#e040fb !important}.mdl-color--purple-A200{background-color:#e040fb !important}.mdl-color-text--purple-A400{color:#d500f9 !important}.mdl-color--purple-A400{background-color:#d500f9 !important}.mdl-color-text--purple-A700{color:#a0f !important}.mdl-color--purple-A700{background-color:#a0f !important}.mdl-color-text--deep-purple{color:#673ab7 !important}.mdl-color--deep-purple{background-color:#673ab7 !important}.mdl-color-text--deep-purple-50{color:#ede7f6 !important}.mdl-color--deep-purple-50{background-color:#ede7f6 !important}.mdl-color-text--deep-purple-100{color:#d1c4e9 !important}.mdl-color--deep-purple-100{background-color:#d1c4e9 !important}.mdl-color-text--deep-purple-200{color:#b39ddb !important}.mdl-color--deep-purple-200{background-color:#b39ddb !important}.mdl-color-text--deep-purple-300{color:#9575cd !important}.mdl-color--deep-purple-300{background-color:#9575cd !important}.mdl-color-text--deep-purple-400{color:#7e57c2 !important}.mdl-color--deep-purple-400{background-color:#7e57c2 !important}.mdl-color-text--deep-purple-500{color:#673ab7 !important}.mdl-color--deep-purple-500{background-color:#673ab7 !important}.mdl-color-text--deep-purple-600{color:#5e35b1 !important}.mdl-color--deep-purple-600{background-color:#5e35b1 !important}.mdl-color-text--deep-purple-700{color:#512da8 !important}.mdl-color--deep-purple-700{background-color:#512da8 !important}.mdl-color-text--deep-purple-800{color:#4527a0 !important}.mdl-color--deep-purple-800{background-color:#4527a0 !important}.mdl-color-text--deep-purple-900{color:#311b92 !important}.mdl-color--deep-purple-900{background-color:#311b92 !important}.mdl-color-text--deep-purple-A100{color:#b388ff !important}.mdl-color--deep-purple-A100{background-color:#b388ff !important}.mdl-color-text--deep-purple-A200{color:#7c4dff !important}.mdl-color--deep-purple-A200{background-color:#7c4dff !important}.mdl-color-text--deep-purple-A400{color:#651fff !important}.mdl-color--deep-purple-A400{background-color:#651fff !important}.mdl-color-text--deep-purple-A700{color:#6200ea !important}.mdl-color--deep-purple-A700{background-color:#6200ea !important}.mdl-color-text--indigo{color:#3f51b5 !important}.mdl-color--indigo{background-color:#3f51b5 !important}.mdl-color-text--indigo-50{color:#e8eaf6 !important}.mdl-color--indigo-50{background-color:#e8eaf6 !important}.mdl-color-text--indigo-100{color:#c5cae9 !important}.mdl-color--indigo-100{background-color:#c5cae9 !important}.mdl-color-text--indigo-200{color:#9fa8da !important}.mdl-color--indigo-200{background-color:#9fa8da !important}.mdl-color-text--indigo-300{color:#7986cb !important}.mdl-color--indigo-300{background-color:#7986cb !important}.mdl-color-text--indigo-400{color:#5c6bc0 !important}.mdl-color--indigo-400{background-color:#5c6bc0 !important}.mdl-color-text--indigo-500{color:#3f51b5 !important}.mdl-color--indigo-500{background-color:#3f51b5 !important}.mdl-color-text--indigo-600{color:#3949ab !important}.mdl-color--indigo-600{background-color:#3949ab !important}.mdl-color-text--indigo-700{color:#303f9f !important}.mdl-color--indigo-700{background-color:#303f9f !important}.mdl-color-text--indigo-800{color:#283593 !important}.mdl-color--indigo-800{background-color:#283593 !important}.mdl-color-text--indigo-900{color:#1a237e !important}.mdl-color--indigo-900{background-color:#1a237e !important}.mdl-color-text--indigo-A100{color:#8c9eff !important}.mdl-color--indigo-A100{background-color:#8c9eff !important}.mdl-color-text--indigo-A200{color:#536dfe !important}.mdl-color--indigo-A200{background-color:#536dfe !important}.mdl-color-text--indigo-A400{color:#3d5afe !important}.mdl-color--indigo-A400{background-color:#3d5afe !important}.mdl-color-text--indigo-A700{color:#304ffe !important}.mdl-color--indigo-A700{background-color:#304ffe !important}.mdl-color-text--blue{color:#2196f3 !important}.mdl-color--blue{background-color:#2196f3 !important}.mdl-color-text--blue-50{color:#e3f2fd !important}.mdl-color--blue-50{background-color:#e3f2fd !important}.mdl-color-text--blue-100{color:#bbdefb !important}.mdl-color--blue-100{background-color:#bbdefb !important}.mdl-color-text--blue-200{color:#90caf9 !important}.mdl-color--blue-200{background-color:#90caf9 !important}.mdl-color-text--blue-300{color:#64b5f6 !important}.mdl-color--blue-300{background-color:#64b5f6 !important}.mdl-color-text--blue-400{color:#42a5f5 !important}.mdl-color--blue-400{background-color:#42a5f5 !important}.mdl-color-text--blue-500{color:#2196f3 !important}.mdl-color--blue-500{background-color:#2196f3 !important}.mdl-color-text--blue-600{color:#1e88e5 !important}.mdl-color--blue-600{background-color:#1e88e5 !important}.mdl-color-text--blue-700{color:#1976d2 !important}.mdl-color--blue-700{background-color:#1976d2 !important}.mdl-color-text--blue-800{color:#1565c0 !important}.mdl-color--blue-800{background-color:#1565c0 !important}.mdl-color-text--blue-900{color:#0d47a1 !important}.mdl-color--blue-900{background-color:#0d47a1 !important}.mdl-color-text--blue-A100{color:#82b1ff !important}.mdl-color--blue-A100{background-color:#82b1ff !important}.mdl-color-text--blue-A200{color:#448aff !important}.mdl-color--blue-A200{background-color:#448aff !important}.mdl-color-text--blue-A400{color:#2979ff !important}.mdl-color--blue-A400{background-color:#2979ff !important}.mdl-color-text--blue-A700{color:#2962ff !important}.mdl-color--blue-A700{background-color:#2962ff !important}.mdl-color-text--light-blue{color:#03a9f4 !important}.mdl-color--light-blue{background-color:#03a9f4 !important}.mdl-color-text--light-blue-50{color:#e1f5fe !important}.mdl-color--light-blue-50{background-color:#e1f5fe !important}.mdl-color-text--light-blue-100{color:#b3e5fc !important}.mdl-color--light-blue-100{background-color:#b3e5fc !important}.mdl-color-text--light-blue-200{color:#81d4fa !important}.mdl-color--light-blue-200{background-color:#81d4fa !important}.mdl-color-text--light-blue-300{color:#4fc3f7 !important}.mdl-color--light-blue-300{background-color:#4fc3f7 !important}.mdl-color-text--light-blue-400{color:#29b6f6 !important}.mdl-color--light-blue-400{background-color:#29b6f6 !important}.mdl-color-text--light-blue-500{color:#03a9f4 !important}.mdl-color--light-blue-500{background-color:#03a9f4 !important}.mdl-color-text--light-blue-600{color:#039be5 !important}.mdl-color--light-blue-600{background-color:#039be5 !important}.mdl-color-text--light-blue-700{color:#0288d1 !important}.mdl-color--light-blue-700{background-color:#0288d1 !important}.mdl-color-text--light-blue-800{color:#0277bd !important}.mdl-color--light-blue-800{background-color:#0277bd !important}.mdl-color-text--light-blue-900{color:#01579b !important}.mdl-color--light-blue-900{background-color:#01579b !important}.mdl-color-text--light-blue-A100{color:#80d8ff !important}.mdl-color--light-blue-A100{background-color:#80d8ff !important}.mdl-color-text--light-blue-A200{color:#40c4ff !important}.mdl-color--light-blue-A200{background-color:#40c4ff !important}.mdl-color-text--light-blue-A400{color:#00b0ff !important}.mdl-color--light-blue-A400{background-color:#00b0ff !important}.mdl-color-text--light-blue-A700{color:#0091ea !important}.mdl-color--light-blue-A700{background-color:#0091ea !important}.mdl-color-text--cyan{color:#00bcd4 !important}.mdl-color--cyan{background-color:#00bcd4 !important}.mdl-color-text--cyan-50{color:#e0f7fa !important}.mdl-color--cyan-50{background-color:#e0f7fa !important}.mdl-color-text--cyan-100{color:#b2ebf2 !important}.mdl-color--cyan-100{background-color:#b2ebf2 !important}.mdl-color-text--cyan-200{color:#80deea !important}.mdl-color--cyan-200{background-color:#80deea !important}.mdl-color-text--cyan-300{color:#4dd0e1 !important}.mdl-color--cyan-300{background-color:#4dd0e1 !important}.mdl-color-text--cyan-400{color:#26c6da !important}.mdl-color--cyan-400{background-color:#26c6da !important}.mdl-color-text--cyan-500{color:#00bcd4 !important}.mdl-color--cyan-500{background-color:#00bcd4 !important}.mdl-color-text--cyan-600{color:#00acc1 !important}.mdl-color--cyan-600{background-color:#00acc1 !important}.mdl-color-text--cyan-700{color:#0097a7 !important}.mdl-color--cyan-700{background-color:#0097a7 !important}.mdl-color-text--cyan-800{color:#00838f !important}.mdl-color--cyan-800{background-color:#00838f !important}.mdl-color-text--cyan-900{color:#006064 !important}.mdl-color--cyan-900{background-color:#006064 !important}.mdl-color-text--cyan-A100{color:#84ffff !important}.mdl-color--cyan-A100{background-color:#84ffff !important}.mdl-color-text--cyan-A200{color:#18ffff !important}.mdl-color--cyan-A200{background-color:#18ffff !important}.mdl-color-text--cyan-A400{color:#00e5ff !important}.mdl-color--cyan-A400{background-color:#00e5ff !important}.mdl-color-text--cyan-A700{color:#00b8d4 !important}.mdl-color--cyan-A700{background-color:#00b8d4 !important}.mdl-color-text--teal{color:#009688 !important}.mdl-color--teal{background-color:#009688 !important}.mdl-color-text--teal-50{color:#e0f2f1 !important}.mdl-color--teal-50{background-color:#e0f2f1 !important}.mdl-color-text--teal-100{color:#b2dfdb !important}.mdl-color--teal-100{background-color:#b2dfdb !important}.mdl-color-text--teal-200{color:#80cbc4 !important}.mdl-color--teal-200{background-color:#80cbc4 !important}.mdl-color-text--teal-300{color:#4db6ac !important}.mdl-color--teal-300{background-color:#4db6ac !important}.mdl-color-text--teal-400{color:#26a69a !important}.mdl-color--teal-400{background-color:#26a69a !important}.mdl-color-text--teal-500{color:#009688 !important}.mdl-color--teal-500{background-color:#009688 !important}.mdl-color-text--teal-600{color:#00897b !important}.mdl-color--teal-600{background-color:#00897b !important}.mdl-color-text--teal-700{color:#00796b !important}.mdl-color--teal-700{background-color:#00796b !important}.mdl-color-text--teal-800{color:#00695c !important}.mdl-color--teal-800{background-color:#00695c !important}.mdl-color-text--teal-900{color:#004d40 !important}.mdl-color--teal-900{background-color:#004d40 !important}.mdl-color-text--teal-A100{color:#a7ffeb !important}.mdl-color--teal-A100{background-color:#a7ffeb !important}.mdl-color-text--teal-A200{color:#64ffda !important}.mdl-color--teal-A200{background-color:#64ffda !important}.mdl-color-text--teal-A400{color:#1de9b6 !important}.mdl-color--teal-A400{background-color:#1de9b6 !important}.mdl-color-text--teal-A700{color:#00bfa5 !important}.mdl-color--teal-A700{background-color:#00bfa5 !important}.mdl-color-text--green{color:#4caf50 !important}.mdl-color--green{background-color:#4caf50 !important}.mdl-color-text--green-50{color:#e8f5e9 !important}.mdl-color--green-50{background-color:#e8f5e9 !important}.mdl-color-text--green-100{color:#c8e6c9 !important}.mdl-color--green-100{background-color:#c8e6c9 !important}.mdl-color-text--green-200{color:#a5d6a7 !important}.mdl-color--green-200{background-color:#a5d6a7 !important}.mdl-color-text--green-300{color:#81c784 !important}.mdl-color--green-300{background-color:#81c784 !important}.mdl-color-text--green-400{color:#66bb6a !important}.mdl-color--green-400{background-color:#66bb6a !important}.mdl-color-text--green-500{color:#4caf50 !important}.mdl-color--green-500{background-color:#4caf50 !important}.mdl-color-text--green-600{color:#43a047 !important}.mdl-color--green-600{background-color:#43a047 !important}.mdl-color-text--green-700{color:#388e3c !important}.mdl-color--green-700{background-color:#388e3c !important}.mdl-color-text--green-800{color:#2e7d32 !important}.mdl-color--green-800{background-color:#2e7d32 !important}.mdl-color-text--green-900{color:#1b5e20 !important}.mdl-color--green-900{background-color:#1b5e20 !important}.mdl-color-text--green-A100{color:#b9f6ca !important}.mdl-color--green-A100{background-color:#b9f6ca !important}.mdl-color-text--green-A200{color:#69f0ae !important}.mdl-color--green-A200{background-color:#69f0ae !important}.mdl-color-text--green-A400{color:#00e676 !important}.mdl-color--green-A400{background-color:#00e676 !important}.mdl-color-text--green-A700{color:#00c853 !important}.mdl-color--green-A700{background-color:#00c853 !important}.mdl-color-text--light-green{color:#8bc34a !important}.mdl-color--light-green{background-color:#8bc34a !important}.mdl-color-text--light-green-50{color:#f1f8e9 !important}.mdl-color--light-green-50{background-color:#f1f8e9 !important}.mdl-color-text--light-green-100{color:#dcedc8 !important}.mdl-color--light-green-100{background-color:#dcedc8 !important}.mdl-color-text--light-green-200{color:#c5e1a5 !important}.mdl-color--light-green-200{background-color:#c5e1a5 !important}.mdl-color-text--light-green-300{color:#aed581 !important}.mdl-color--light-green-300{background-color:#aed581 !important}.mdl-color-text--light-green-400{color:#9ccc65 !important}.mdl-color--light-green-400{background-color:#9ccc65 !important}.mdl-color-text--light-green-500{color:#8bc34a !important}.mdl-color--light-green-500{background-color:#8bc34a !important}.mdl-color-text--light-green-600{color:#7cb342 !important}.mdl-color--light-green-600{background-color:#7cb342 !important}.mdl-color-text--light-green-700{color:#689f38 !important}.mdl-color--light-green-700{background-color:#689f38 !important}.mdl-color-text--light-green-800{color:#558b2f !important}.mdl-color--light-green-800{background-color:#558b2f !important}.mdl-color-text--light-green-900{color:#33691e !important}.mdl-color--light-green-900{background-color:#33691e !important}.mdl-color-text--light-green-A100{color:#ccff90 !important}.mdl-color--light-green-A100{background-color:#ccff90 !important}.mdl-color-text--light-green-A200{color:#b2ff59 !important}.mdl-color--light-green-A200{background-color:#b2ff59 !important}.mdl-color-text--light-green-A400{color:#76ff03 !important}.mdl-color--light-green-A400{background-color:#76ff03 !important}.mdl-color-text--light-green-A700{color:#64dd17 !important}.mdl-color--light-green-A700{background-color:#64dd17 !important}.mdl-color-text--lime{color:#cddc39 !important}.mdl-color--lime{background-color:#cddc39 !important}.mdl-color-text--lime-50{color:#f9fbe7 !important}.mdl-color--lime-50{background-color:#f9fbe7 !important}.mdl-color-text--lime-100{color:#f0f4c3 !important}.mdl-color--lime-100{background-color:#f0f4c3 !important}.mdl-color-text--lime-200{color:#e6ee9c !important}.mdl-color--lime-200{background-color:#e6ee9c !important}.mdl-color-text--lime-300{color:#dce775 !important}.mdl-color--lime-300{background-color:#dce775 !important}.mdl-color-text--lime-400{color:#d4e157 !important}.mdl-color--lime-400{background-color:#d4e157 !important}.mdl-color-text--lime-500{color:#cddc39 !important}.mdl-color--lime-500{background-color:#cddc39 !important}.mdl-color-text--lime-600{color:#c0ca33 !important}.mdl-color--lime-600{background-color:#c0ca33 !important}.mdl-color-text--lime-700{color:#afb42b !important}.mdl-color--lime-700{background-color:#afb42b !important}.mdl-color-text--lime-800{color:#9e9d24 !important}.mdl-color--lime-800{background-color:#9e9d24 !important}.mdl-color-text--lime-900{color:#827717 !important}.mdl-color--lime-900{background-color:#827717 !important}.mdl-color-text--lime-A100{color:#f4ff81 !important}.mdl-color--lime-A100{background-color:#f4ff81 !important}.mdl-color-text--lime-A200{color:#eeff41 !important}.mdl-color--lime-A200{background-color:#eeff41 !important}.mdl-color-text--lime-A400{color:#c6ff00 !important}.mdl-color--lime-A400{background-color:#c6ff00 !important}.mdl-color-text--lime-A700{color:#aeea00 !important}.mdl-color--lime-A700{background-color:#aeea00 !important}.mdl-color-text--yellow{color:#ffeb3b !important}.mdl-color--yellow{background-color:#ffeb3b !important}.mdl-color-text--yellow-50{color:#fffde7 !important}.mdl-color--yellow-50{background-color:#fffde7 !important}.mdl-color-text--yellow-100{color:#fff9c4 !important}.mdl-color--yellow-100{background-color:#fff9c4 !important}.mdl-color-text--yellow-200{color:#fff59d !important}.mdl-color--yellow-200{background-color:#fff59d !important}.mdl-color-text--yellow-300{color:#fff176 !important}.mdl-color--yellow-300{background-color:#fff176 !important}.mdl-color-text--yellow-400{color:#ffee58 !important}.mdl-color--yellow-400{background-color:#ffee58 !important}.mdl-color-text--yellow-500{color:#ffeb3b !important}.mdl-color--yellow-500{background-color:#ffeb3b !important}.mdl-color-text--yellow-600{color:#fdd835 !important}.mdl-color--yellow-600{background-color:#fdd835 !important}.mdl-color-text--yellow-700{color:#fbc02d !important}.mdl-color--yellow-700{background-color:#fbc02d !important}.mdl-color-text--yellow-800{color:#f9a825 !important}.mdl-color--yellow-800{background-color:#f9a825 !important}.mdl-color-text--yellow-900{color:#f57f17 !important}.mdl-color--yellow-900{background-color:#f57f17 !important}.mdl-color-text--yellow-A100{color:#ffff8d !important}.mdl-color--yellow-A100{background-color:#ffff8d !important}.mdl-color-text--yellow-A200{color:#ff0 !important}.mdl-color--yellow-A200{background-color:#ff0 !important}.mdl-color-text--yellow-A400{color:#ffea00 !important}.mdl-color--yellow-A400{background-color:#ffea00 !important}.mdl-color-text--yellow-A700{color:#ffd600 !important}.mdl-color--yellow-A700{background-color:#ffd600 !important}.mdl-color-text--amber{color:#ffc107 !important}.mdl-color--amber{background-color:#ffc107 !important}.mdl-color-text--amber-50{color:#fff8e1 !important}.mdl-color--amber-50{background-color:#fff8e1 !important}.mdl-color-text--amber-100{color:#ffecb3 !important}.mdl-color--amber-100{background-color:#ffecb3 !important}.mdl-color-text--amber-200{color:#ffe082 !important}.mdl-color--amber-200{background-color:#ffe082 !important}.mdl-color-text--amber-300{color:#ffd54f !important}.mdl-color--amber-300{background-color:#ffd54f !important}.mdl-color-text--amber-400{color:#ffca28 !important}.mdl-color--amber-400{background-color:#ffca28 !important}.mdl-color-text--amber-500{color:#ffc107 !important}.mdl-color--amber-500{background-color:#ffc107 !important}.mdl-color-text--amber-600{color:#ffb300 !important}.mdl-color--amber-600{background-color:#ffb300 !important}.mdl-color-text--amber-700{color:#ffa000 !important}.mdl-color--amber-700{background-color:#ffa000 !important}.mdl-color-text--amber-800{color:#ff8f00 !important}.mdl-color--amber-800{background-color:#ff8f00 !important}.mdl-color-text--amber-900{color:#ff6f00 !important}.mdl-color--amber-900{background-color:#ff6f00 !important}.mdl-color-text--amber-A100{color:#ffe57f !important}.mdl-color--amber-A100{background-color:#ffe57f !important}.mdl-color-text--amber-A200{color:#ffd740 !important}.mdl-color--amber-A200{background-color:#ffd740 !important}.mdl-color-text--amber-A400{color:#ffc400 !important}.mdl-color--amber-A400{background-color:#ffc400 !important}.mdl-color-text--amber-A700{color:#ffab00 !important}.mdl-color--amber-A700{background-color:#ffab00 !important}.mdl-color-text--orange{color:#ff9800 !important}.mdl-color--orange{background-color:#ff9800 !important}.mdl-color-text--orange-50{color:#fff3e0 !important}.mdl-color--orange-50{background-color:#fff3e0 !important}.mdl-color-text--orange-100{color:#ffe0b2 !important}.mdl-color--orange-100{background-color:#ffe0b2 !important}.mdl-color-text--orange-200{color:#ffcc80 !important}.mdl-color--orange-200{background-color:#ffcc80 !important}.mdl-color-text--orange-300{color:#ffb74d !important}.mdl-color--orange-300{background-color:#ffb74d !important}.mdl-color-text--orange-400{color:#ffa726 !important}.mdl-color--orange-400{background-color:#ffa726 !important}.mdl-color-text--orange-500{color:#ff9800 !important}.mdl-color--orange-500{background-color:#ff9800 !important}.mdl-color-text--orange-600{color:#fb8c00 !important}.mdl-color--orange-600{background-color:#fb8c00 !important}.mdl-color-text--orange-700{color:#f57c00 !important}.mdl-color--orange-700{background-color:#f57c00 !important}.mdl-color-text--orange-800{color:#ef6c00 !important}.mdl-color--orange-800{background-color:#ef6c00 !important}.mdl-color-text--orange-900{color:#e65100 !important}.mdl-color--orange-900{background-color:#e65100 !important}.mdl-color-text--orange-A100{color:#ffd180 !important}.mdl-color--orange-A100{background-color:#ffd180 !important}.mdl-color-text--orange-A200{color:#ffab40 !important}.mdl-color--orange-A200{background-color:#ffab40 !important}.mdl-color-text--orange-A400{color:#ff9100 !important}.mdl-color--orange-A400{background-color:#ff9100 !important}.mdl-color-text--orange-A700{color:#ff6d00 !important}.mdl-color--orange-A700{background-color:#ff6d00 !important}.mdl-color-text--deep-orange{color:#ff5722 !important}.mdl-color--deep-orange{background-color:#ff5722 !important}.mdl-color-text--deep-orange-50{color:#fbe9e7 !important}.mdl-color--deep-orange-50{background-color:#fbe9e7 !important}.mdl-color-text--deep-orange-100{color:#ffccbc !important}.mdl-color--deep-orange-100{background-color:#ffccbc !important}.mdl-color-text--deep-orange-200{color:#ffab91 !important}.mdl-color--deep-orange-200{background-color:#ffab91 !important}.mdl-color-text--deep-orange-300{color:#ff8a65 !important}.mdl-color--deep-orange-300{background-color:#ff8a65 !important}.mdl-color-text--deep-orange-400{color:#ff7043 !important}.mdl-color--deep-orange-400{background-color:#ff7043 !important}.mdl-color-text--deep-orange-500{color:#ff5722 !important}.mdl-color--deep-orange-500{background-color:#ff5722 !important}.mdl-color-text--deep-orange-600{color:#f4511e !important}.mdl-color--deep-orange-600{background-color:#f4511e !important}.mdl-color-text--deep-orange-700{color:#e64a19 !important}.mdl-color--deep-orange-700{background-color:#e64a19 !important}.mdl-color-text--deep-orange-800{color:#d84315 !important}.mdl-color--deep-orange-800{background-color:#d84315 !important}.mdl-color-text--deep-orange-900{color:#bf360c !important}.mdl-color--deep-orange-900{background-color:#bf360c !important}.mdl-color-text--deep-orange-A100{color:#ff9e80 !important}.mdl-color--deep-orange-A100{background-color:#ff9e80 !important}.mdl-color-text--deep-orange-A200{color:#ff6e40 !important}.mdl-color--deep-orange-A200{background-color:#ff6e40 !important}.mdl-color-text--deep-orange-A400{color:#ff3d00 !important}.mdl-color--deep-orange-A400{background-color:#ff3d00 !important}.mdl-color-text--deep-orange-A700{color:#dd2c00 !important}.mdl-color--deep-orange-A700{background-color:#dd2c00 !important}.mdl-color-text--brown{color:#795548 !important}.mdl-color--brown{background-color:#795548 !important}.mdl-color-text--brown-50{color:#efebe9 !important}.mdl-color--brown-50{background-color:#efebe9 !important}.mdl-color-text--brown-100{color:#d7ccc8 !important}.mdl-color--brown-100{background-color:#d7ccc8 !important}.mdl-color-text--brown-200{color:#bcaaa4 !important}.mdl-color--brown-200{background-color:#bcaaa4 !important}.mdl-color-text--brown-300{color:#a1887f !important}.mdl-color--brown-300{background-color:#a1887f !important}.mdl-color-text--brown-400{color:#8d6e63 !important}.mdl-color--brown-400{background-color:#8d6e63 !important}.mdl-color-text--brown-500{color:#795548 !important}.mdl-color--brown-500{background-color:#795548 !important}.mdl-color-text--brown-600{color:#6d4c41 !important}.mdl-color--brown-600{background-color:#6d4c41 !important}.mdl-color-text--brown-700{color:#5d4037 !important}.mdl-color--brown-700{background-color:#5d4037 !important}.mdl-color-text--brown-800{color:#4e342e !important}.mdl-color--brown-800{background-color:#4e342e !important}.mdl-color-text--brown-900{color:#3e2723 !important}.mdl-color--brown-900{background-color:#3e2723 !important}.mdl-color-text--grey{color:#9e9e9e !important}.mdl-color--grey{background-color:#9e9e9e !important}.mdl-color-text--grey-50{color:#fafafa !important}.mdl-color--grey-50{background-color:#fafafa !important}.mdl-color-text--grey-100{color:#f5f5f5 !important}.mdl-color--grey-100{background-color:#f5f5f5 !important}.mdl-color-text--grey-200{color:#eee !important}.mdl-color--grey-200{background-color:#eee !important}.mdl-color-text--grey-300{color:#e0e0e0 !important}.mdl-color--grey-300{background-color:#e0e0e0 !important}.mdl-color-text--grey-400{color:#bdbdbd !important}.mdl-color--grey-400{background-color:#bdbdbd !important}.mdl-color-text--grey-500{color:#9e9e9e !important}.mdl-color--grey-500{background-color:#9e9e9e !important}.mdl-color-text--grey-600{color:#757575 !important}.mdl-color--grey-600{background-color:#757575 !important}.mdl-color-text--grey-700{color:#616161 !important}.mdl-color--grey-700{background-color:#616161 !important}.mdl-color-text--grey-800{color:#424242 !important}.mdl-color--grey-800{background-color:#424242 !important}.mdl-color-text--grey-900{color:#212121 !important}.mdl-color--grey-900{background-color:#212121 !important}.mdl-color-text--blue-grey{color:#607d8b !important}.mdl-color--blue-grey{background-color:#607d8b !important}.mdl-color-text--blue-grey-50{color:#eceff1 !important}.mdl-color--blue-grey-50{background-color:#eceff1 !important}.mdl-color-text--blue-grey-100{color:#cfd8dc !important}.mdl-color--blue-grey-100{background-color:#cfd8dc !important}.mdl-color-text--blue-grey-200{color:#b0bec5 !important}.mdl-color--blue-grey-200{background-color:#b0bec5 !important}.mdl-color-text--blue-grey-300{color:#90a4ae !important}.mdl-color--blue-grey-300{background-color:#90a4ae !important}.mdl-color-text--blue-grey-400{color:#78909c !important}.mdl-color--blue-grey-400{background-color:#78909c !important}.mdl-color-text--blue-grey-500{color:#607d8b !important}.mdl-color--blue-grey-500{background-color:#607d8b !important}.mdl-color-text--blue-grey-600{color:#546e7a !important}.mdl-color--blue-grey-600{background-color:#546e7a !important}.mdl-color-text--blue-grey-700{color:#455a64 !important}.mdl-color--blue-grey-700{background-color:#455a64 !important}.mdl-color-text--blue-grey-800{color:#37474f !important}.mdl-color--blue-grey-800{background-color:#37474f !important}.mdl-color-text--blue-grey-900{color:#263238 !important}.mdl-color--blue-grey-900{background-color:#263238 !important}.mdl-color--black{background-color:#000 !important}.mdl-color-text--black{color:#000 !important}.mdl-color--white{background-color:#fff !important}.mdl-color-text--white{color:#fff !important}.mdl-color--primary{background-color:#3f51b5 !important}.mdl-color--primary-contrast{background-color:#fff !important}.mdl-color--primary-dark{background-color:#303f9f !important}.mdl-color--accent{background-color:#ff4081 !important}.mdl-color--accent-contrast{background-color:#fff !important}.mdl-color-text--primary{color:#3f51b5 !important}.mdl-color-text--primary-contrast{color:#fff !important}.mdl-color-text--primary-dark{color:#303f9f !important}.mdl-color-text--accent{color:#ff4081 !important}.mdl-color-text--accent-contrast{color:#fff !important}.mdl-ripple{background:#000;border-radius:50%;height:50px;left:0;opacity:0;pointer-events:none;position:absolute;top:0;-webkit-transform:translate(-50%,-50%);-ms-transform:translate(-50%,-50%);transform:translate(-50%,-50%);width:50px;overflow:hidden}.mdl-ripple.is-animating{-webkit-transition:-webkit-transform .3s cubic-bezier(0,0,.2,1),width .3s cubic-bezier(0,0,.2,1),height .3s cubic-bezier(0,0,.2,1),opacity .6s cubic-bezier(0,0,.2,1);transition:transform .3s cubic-bezier(0,0,.2,1),width .3s cubic-bezier(0,0,.2,1),height .3s cubic-bezier(0,0,.2,1),opacity .6s cubic-bezier(0,0,.2,1)}.mdl-ripple.is-visible{opacity:.3}.mdl-animation--default,.mdl-animation--fast-out-slow-in{-webkit-transition-timing-function:cubic-bezier(.4,0,.2,1);transition-timing-function:cubic-bezier(.4,0,.2,1)}.mdl-animation--linear-out-slow-in{-webkit-transition-timing-function:cubic-bezier(0,0,.2,1);transition-timing-function:cubic-bezier(0,0,.2,1)}.mdl-animation--fast-out-linear-in{-webkit-transition-timing-function:cubic-bezier(.4,0,1,1);transition-timing-function:cubic-bezier(.4,0,1,1)}.mdl-badge{position:relative;white-space:nowrap;margin-right:24px}.mdl-badge:not([data-badge]){margin-right:auto}.mdl-badge[data-badge]:after{content:attr(data-badge);display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row;-webkit-flex-wrap:wrap;-ms-flex-wrap:wrap;flex-wrap:wrap;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;-webkit-align-content:center;-ms-flex-line-pack:center;align-content:center;-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;position:absolute;top:-11px;right:-24px;font-family:"Roboto","Helvetica","Arial",sans-serif;font-weight:600;font-size:12px;width:22px;height:22px;border-radius:50%;background:#ff4081;color:#fff}.mdl-button .mdl-badge[data-badge]:after{top:-10px;right:-5px}.mdl-badge.mdl-badge--no-background[data-badge]:after{color:#ff4081;background:#fff;box-shadow:0 0 1px gray}.mdl-button{background:0 0;border:none;border-radius:2px;color:#000;position:relative;height:36px;min-width:64px;padding:0 16px;display:inline-block;font-family:"Roboto","Helvetica","Arial",sans-serif;font-size:14px;font-weight:500;text-transform:uppercase;letter-spacing:0;overflow:hidden;will-change:box-shadow,transform;-webkit-transition:box-shadow .2s cubic-bezier(.4,0,1,1),background-color .2s cubic-bezier(.4,0,.2,1),color .2s cubic-bezier(.4,0,.2,1);transition:box-shadow .2s cubic-bezier(.4,0,1,1),background-color .2s cubic-bezier(.4,0,.2,1),color .2s cubic-bezier(.4,0,.2,1);outline:none;cursor:pointer;text-decoration:none;text-align:center;line-height:36px;vertical-align:middle}.mdl-button::-moz-focus-inner{border:0}.mdl-button:hover{background-color:rgba(158,158,158,.2)}.mdl-button:focus:not(:active){background-color:rgba(0,0,0,.12)}.mdl-button:active{background-color:rgba(158,158,158,.4)}.mdl-button.mdl-button--colored{color:#3f51b5}.mdl-button.mdl-button--colored:focus:not(:active){background-color:rgba(0,0,0,.12)}input.mdl-button[type="submit"]{-webkit-appearance:none}.mdl-button--raised{background:rgba(158,158,158,.2);box-shadow:0 2px 2px 0 rgba(0,0,0,.14),0 3px 1px -2px rgba(0,0,0,.2),0 1px 5px 0 rgba(0,0,0,.12)}.mdl-button--raised:active{box-shadow:0 4px 5px 0 rgba(0,0,0,.14),0 1px 10px 0 rgba(0,0,0,.12),0 2px 4px -1px rgba(0,0,0,.2);background-color:rgba(158,158,158,.4)}.mdl-button--raised:focus:not(:active){box-shadow:0 0 8px rgba(0,0,0,.18),0 8px 16px rgba(0,0,0,.36);background-color:rgba(158,158,158,.4)}.mdl-button--raised.mdl-button--colored{background:#3f51b5;color:#fff}.mdl-button--raised.mdl-button--colored:hover{background-color:#3f51b5}.mdl-button--raised.mdl-button--colored:active{background-color:#3f51b5}.mdl-button--raised.mdl-button--colored:focus:not(:active){background-color:#3f51b5}.mdl-button--raised.mdl-button--colored .mdl-ripple{background:#fff}.mdl-button--fab{border-radius:50%;font-size:24px;height:56px;margin:auto;min-width:56px;width:56px;padding:0;overflow:hidden;background:rgba(158,158,158,.2);box-shadow:0 1px 1.5px 0 rgba(0,0,0,.12),0 1px 1px 0 rgba(0,0,0,.24);position:relative;line-height:normal}.mdl-button--fab .material-icons{position:absolute;top:50%;left:50%;-webkit-transform:translate(-12px,-12px);-ms-transform:translate(-12px,-12px);transform:translate(-12px,-12px);line-height:24px;width:24px}.mdl-button--fab.mdl-button--mini-fab{height:40px;min-width:40px;width:40px}.mdl-button--fab .mdl-button__ripple-container{border-radius:50%;-webkit-mask-image:-webkit-radial-gradient(circle,#fff,#000)}.mdl-button--fab:active{box-shadow:0 4px 5px 0 rgba(0,0,0,.14),0 1px 10px 0 rgba(0,0,0,.12),0 2px 4px -1px rgba(0,0,0,.2);background-color:rgba(158,158,158,.4)}.mdl-button--fab:focus:not(:active){box-shadow:0 0 8px rgba(0,0,0,.18),0 8px 16px rgba(0,0,0,.36);background-color:rgba(158,158,158,.4)}.mdl-button--fab.mdl-button--colored{background:#ff4081;color:#fff}.mdl-button--fab.mdl-button--colored:hover{background-color:#ff4081}.mdl-button--fab.mdl-button--colored:focus:not(:active){background-color:#ff4081}.mdl-button--fab.mdl-button--colored:active{background-color:#ff4081}.mdl-button--fab.mdl-button--colored .mdl-ripple{background:#fff}.mdl-button--icon{border-radius:50%;font-size:24px;height:32px;margin-left:0;margin-right:0;min-width:32px;width:32px;padding:0;overflow:hidden;color:inherit;line-height:normal}.mdl-button--icon .material-icons{position:absolute;top:50%;left:50%;-webkit-transform:translate(-12px,-12px);-ms-transform:translate(-12px,-12px);transform:translate(-12px,-12px);line-height:24px;width:24px}.mdl-button--icon.mdl-button--mini-icon{height:24px;min-width:24px;width:24px}.mdl-button--icon.mdl-button--mini-icon .material-icons{top:0;left:0}.mdl-button--icon .mdl-button__ripple-container{border-radius:50%;-webkit-mask-image:-webkit-radial-gradient(circle,#fff,#000)}.mdl-button__ripple-container{display:block;height:100%;left:0;position:absolute;top:0;width:100%;z-index:0;overflow:hidden}.mdl-button[disabled] .mdl-button__ripple-container .mdl-ripple,.mdl-button.mdl-button--disabled .mdl-button__ripple-container .mdl-ripple{background-color:transparent}.mdl-button--primary.mdl-button--primary{color:#3f51b5}.mdl-button--primary.mdl-button--primary .mdl-ripple{background:#fff}.mdl-button--primary.mdl-button--primary.mdl-button--raised,.mdl-button--primary.mdl-button--primary.mdl-button--fab{color:#fff;background-color:#3f51b5}.mdl-button--accent.mdl-button--accent{color:#ff4081}.mdl-button--accent.mdl-button--accent .mdl-ripple{background:#fff}.mdl-button--accent.mdl-button--accent.mdl-button--raised,.mdl-button--accent.mdl-button--accent.mdl-button--fab{color:#fff;background-color:#ff4081}.mdl-button[disabled][disabled],.mdl-button.mdl-button--disabled.mdl-button--disabled{color:rgba(0,0,0,.26);cursor:default;background-color:transparent}.mdl-button--fab[disabled][disabled],.mdl-button--fab.mdl-button--disabled.mdl-button--disabled,.mdl-button--raised[disabled][disabled],.mdl-button--raised.mdl-button--disabled.mdl-button--disabled{background-color:rgba(0,0,0,.12);color:rgba(0,0,0,.26);box-shadow:0 2px 2px 0 rgba(0,0,0,.14),0 3px 1px -2px rgba(0,0,0,.2),0 1px 5px 0 rgba(0,0,0,.12)}.mdl-button--colored[disabled][disabled],.mdl-button--colored.mdl-button--disabled.mdl-button--disabled{color:rgba(0,0,0,.26)}.mdl-button .material-icons{vertical-align:middle}.mdl-card{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;font-size:16px;font-weight:400;min-height:200px;overflow:hidden;width:330px;z-index:1;position:relative;background:#fff;border-radius:2px;box-sizing:border-box}.mdl-card__media{background-color:#ff4081;background-repeat:repeat;background-position:50% 50%;background-size:cover;background-origin:padding-box;background-attachment:scroll;box-sizing:border-box}.mdl-card__title{-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;color:#000;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-pack:stretch;-webkit-justify-content:stretch;-ms-flex-pack:stretch;justify-content:stretch;line-height:normal;padding:16px;-webkit-perspective-origin:165px 56px;perspective-origin:165px 56px;-webkit-transform-origin:165px 56px;-ms-transform-origin:165px 56px;transform-origin:165px 56px;box-sizing:border-box}.mdl-card__title.mdl-card--border{border-bottom:1px solid rgba(0,0,0,.1)}.mdl-card__title-text{-webkit-align-self:flex-end;-ms-flex-item-align:end;align-self:flex-end;color:inherit;display:-webkit-flex;display:-ms-flexbox;display:flex;font-size:24px;font-weight:300;line-height:normal;overflow:hidden;-webkit-transform-origin:149px 48px;-ms-transform-origin:149px 48px;transform-origin:149px 48px;margin:0}.mdl-card__subtitle-text{font-size:14px;color:rgba(0,0,0,.54);margin:0}.mdl-card__supporting-text{color:rgba(0,0,0,.54);font-size:13px;line-height:18px;overflow:hidden;padding:16px;width:90%}.mdl-card__actions{font-size:16px;line-height:normal;width:100%;background-color:transparent;padding:8px;box-sizing:border-box}.mdl-card__actions.mdl-card--border{border-top:1px solid rgba(0,0,0,.1)}.mdl-card--expand{-webkit-box-flex:1;-webkit-flex-grow:1;-ms-flex-positive:1;flex-grow:1}.mdl-card__menu{position:absolute;right:16px;top:16px}.mdl-checkbox{position:relative;z-index:1;vertical-align:middle;display:inline-block;box-sizing:border-box;width:100%;height:24px;margin:0;padding:0}.mdl-checkbox.is-upgraded{padding-left:24px}.mdl-checkbox__input{line-height:24px}.mdl-checkbox.is-upgraded .mdl-checkbox__input{position:absolute;width:0;height:0;margin:0;padding:0;opacity:0;-ms-appearance:none;-moz-appearance:none;-webkit-appearance:none;appearance:none;border:none}.mdl-checkbox__box-outline{position:absolute;top:3px;left:0;display:inline-block;box-sizing:border-box;width:16px;height:16px;margin:0;cursor:pointer;overflow:hidden;border:2px solid rgba(0,0,0,.54);border-radius:2px;z-index:2}.mdl-checkbox.is-checked .mdl-checkbox__box-outline{border:2px solid #3f51b5}.mdl-checkbox.is-disabled .mdl-checkbox__box-outline{border:2px solid rgba(0,0,0,.26);cursor:auto}.mdl-checkbox__focus-helper{position:absolute;top:3px;left:0;display:inline-block;box-sizing:border-box;width:16px;height:16px;border-radius:50%;background-color:transparent}.mdl-checkbox.is-focused .mdl-checkbox__focus-helper{box-shadow:0 0 0 8px rgba(0,0,0,.1);background-color:rgba(0,0,0,.1)}.mdl-checkbox.is-focused.is-checked .mdl-checkbox__focus-helper{box-shadow:0 0 0 8px rgba(63,81,181,.26);background-color:rgba(63,81,181,.26)}.mdl-checkbox__tick-outline{position:absolute;top:0;left:0;height:100%;width:100%;-webkit-mask:url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcKICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICB4bWxuczpjYz0iaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbnMjIgogICB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiCiAgIHhtbG5zOnN2Zz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgdmVyc2lvbj0iMS4xIgogICB2aWV3Qm94PSIwIDAgMSAxIgogICBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWluWU1pbiBtZWV0Ij4KICA8ZGVmcz4KICAgIDxjbGlwUGF0aCBpZD0iY2xpcCI+CiAgICAgIDxwYXRoCiAgICAgICAgIGQ9Ik0gMCwwIDAsMSAxLDEgMSwwIDAsMCB6IE0gMC44NTM0Mzc1LDAuMTY3MTg3NSAwLjk1OTY4NzUsMC4yNzMxMjUgMC40MjkzNzUsMC44MDM0Mzc1IDAuMzIzMTI1LDAuOTA5Njg3NSAwLjIxNzE4NzUsMC44MDM0Mzc1IDAuMDQwMzEyNSwwLjYyNjg3NSAwLjE0NjU2MjUsMC41MjA2MjUgMC4zMjMxMjUsMC42OTc1IDAuODUzNDM3NSwwLjE2NzE4NzUgeiIKICAgICAgICAgc3R5bGU9ImZpbGw6I2ZmZmZmZjtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZSIgLz4KICAgIDwvY2xpcFBhdGg+CiAgICA8bWFzayBpZD0ibWFzayIgbWFza1VuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgbWFza0NvbnRlbnRVbml0cz0ib2JqZWN0Qm91bmRpbmdCb3giPgogICAgICA8cGF0aAogICAgICAgICBkPSJNIDAsMCAwLDEgMSwxIDEsMCAwLDAgeiBNIDAuODUzNDM3NSwwLjE2NzE4NzUgMC45NTk2ODc1LDAuMjczMTI1IDAuNDI5Mzc1LDAuODAzNDM3NSAwLjMyMzEyNSwwLjkwOTY4NzUgMC4yMTcxODc1LDAuODAzNDM3NSAwLjA0MDMxMjUsMC42MjY4NzUgMC4xNDY1NjI1LDAuNTIwNjI1IDAuMzIzMTI1LDAuNjk3NSAwLjg1MzQzNzUsMC4xNjcxODc1IHoiCiAgICAgICAgIHN0eWxlPSJmaWxsOiNmZmZmZmY7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmUiIC8+CiAgICA8L21hc2s+CiAgPC9kZWZzPgogIDxyZWN0CiAgICAgd2lkdGg9IjEiCiAgICAgaGVpZ2h0PSIxIgogICAgIHg9IjAiCiAgICAgeT0iMCIKICAgICBjbGlwLXBhdGg9InVybCgjY2xpcCkiCiAgICAgc3R5bGU9ImZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZSIgLz4KPC9zdmc+Cg==");mask:url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcKICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICB4bWxuczpjYz0iaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbnMjIgogICB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiCiAgIHhtbG5zOnN2Zz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgdmVyc2lvbj0iMS4xIgogICB2aWV3Qm94PSIwIDAgMSAxIgogICBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWluWU1pbiBtZWV0Ij4KICA8ZGVmcz4KICAgIDxjbGlwUGF0aCBpZD0iY2xpcCI+CiAgICAgIDxwYXRoCiAgICAgICAgIGQ9Ik0gMCwwIDAsMSAxLDEgMSwwIDAsMCB6IE0gMC44NTM0Mzc1LDAuMTY3MTg3NSAwLjk1OTY4NzUsMC4yNzMxMjUgMC40MjkzNzUsMC44MDM0Mzc1IDAuMzIzMTI1LDAuOTA5Njg3NSAwLjIxNzE4NzUsMC44MDM0Mzc1IDAuMDQwMzEyNSwwLjYyNjg3NSAwLjE0NjU2MjUsMC41MjA2MjUgMC4zMjMxMjUsMC42OTc1IDAuODUzNDM3NSwwLjE2NzE4NzUgeiIKICAgICAgICAgc3R5bGU9ImZpbGw6I2ZmZmZmZjtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZSIgLz4KICAgIDwvY2xpcFBhdGg+CiAgICA8bWFzayBpZD0ibWFzayIgbWFza1VuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgbWFza0NvbnRlbnRVbml0cz0ib2JqZWN0Qm91bmRpbmdCb3giPgogICAgICA8cGF0aAogICAgICAgICBkPSJNIDAsMCAwLDEgMSwxIDEsMCAwLDAgeiBNIDAuODUzNDM3NSwwLjE2NzE4NzUgMC45NTk2ODc1LDAuMjczMTI1IDAuNDI5Mzc1LDAuODAzNDM3NSAwLjMyMzEyNSwwLjkwOTY4NzUgMC4yMTcxODc1LDAuODAzNDM3NSAwLjA0MDMxMjUsMC42MjY4NzUgMC4xNDY1NjI1LDAuNTIwNjI1IDAuMzIzMTI1LDAuNjk3NSAwLjg1MzQzNzUsMC4xNjcxODc1IHoiCiAgICAgICAgIHN0eWxlPSJmaWxsOiNmZmZmZmY7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmUiIC8+CiAgICA8L21hc2s+CiAgPC9kZWZzPgogIDxyZWN0CiAgICAgd2lkdGg9IjEiCiAgICAgaGVpZ2h0PSIxIgogICAgIHg9IjAiCiAgICAgeT0iMCIKICAgICBjbGlwLXBhdGg9InVybCgjY2xpcCkiCiAgICAgc3R5bGU9ImZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZSIgLz4KPC9zdmc+Cg==");background:0 0;-webkit-transition-duration:.28s;transition-duration:.28s;-webkit-transition-timing-function:cubic-bezier(.4,0,.2,1);transition-timing-function:cubic-bezier(.4,0,.2,1);-webkit-transition-property:background;transition-property:background}.mdl-checkbox.is-checked .mdl-checkbox__tick-outline{background:#3f51b5 url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcKICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICB4bWxuczpjYz0iaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbnMjIgogICB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiCiAgIHhtbG5zOnN2Zz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgdmVyc2lvbj0iMS4xIgogICB2aWV3Qm94PSIwIDAgMSAxIgogICBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWluWU1pbiBtZWV0Ij4KICA8cGF0aAogICAgIGQ9Ik0gMC4wNDAzODA1OSwwLjYyNjc3NjcgMC4xNDY0NDY2MSwwLjUyMDcxMDY4IDAuNDI5Mjg5MzIsMC44MDM1NTMzOSAwLjMyMzIyMzMsMC45MDk2MTk0MSB6IE0gMC4yMTcxNTcyOSwwLjgwMzU1MzM5IDAuODUzNTUzMzksMC4xNjcxNTcyOSAwLjk1OTYxOTQxLDAuMjczMjIzMyAwLjMyMzIyMzMsMC45MDk2MTk0MSB6IgogICAgIGlkPSJyZWN0Mzc4MCIKICAgICBzdHlsZT0iZmlsbDojZmZmZmZmO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lIiAvPgo8L3N2Zz4K")}.mdl-checkbox.is-checked.is-disabled .mdl-checkbox__tick-outline{background:rgba(0,0,0,.26)url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcKICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICB4bWxuczpjYz0iaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbnMjIgogICB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiCiAgIHhtbG5zOnN2Zz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgdmVyc2lvbj0iMS4xIgogICB2aWV3Qm94PSIwIDAgMSAxIgogICBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWluWU1pbiBtZWV0Ij4KICA8cGF0aAogICAgIGQ9Ik0gMC4wNDAzODA1OSwwLjYyNjc3NjcgMC4xNDY0NDY2MSwwLjUyMDcxMDY4IDAuNDI5Mjg5MzIsMC44MDM1NTMzOSAwLjMyMzIyMzMsMC45MDk2MTk0MSB6IE0gMC4yMTcxNTcyOSwwLjgwMzU1MzM5IDAuODUzNTUzMzksMC4xNjcxNTcyOSAwLjk1OTYxOTQxLDAuMjczMjIzMyAwLjMyMzIyMzMsMC45MDk2MTk0MSB6IgogICAgIGlkPSJyZWN0Mzc4MCIKICAgICBzdHlsZT0iZmlsbDojZmZmZmZmO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lIiAvPgo8L3N2Zz4K")}.mdl-checkbox__label{position:relative;cursor:pointer;font-size:16px;line-height:24px;margin:0}.mdl-checkbox.is-disabled .mdl-checkbox__label{color:rgba(0,0,0,.26);cursor:auto}.mdl-checkbox__ripple-container{position:absolute;z-index:2;top:-6px;left:-10px;box-sizing:border-box;width:36px;height:36px;border-radius:50%;cursor:pointer;overflow:hidden;-webkit-mask-image:-webkit-radial-gradient(circle,#fff,#000)}.mdl-checkbox__ripple-container .mdl-ripple{background:#3f51b5}.mdl-checkbox.is-disabled .mdl-checkbox__ripple-container{cursor:auto}.mdl-checkbox.is-disabled .mdl-checkbox__ripple-container .mdl-ripple{background:0 0}.mdl-data-table{position:relative;border:1px solid rgba(0,0,0,.12);border-collapse:collapse;white-space:nowrap;font-size:13px;background-color:#fff}.mdl-data-table thead{padding-bottom:3px}.mdl-data-table thead .mdl-data-table__select{margin-top:0}.mdl-data-table tbody tr{position:relative;height:48px;-webkit-transition-duration:.28s;transition-duration:.28s;-webkit-transition-timing-function:cubic-bezier(.4,0,.2,1);transition-timing-function:cubic-bezier(.4,0,.2,1);-webkit-transition-property:background-color;transition-property:background-color}.mdl-data-table tbody tr.is-selected{background-color:#e0e0e0}.mdl-data-table tbody tr:hover{background-color:#eee}.mdl-data-table td{text-align:right}.mdl-data-table th{padding:0 18px;text-align:right}.mdl-data-table td:first-of-type,.mdl-data-table th:first-of-type{padding-left:24px}.mdl-data-table td:last-of-type,.mdl-data-table th:last-of-type{padding-right:24px}.mdl-data-table td{position:relative;vertical-align:top;height:48px;border-top:1px solid rgba(0,0,0,.12);border-bottom:1px solid rgba(0,0,0,.12);padding:12px 18px 0;box-sizing:border-box}.mdl-data-table td .mdl-data-table__select{vertical-align:top;position:absolute;left:24px}.mdl-data-table th{position:relative;vertical-align:bottom;text-overflow:ellipsis;font-weight:700;line-height:24px;letter-spacing:0;height:48px;font-size:12px;color:rgba(0,0,0,.54);padding-bottom:8px;box-sizing:border-box}.mdl-data-table th .mdl-data-table__select{position:absolute;bottom:8px;left:24px}.mdl-data-table__select{width:16px}.mdl-data-table__cell--non-numeric.mdl-data-table__cell--non-numeric{text-align:left}.mdl-mega-footer{padding:16px 40px;color:#9e9e9e;background-color:#424242}.mdl-mega-footer--top-section:after,.mdl-mega-footer--middle-section:after,.mdl-mega-footer--bottom-section:after,.mdl-mega-footer__top-section:after,.mdl-mega-footer__middle-section:after,.mdl-mega-footer__bottom-section:after{content:'';display:block;clear:both}.mdl-mega-footer--left-section,.mdl-mega-footer__left-section,.mdl-mega-footer--right-section,.mdl-mega-footer__right-section{margin-bottom:16px}.mdl-mega-footer--right-section a,.mdl-mega-footer__right-section a{display:block;margin-bottom:16px;color:inherit;text-decoration:none}@media screen and (min-width:760px){.mdl-mega-footer--left-section,.mdl-mega-footer__left-section{float:left}.mdl-mega-footer--right-section,.mdl-mega-footer__right-section{float:right}.mdl-mega-footer--right-section a,.mdl-mega-footer__right-section a{display:inline-block;margin-left:16px;line-height:36px;vertical-align:middle}}.mdl-mega-footer--social-btn,.mdl-mega-footer__social-btn{width:36px;height:36px;padding:0;margin:0;background-color:#9e9e9e;border:none}.mdl-mega-footer--drop-down-section,.mdl-mega-footer__drop-down-section{display:block;position:relative}@media screen and (min-width:760px){.mdl-mega-footer--drop-down-section,.mdl-mega-footer__drop-down-section{width:33%}.mdl-mega-footer--drop-down-section:nth-child(1),.mdl-mega-footer--drop-down-section:nth-child(2),.mdl-mega-footer__drop-down-section:nth-child(1),.mdl-mega-footer__drop-down-section:nth-child(2){float:left}.mdl-mega-footer--drop-down-section:nth-child(3),.mdl-mega-footer__drop-down-section:nth-child(3){float:right}.mdl-mega-footer--drop-down-section:nth-child(3):after,.mdl-mega-footer__drop-down-section:nth-child(3):after{clear:right}.mdl-mega-footer--drop-down-section:nth-child(4),.mdl-mega-footer__drop-down-section:nth-child(4){clear:right;float:right}.mdl-mega-footer--middle-section:after,.mdl-mega-footer__middle-section:after{content:'';display:block;clear:both}.mdl-mega-footer--bottom-section,.mdl-mega-footer__bottom-section{padding-top:0}}@media screen and (min-width:1024px){.mdl-mega-footer--drop-down-section,.mdl-mega-footer--drop-down-section:nth-child(3),.mdl-mega-footer--drop-down-section:nth-child(4),.mdl-mega-footer__drop-down-section,.mdl-mega-footer__drop-down-section:nth-child(3),.mdl-mega-footer__drop-down-section:nth-child(4){width:24%;float:left}}.mdl-mega-footer--heading-checkbox,.mdl-mega-footer__heading-checkbox{position:absolute;width:100%;height:55.8px;padding:32px;margin:-16px 0 0;cursor:pointer;z-index:1;opacity:0}.mdl-mega-footer--heading-checkbox+.mdl-mega-footer--heading:after,.mdl-mega-footer--heading-checkbox+.mdl-mega-footer__heading:after,.mdl-mega-footer__heading-checkbox+.mdl-mega-footer--heading:after,.mdl-mega-footer__heading-checkbox+.mdl-mega-footer__heading:after{font-family:'Material Icons';content:'\E5CE'}.mdl-mega-footer--heading-checkbox:checked~.mdl-mega-footer--link-list,.mdl-mega-footer--heading-checkbox:checked~.mdl-mega-footer__link-list,.mdl-mega-footer--heading-checkbox:checked+.mdl-mega-footer--heading+.mdl-mega-footer--link-list,.mdl-mega-footer--heading-checkbox:checked+.mdl-mega-footer__heading+.mdl-mega-footer__link-list,.mdl-mega-footer__heading-checkbox:checked~.mdl-mega-footer--link-list,.mdl-mega-footer__heading-checkbox:checked~.mdl-mega-footer__link-list,.mdl-mega-footer__heading-checkbox:checked+.mdl-mega-footer--heading+.mdl-mega-footer--link-list,.mdl-mega-footer__heading-checkbox:checked+.mdl-mega-footer__heading+.mdl-mega-footer__link-list{display:none}.mdl-mega-footer--heading-checkbox:checked+.mdl-mega-footer--heading:after,.mdl-mega-footer--heading-checkbox:checked+.mdl-mega-footer__heading:after,.mdl-mega-footer__heading-checkbox:checked+.mdl-mega-footer--heading:after,.mdl-mega-footer__heading-checkbox:checked+.mdl-mega-footer__heading:after{font-family:'Material Icons';content:'\E5CF'}.mdl-mega-footer--heading,.mdl-mega-footer__heading{position:relative;width:100%;padding-right:39.8px;margin-bottom:16px;box-sizing:border-box;font-size:14px;line-height:23.8px;font-weight:500;white-space:nowrap;text-overflow:ellipsis;overflow:hidden;color:#e0e0e0}.mdl-mega-footer--heading:after,.mdl-mega-footer__heading:after{content:'';position:absolute;top:0;right:0;display:block;width:23.8px;height:23.8px;background-size:cover}.mdl-mega-footer--link-list,.mdl-mega-footer__link-list{list-style:none;padding:0;margin:0 0 32px}.mdl-mega-footer--link-list:after,.mdl-mega-footer__link-list:after{clear:both;display:block;content:''}.mdl-mega-footer--link-list li,.mdl-mega-footer__link-list li{font-size:14px;font-weight:400;letter-spacing:0;line-height:20px}.mdl-mega-footer--link-list a,.mdl-mega-footer__link-list a{color:inherit;text-decoration:none;white-space:nowrap}@media screen and (min-width:760px){.mdl-mega-footer--heading-checkbox,.mdl-mega-footer__heading-checkbox{display:none}.mdl-mega-footer--heading-checkbox+.mdl-mega-footer--heading:after,.mdl-mega-footer--heading-checkbox+.mdl-mega-footer__heading:after,.mdl-mega-footer__heading-checkbox+.mdl-mega-footer--heading:after,.mdl-mega-footer__heading-checkbox+.mdl-mega-footer__heading:after{background-image:none}.mdl-mega-footer--heading-checkbox:checked~.mdl-mega-footer--link-list,.mdl-mega-footer--heading-checkbox:checked~.mdl-mega-footer__link-list,.mdl-mega-footer--heading-checkbox:checked+.mdl-mega-footer__heading+.mdl-mega-footer__link-list,.mdl-mega-footer--heading-checkbox:checked+.mdl-mega-footer--heading+.mdl-mega-footer--link-list,.mdl-mega-footer__heading-checkbox:checked~.mdl-mega-footer--link-list,.mdl-mega-footer__heading-checkbox:checked~.mdl-mega-footer__link-list,.mdl-mega-footer__heading-checkbox:checked+.mdl-mega-footer__heading+.mdl-mega-footer__link-list,.mdl-mega-footer__heading-checkbox:checked+.mdl-mega-footer--heading+.mdl-mega-footer--link-list{display:block}.mdl-mega-footer--heading-checkbox:checked+.mdl-mega-footer--heading:after,.mdl-mega-footer--heading-checkbox:checked+.mdl-mega-footer__heading:after,.mdl-mega-footer__heading-checkbox:checked+.mdl-mega-footer--heading:after,.mdl-mega-footer__heading-checkbox:checked+.mdl-mega-footer__heading:after{content:''}}.mdl-mega-footer--bottom-section,.mdl-mega-footer__bottom-section{padding-top:16px;margin-bottom:16px}.mdl-logo{margin-bottom:16px;color:#fff}.mdl-mega-footer--bottom-section .mdl-mega-footer--link-list li,.mdl-mega-footer__bottom-section .mdl-mega-footer__link-list li{float:left;margin-bottom:0;margin-right:16px}@media screen and (min-width:760px){.mdl-logo{float:left;margin-bottom:0;margin-right:16px}}.mdl-mini-footer{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-flow:row wrap;-ms-flex-flow:row wrap;flex-flow:row wrap;-webkit-box-pack:justify;-webkit-justify-content:space-between;-ms-flex-pack:justify;justify-content:space-between;padding:32px 16px;color:#9e9e9e;background-color:#424242}.mdl-mini-footer:after{content:'';display:block}.mdl-mini-footer .mdl-logo{line-height:36px}.mdl-mini-footer--link-list,.mdl-mini-footer__link-list{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-flow:row nowrap;-ms-flex-flow:row nowrap;flex-flow:row nowrap;list-style:none;margin:0;padding:0}.mdl-mini-footer--link-list li,.mdl-mini-footer__link-list li{margin-bottom:0;margin-right:16px}@media screen and (min-width:760px){.mdl-mini-footer--link-list li,.mdl-mini-footer__link-list li{line-height:36px}}.mdl-mini-footer--link-list a,.mdl-mini-footer__link-list a{color:inherit;text-decoration:none;white-space:nowrap}.mdl-mini-footer--left-section,.mdl-mini-footer__left-section{display:inline-block;-webkit-box-ordinal-group:1;-webkit-order:0;-ms-flex-order:0;order:0}.mdl-mini-footer--right-section,.mdl-mini-footer__right-section{display:inline-block;-webkit-box-ordinal-group:2;-webkit-order:1;-ms-flex-order:1;order:1}.mdl-mini-footer--social-btn,.mdl-mini-footer__social-btn{width:36px;height:36px;padding:0;margin:0;background-color:#9e9e9e;border:none}.mdl-icon-toggle{position:relative;z-index:1;vertical-align:middle;display:inline-block;height:32px;margin:0;padding:0}.mdl-icon-toggle__input{line-height:32px}.mdl-icon-toggle.is-upgraded .mdl-icon-toggle__input{position:absolute;width:0;height:0;margin:0;padding:0;opacity:0;-ms-appearance:none;-moz-appearance:none;-webkit-appearance:none;appearance:none;border:none}.mdl-icon-toggle__label{display:inline-block;position:relative;cursor:pointer;height:32px;width:32px;min-width:32px;color:#616161;border-radius:50%;padding:0;margin-left:0;margin-right:0;text-align:center;background-color:transparent;will-change:background-color;-webkit-transition:background-color .2s cubic-bezier(.4,0,.2,1),color .2s cubic-bezier(.4,0,.2,1);transition:background-color .2s cubic-bezier(.4,0,.2,1),color .2s cubic-bezier(.4,0,.2,1)}.mdl-icon-toggle__label.material-icons{line-height:32px;font-size:24px}.mdl-icon-toggle.is-checked .mdl-icon-toggle__label{color:#3f51b5}.mdl-icon-toggle.is-disabled .mdl-icon-toggle__label{color:rgba(0,0,0,.26);cursor:auto;-webkit-transition:none;transition:none}.mdl-icon-toggle.is-focused .mdl-icon-toggle__label{background-color:rgba(0,0,0,.12)}.mdl-icon-toggle.is-focused.is-checked .mdl-icon-toggle__label{background-color:rgba(63,81,181,.26)}.mdl-icon-toggle__ripple-container{position:absolute;z-index:2;top:-2px;left:-2px;box-sizing:border-box;width:36px;height:36px;border-radius:50%;cursor:pointer;overflow:hidden;-webkit-mask-image:-webkit-radial-gradient(circle,#fff,#000)}.mdl-icon-toggle__ripple-container .mdl-ripple{background:#616161}.mdl-icon-toggle.is-disabled .mdl-icon-toggle__ripple-container{cursor:auto}.mdl-icon-toggle.is-disabled .mdl-icon-toggle__ripple-container .mdl-ripple{background:0 0}.mdl-menu__container{display:block;margin:0;padding:0;border:none;position:absolute;overflow:visible;height:0;width:0;visibility:hidden;z-index:-1}.mdl-menu__container.is-visible,.mdl-menu__container.is-animating{z-index:999;visibility:visible}.mdl-menu__outline{display:block;background:#fff;margin:0;padding:0;border:none;border-radius:2px;position:absolute;top:0;left:0;overflow:hidden;opacity:0;-webkit-transform:scale(0);-ms-transform:scale(0);transform:scale(0);-webkit-transform-origin:0 0;-ms-transform-origin:0 0;transform-origin:0 0;box-shadow:0 2px 2px 0 rgba(0,0,0,.14),0 3px 1px -2px rgba(0,0,0,.2),0 1px 5px 0 rgba(0,0,0,.12);will-change:transform;-webkit-transition:-webkit-transform .3s cubic-bezier(.4,0,.2,1),opacity .2s cubic-bezier(.4,0,.2,1);transition:transform .3s cubic-bezier(.4,0,.2,1),opacity .2s cubic-bezier(.4,0,.2,1);z-index:-1}.mdl-menu__container.is-visible .mdl-menu__outline{opacity:1;-webkit-transform:scale(1);-ms-transform:scale(1);transform:scale(1);z-index:999}.mdl-menu__outline.mdl-menu--bottom-right{-webkit-transform-origin:100% 0;-ms-transform-origin:100% 0;transform-origin:100% 0}.mdl-menu__outline.mdl-menu--top-left{-webkit-transform-origin:0 100%;-ms-transform-origin:0 100%;transform-origin:0 100%}.mdl-menu__outline.mdl-menu--top-right{-webkit-transform-origin:100% 100%;-ms-transform-origin:100% 100%;transform-origin:100% 100%}.mdl-menu{position:absolute;list-style:none;top:0;left:0;height:auto;width:auto;min-width:124px;padding:8px 0;margin:0;opacity:0;clip:rect(0 0 0 0);z-index:-1}.mdl-menu__container.is-visible .mdl-menu{opacity:1;z-index:999}.mdl-menu.is-animating{-webkit-transition:opacity .2s cubic-bezier(.4,0,.2,1),clip .3s cubic-bezier(.4,0,.2,1);transition:opacity .2s cubic-bezier(.4,0,.2,1),clip .3s cubic-bezier(.4,0,.2,1)}.mdl-menu.mdl-menu--bottom-right{left:auto;right:0}.mdl-menu.mdl-menu--top-left{top:auto;bottom:0}.mdl-menu.mdl-menu--top-right{top:auto;left:auto;bottom:0;right:0}.mdl-menu.mdl-menu--unaligned{top:auto;left:auto}.mdl-menu__item{display:block;border:none;color:rgba(0,0,0,.87);background-color:transparent;text-align:left;margin:0;padding:0 16px;outline-color:#bdbdbd;position:relative;overflow:hidden;font-size:14px;font-weight:400;letter-spacing:0;text-decoration:none;cursor:pointer;height:48px;line-height:48px;white-space:nowrap;opacity:0;-webkit-transition:opacity .2s cubic-bezier(.4,0,.2,1);transition:opacity .2s cubic-bezier(.4,0,.2,1);-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.mdl-menu__container.is-visible .mdl-menu__item{opacity:1}.mdl-menu__item::-moz-focus-inner{border:0}.mdl-menu__item[disabled]{color:#bdbdbd;background-color:transparent;cursor:auto}.mdl-menu__item[disabled]:hover{background-color:transparent}.mdl-menu__item[disabled]:focus{background-color:transparent}.mdl-menu__item[disabled] .mdl-ripple{background:0 0}.mdl-menu__item:hover{background-color:#eee}.mdl-menu__item:focus{outline:none;background-color:#eee}.mdl-menu__item:active{background-color:#e0e0e0}.mdl-menu__item--ripple-container{display:block;height:100%;left:0;position:absolute;top:0;width:100%;z-index:0;overflow:hidden}.mdl-progress{display:block;position:relative;height:4px;width:500px}.mdl-progress>.bar{display:block;position:absolute;top:0;bottom:0;width:0%;-webkit-transition:width .2s cubic-bezier(.4,0,.2,1);transition:width .2s cubic-bezier(.4,0,.2,1)}.mdl-progress>.progressbar{background-color:#3f51b5;z-index:1;left:0}.mdl-progress>.bufferbar{background-image:-webkit-linear-gradient(left,rgba(255,255,255,.7),rgba(255,255,255,.7)),-webkit-linear-gradient(left,#3f51b5 ,#3f51b5);background-image:linear-gradient(to right,rgba(255,255,255,.7),rgba(255,255,255,.7)),linear-gradient(to right,#3f51b5 ,#3f51b5);z-index:0;left:0}.mdl-progress>.auxbar{right:0}@supports (-webkit-appearance:none){.mdl-progress:not(.mdl-progress__indeterminate):not(.mdl-progress__indeterminate)>.auxbar{background-image:-webkit-linear-gradient(left,rgba(255,255,255,.7),rgba(255,255,255,.7)),-webkit-linear-gradient(left,#3f51b5 ,#3f51b5);background-image:linear-gradient(to right,rgba(255,255,255,.7),rgba(255,255,255,.7)),linear-gradient(to right,#3f51b5 ,#3f51b5);-webkit-mask:url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIj8+Cjxzdmcgd2lkdGg9IjEyIiBoZWlnaHQ9IjQiIHZpZXdQb3J0PSIwIDAgMTIgNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgogIDxlbGxpcHNlIGN4PSIyIiBjeT0iMiIgcng9IjIiIHJ5PSIyIj4KICAgIDxhbmltYXRlIGF0dHJpYnV0ZU5hbWU9ImN4IiBmcm9tPSIyIiB0bz0iLTEwIiBkdXI9IjAuNnMiIHJlcGVhdENvdW50PSJpbmRlZmluaXRlIiAvPgogIDwvZWxsaXBzZT4KICA8ZWxsaXBzZSBjeD0iMTQiIGN5PSIyIiByeD0iMiIgcnk9IjIiIGNsYXNzPSJsb2FkZXIiPgogICAgPGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iY3giIGZyb209IjE0IiB0bz0iMiIgZHVyPSIwLjZzIiByZXBlYXRDb3VudD0iaW5kZWZpbml0ZSIgLz4KICA8L2VsbGlwc2U+Cjwvc3ZnPgo=");mask:url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIj8+Cjxzdmcgd2lkdGg9IjEyIiBoZWlnaHQ9IjQiIHZpZXdQb3J0PSIwIDAgMTIgNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgogIDxlbGxpcHNlIGN4PSIyIiBjeT0iMiIgcng9IjIiIHJ5PSIyIj4KICAgIDxhbmltYXRlIGF0dHJpYnV0ZU5hbWU9ImN4IiBmcm9tPSIyIiB0bz0iLTEwIiBkdXI9IjAuNnMiIHJlcGVhdENvdW50PSJpbmRlZmluaXRlIiAvPgogIDwvZWxsaXBzZT4KICA8ZWxsaXBzZSBjeD0iMTQiIGN5PSIyIiByeD0iMiIgcnk9IjIiIGNsYXNzPSJsb2FkZXIiPgogICAgPGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iY3giIGZyb209IjE0IiB0bz0iMiIgZHVyPSIwLjZzIiByZXBlYXRDb3VudD0iaW5kZWZpbml0ZSIgLz4KICA8L2VsbGlwc2U+Cjwvc3ZnPgo=")}}.mdl-progress:not(.mdl-progress__indeterminate)>.auxbar{background-image:-webkit-linear-gradient(left,rgba(255,255,255,.9),rgba(255,255,255,.9)),-webkit-linear-gradient(left,#3f51b5 ,#3f51b5);background-image:linear-gradient(to right,rgba(255,255,255,.9),rgba(255,255,255,.9)),linear-gradient(to right,#3f51b5 ,#3f51b5)}.mdl-progress.mdl-progress__indeterminate>.bar1{-webkit-animation-name:indeterminate1;animation-name:indeterminate1}.mdl-progress.mdl-progress__indeterminate>.bar1,.mdl-progress.mdl-progress__indeterminate>.bar3{background-color:#3f51b5;-webkit-animation-duration:2s;animation-duration:2s;-webkit-animation-iteration-count:infinite;animation-iteration-count:infinite;-webkit-animation-timing-function:linear;animation-timing-function:linear}.mdl-progress.mdl-progress__indeterminate>.bar3{background-image:none;-webkit-animation-name:indeterminate2;animation-name:indeterminate2}@-webkit-keyframes indeterminate1{0%{left:0%;width:0%}50%{left:25%;width:75%}75%{left:100%;width:0%}}@keyframes indeterminate1{0%{left:0%;width:0%}50%{left:25%;width:75%}75%{left:100%;width:0%}}@-webkit-keyframes indeterminate2{0%,50%{left:0%;width:0%}75%{left:0%;width:25%}100%{left:100%;width:0%}}@keyframes indeterminate2{0%,50%{left:0%;width:0%}75%{left:0%;width:25%}100%{left:100%;width:0%}}.mdl-navigation{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-wrap:nowrap;-ms-flex-wrap:nowrap;flex-wrap:nowrap;box-sizing:border-box}.mdl-navigation__link{color:#424242;text-decoration:none;font-weight:500;font-size:13px;margin:0}.mdl-layout{width:100%;height:100%;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;overflow-y:auto;overflow-x:hidden;position:relative;-webkit-overflow-scrolling:touch}.mdl-layout.is-small-screen .mdl-layout--large-screen-only{display:none}.mdl-layout:not(.is-small-screen) .mdl-layout--small-screen-only{display:none}.mdl-layout__container{position:absolute;width:100%;height:100%}.mdl-layout__title,.mdl-layout-title{display:block;position:relative;font-family:"Roboto","Helvetica","Arial",sans-serif;font-size:20px;line-height:1;letter-spacing:.02em;font-weight:400;box-sizing:border-box}.mdl-layout-spacer{-webkit-box-flex:1;-webkit-flex-grow:1;-ms-flex-positive:1;flex-grow:1}.mdl-layout__drawer{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;-webkit-flex-wrap:nowrap;-ms-flex-wrap:nowrap;flex-wrap:nowrap;width:240px;height:100%;max-height:100%;position:absolute;top:0;left:0;box-shadow:0 2px 2px 0 rgba(0,0,0,.14),0 3px 1px -2px rgba(0,0,0,.2),0 1px 5px 0 rgba(0,0,0,.12);box-sizing:border-box;border-right:1px solid #e0e0e0;background:#fafafa;-webkit-transform:translateX(-250px);-ms-transform:translateX(-250px);transform:translateX(-250px);-webkit-transform-style:preserve-3d;transform-style:preserve-3d;will-change:transform;-webkit-transition-duration:.2s;transition-duration:.2s;-webkit-transition-timing-function:cubic-bezier(.4,0,.2,1);transition-timing-function:cubic-bezier(.4,0,.2,1);-webkit-transition-property:-webkit-transform;transition-property:transform;color:#424242;overflow:visible;overflow-y:auto;z-index:5}.mdl-layout__drawer.is-visible{-webkit-transform:translateX(0);-ms-transform:translateX(0);transform:translateX(0)}.mdl-layout__drawer.is-visible~.mdl-layout__content.mdl-layout__content{overflow:hidden}.mdl-layout__drawer>*{-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0}.mdl-layout__drawer>.mdl-layout__title,.mdl-layout__drawer>.mdl-layout-title{line-height:64px;padding-left:40px}@media screen and (max-width:1024px){.mdl-layout__drawer>.mdl-layout__title,.mdl-layout__drawer>.mdl-layout-title{line-height:56px;padding-left:16px}}.mdl-layout__drawer .mdl-navigation{-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;-webkit-box-align:stretch;-webkit-align-items:stretch;-ms-flex-align:stretch;align-items:stretch;padding-top:16px}.mdl-layout__drawer .mdl-navigation .mdl-navigation__link{display:block;-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0;padding:16px 40px;margin:0;color:#757575}@media screen and (max-width:1024px){.mdl-layout__drawer .mdl-navigation .mdl-navigation__link{padding:16px}}.mdl-layout__drawer .mdl-navigation .mdl-navigation__link:hover{background-color:#e0e0e0}.mdl-layout__drawer .mdl-navigation .mdl-navigation__link--current{background-color:#000;color:#e0e0e0}@media screen and (min-width:1025px){.mdl-layout--fixed-drawer>.mdl-layout__drawer{-webkit-transform:translateX(0);-ms-transform:translateX(0);transform:translateX(0)}}.mdl-layout__drawer-button{display:block;position:absolute;height:48px;width:48px;border:0;-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0;overflow:hidden;text-align:center;cursor:pointer;font-size:26px;line-height:50px;font-family:Helvetica,Arial,sans-serif;margin:10px 12px;top:0;left:0;color:#fff;z-index:4}.mdl-layout__header .mdl-layout__drawer-button{position:absolute;color:#fff;background-color:inherit}@media screen and (max-width:1024px){.mdl-layout__header .mdl-layout__drawer-button{margin:4px}}@media screen and (max-width:1024px){.mdl-layout__drawer-button{margin:4px;color:rgba(0,0,0,.5)}}@media screen and (min-width:1025px){.mdl-layout--fixed-drawer>.mdl-layout__drawer-button{display:none}}.mdl-layout__header{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;-webkit-flex-wrap:nowrap;-ms-flex-wrap:nowrap;flex-wrap:nowrap;-webkit-box-pack:start;-webkit-justify-content:flex-start;-ms-flex-pack:start;justify-content:flex-start;box-sizing:border-box;-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0;width:100%;margin:0;padding:0;border:none;min-height:64px;max-height:1000px;z-index:3;background-color:#3f51b5;color:#fff;box-shadow:0 2px 2px 0 rgba(0,0,0,.14),0 3px 1px -2px rgba(0,0,0,.2),0 1px 5px 0 rgba(0,0,0,.12);-webkit-transition-duration:.2s;transition-duration:.2s;-webkit-transition-timing-function:cubic-bezier(.4,0,.2,1);transition-timing-function:cubic-bezier(.4,0,.2,1);-webkit-transition-property:max-height,box-shadow;transition-property:max-height,box-shadow}@media screen and (max-width:1024px){.mdl-layout__header{min-height:56px}}.mdl-layout--fixed-drawer.is-upgraded:not(.is-small-screen)>.mdl-layout__header{margin-left:240px;width:calc(100% - 240px)}@media screen and (min-width:1025px){.mdl-layout--fixed-drawer>.mdl-layout__header .mdl-layout__header-row{padding-left:40px}}.mdl-layout__header>.mdl-layout-icon{position:absolute;left:40px;top:16px;height:32px;width:32px;overflow:hidden;z-index:3;display:block}@media screen and (max-width:1024px){.mdl-layout__header>.mdl-layout-icon{left:16px;top:12px}}.mdl-layout.has-drawer .mdl-layout__header>.mdl-layout-icon{display:none}.mdl-layout__header.is-compact{max-height:64px}@media screen and (max-width:1024px){.mdl-layout__header.is-compact{max-height:56px}}.mdl-layout__header.is-compact.has-tabs{height:112px}@media screen and (max-width:1024px){.mdl-layout__header.is-compact.has-tabs{min-height:104px}}@media screen and (max-width:1024px){.mdl-layout__header{display:none}.mdl-layout--fixed-header>.mdl-layout__header{display:-webkit-flex;display:-ms-flexbox;display:flex}}.mdl-layout__header--transparent.mdl-layout__header--transparent{background-color:transparent;box-shadow:none}.mdl-layout__header--seamed,.mdl-layout__header--scroll{box-shadow:none}.mdl-layout__header--waterfall{box-shadow:none;overflow:hidden}.mdl-layout__header--waterfall.is-casting-shadow{box-shadow:0 2px 2px 0 rgba(0,0,0,.14),0 3px 1px -2px rgba(0,0,0,.2),0 1px 5px 0 rgba(0,0,0,.12)}.mdl-layout__header-row{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row;-webkit-flex-wrap:nowrap;-ms-flex-wrap:nowrap;flex-wrap:nowrap;-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0;box-sizing:border-box;-webkit-align-self:stretch;-ms-flex-item-align:stretch;align-self:stretch;-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;height:64px;margin:0;padding:0 40px 0 80px}@media screen and (max-width:1024px){.mdl-layout__header-row{height:56px;padding:0 16px 0 72px}}.mdl-layout__header-row>*{-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0}.mdl-layout__header--scroll .mdl-layout__header-row{width:100%}.mdl-layout__header-row .mdl-navigation{margin:0;padding:0;height:64px;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row;-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center}@media screen and (max-width:1024px){.mdl-layout__header-row .mdl-navigation{height:56px}}.mdl-layout__header-row .mdl-navigation__link{display:block;color:#fff;line-height:64px;padding:0 24px}@media screen and (max-width:1024px){.mdl-layout__header-row .mdl-navigation__link{line-height:56px;padding:0 16px}}.mdl-layout__obfuscator{background-color:transparent;position:absolute;top:0;left:0;height:100%;width:100%;z-index:4;visibility:hidden;-webkit-transition-property:background-color;transition-property:background-color;-webkit-transition-duration:.2s;transition-duration:.2s;-webkit-transition-timing-function:cubic-bezier(.4,0,.2,1);transition-timing-function:cubic-bezier(.4,0,.2,1)}.mdl-layout__obfuscator.is-visible{background-color:rgba(0,0,0,.5);visibility:visible}.mdl-layout__content{-ms-flex:0 1 auto;display:inline-block;overflow-y:auto;overflow-x:hidden;-webkit-box-flex:1;-webkit-flex-grow:1;-ms-flex-positive:1;flex-grow:1;z-index:1;-webkit-overflow-scrolling:touch}.mdl-layout--fixed-drawer>.mdl-layout__content{margin-left:240px}.mdl-layout__container.has-scrolling-header .mdl-layout__content{overflow:visible}@media screen and (max-width:1024px){.mdl-layout--fixed-drawer>.mdl-layout__content{margin-left:0}.mdl-layout__container.has-scrolling-header .mdl-layout__content{overflow-y:auto;overflow-x:hidden}}.mdl-layout__tab-bar{height:96px;margin:0;width:calc(100% - 112px);padding:0 0 0 56px;display:-webkit-flex;display:-ms-flexbox;display:flex;background-color:#3f51b5;overflow-y:hidden;overflow-x:scroll}.mdl-layout__tab-bar::-webkit-scrollbar{display:none}@media screen and (max-width:1024px){.mdl-layout__tab-bar{width:calc(100% - 60px);padding:0 0 0 60px}}.mdl-layout--fixed-tabs .mdl-layout__tab-bar{padding:0;overflow:hidden;width:100%}.mdl-layout__tab-bar-container{position:relative;height:48px;width:100%;border:none;margin:0;z-index:2;-webkit-box-flex:0;-webkit-flex-grow:0;-ms-flex-positive:0;flex-grow:0;-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0;overflow:hidden}.mdl-layout__container>.mdl-layout__tab-bar-container{position:absolute;top:0;left:0}.mdl-layout__tab-bar-button{display:inline-block;position:absolute;top:0;height:48px;width:56px;z-index:4;text-align:center;background-color:#3f51b5;color:transparent;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}@media screen and (max-width:1024px){.mdl-layout__tab-bar-button{display:none;width:60px}}.mdl-layout--fixed-tabs .mdl-layout__tab-bar-button{display:none}.mdl-layout__tab-bar-button .material-icons{line-height:48px}.mdl-layout__tab-bar-button.is-active{color:#fff}.mdl-layout__tab-bar-left-button{left:0}.mdl-layout__tab-bar-right-button{right:0}.mdl-layout__tab{margin:0;border:none;padding:0 24px;float:left;position:relative;display:block;-webkit-box-flex:0;-webkit-flex-grow:0;-ms-flex-positive:0;flex-grow:0;-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0;text-decoration:none;height:48px;line-height:48px;text-align:center;font-weight:500;font-size:14px;text-transform:uppercase;color:rgba(255,255,255,.6);overflow:hidden}@media screen and (max-width:1024px){.mdl-layout__tab{padding:0 12px}}.mdl-layout--fixed-tabs .mdl-layout__tab{float:none;-webkit-box-flex:1;-webkit-flex-grow:1;-ms-flex-positive:1;flex-grow:1;padding:0}.mdl-layout.is-upgraded .mdl-layout__tab.is-active{color:#fff}.mdl-layout.is-upgraded .mdl-layout__tab.is-active::after{height:2px;width:100%;display:block;content:" ";bottom:0;left:0;position:absolute;background:#ff4081;-webkit-animation:border-expand .2s cubic-bezier(.4,0,.4,1).01s alternate forwards;animation:border-expand .2s cubic-bezier(.4,0,.4,1).01s alternate forwards;-webkit-transition:all 1s cubic-bezier(.4,0,1,1);transition:all 1s cubic-bezier(.4,0,1,1)}.mdl-layout__tab .mdl-layout__tab-ripple-container{display:block;position:absolute;height:100%;width:100%;left:0;top:0;z-index:1;overflow:hidden}.mdl-layout__tab .mdl-layout__tab-ripple-container .mdl-ripple{background-color:#fff}.mdl-layout__tab-panel{display:block}.mdl-layout.is-upgraded .mdl-layout__tab-panel{display:none}.mdl-layout.is-upgraded .mdl-layout__tab-panel.is-active{display:block}.mdl-radio{position:relative;font-size:16px;line-height:24px;display:inline-block;box-sizing:border-box;margin:0;padding-left:0}.mdl-radio.is-upgraded{padding-left:24px}.mdl-radio__button{line-height:24px}.mdl-radio.is-upgraded .mdl-radio__button{position:absolute;width:0;height:0;margin:0;padding:0;opacity:0;-ms-appearance:none;-moz-appearance:none;-webkit-appearance:none;appearance:none;border:none}.mdl-radio__outer-circle{position:absolute;top:4px;left:0;display:inline-block;box-sizing:border-box;width:16px;height:16px;margin:0;cursor:pointer;border:2px solid rgba(0,0,0,.54);border-radius:50%;z-index:2}.mdl-radio.is-checked .mdl-radio__outer-circle{border:2px solid #3f51b5}.mdl-radio.is-disabled .mdl-radio__outer-circle{border:2px solid rgba(0,0,0,.26);cursor:auto}.mdl-radio__inner-circle{position:absolute;z-index:1;margin:0;top:8px;left:4px;box-sizing:border-box;width:8px;height:8px;cursor:pointer;-webkit-transition-duration:.28s;transition-duration:.28s;-webkit-transition-timing-function:cubic-bezier(.4,0,.2,1);transition-timing-function:cubic-bezier(.4,0,.2,1);-webkit-transition-property:-webkit-transform;transition-property:transform;-webkit-transform:scale3d(0,0,0);transform:scale3d(0,0,0);border-radius:50%;background:#3f51b5}.mdl-radio.is-checked .mdl-radio__inner-circle{-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}.mdl-radio.is-disabled .mdl-radio__inner-circle{background:rgba(0,0,0,.26);cursor:auto}.mdl-radio.is-focused .mdl-radio__inner-circle{box-shadow:0 0 0 10px rgba(0,0,0,.1)}.mdl-radio__label{cursor:pointer}.mdl-radio.is-disabled .mdl-radio__label{color:rgba(0,0,0,.26);cursor:auto}.mdl-radio__ripple-container{position:absolute;z-index:2;top:-9px;left:-13px;box-sizing:border-box;width:42px;height:42px;border-radius:50%;cursor:pointer;overflow:hidden;-webkit-mask-image:-webkit-radial-gradient(circle,#fff,#000)}.mdl-radio__ripple-container .mdl-ripple{background:#3f51b5}.mdl-radio.is-disabled .mdl-radio__ripple-container{cursor:auto}.mdl-radio.is-disabled .mdl-radio__ripple-container .mdl-ripple{background:0 0}_:-ms-input-placeholder,:root .mdl-slider.mdl-slider.is-upgraded{-ms-appearance:none;height:32px;margin:0}.mdl-slider{width:calc(100% - 40px);margin:0 20px}.mdl-slider.is-upgraded{-webkit-appearance:none;-moz-appearance:none;appearance:none;height:2px;background:0 0;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;outline:0;padding:0;color:#3f51b5;-webkit-align-self:center;-ms-flex-item-align:center;align-self:center;z-index:1;cursor:pointer}.mdl-slider.is-upgraded::-moz-focus-outer{border:0}.mdl-slider.is-upgraded::-ms-tooltip{display:none}.mdl-slider.is-upgraded::-webkit-slider-runnable-track{background:0 0}.mdl-slider.is-upgraded::-moz-range-track{background:0 0;border:none}.mdl-slider.is-upgraded::-ms-track{background:0 0;color:transparent;height:2px;width:100%;border:none}.mdl-slider.is-upgraded::-ms-fill-lower{padding:0;background:linear-gradient(to right,transparent,transparent 16px,#3f51b5 16px,#3f51b5 0)}.mdl-slider.is-upgraded::-ms-fill-upper{padding:0;background:linear-gradient(to left,transparent,transparent 16px,rgba(0,0,0,.26)16px,rgba(0,0,0,.26)0)}.mdl-slider.is-upgraded::-webkit-slider-thumb{-webkit-appearance:none;width:12px;height:12px;box-sizing:border-box;border-radius:50%;background:#3f51b5;border:none;-webkit-transition:-webkit-transform .18s cubic-bezier(.4,0,.2,1),border .18s cubic-bezier(.4,0,.2,1),box-shadow .18s cubic-bezier(.4,0,.2,1),background .28s cubic-bezier(.4,0,.2,1);transition:transform .18s cubic-bezier(.4,0,.2,1),border .18s cubic-bezier(.4,0,.2,1),box-shadow .18s cubic-bezier(.4,0,.2,1),background .28s cubic-bezier(.4,0,.2,1)}.mdl-slider.is-upgraded::-moz-range-thumb{-moz-appearance:none;width:12px;height:12px;box-sizing:border-box;border-radius:50%;background-image:none;background:#3f51b5;border:none}.mdl-slider.is-upgraded:focus:not(:active)::-webkit-slider-thumb{box-shadow:0 0 0 10px rgba(63,81,181,.26)}.mdl-slider.is-upgraded:focus:not(:active)::-moz-range-thumb{box-shadow:0 0 0 10px rgba(63,81,181,.26)}.mdl-slider.is-upgraded:active::-webkit-slider-thumb{background-image:none;background:#3f51b5;-webkit-transform:scale(1.5);transform:scale(1.5)}.mdl-slider.is-upgraded:active::-moz-range-thumb{background-image:none;background:#3f51b5;transform:scale(1.5)}.mdl-slider.is-upgraded::-ms-thumb{width:32px;height:32px;border:none;border-radius:50%;background:#3f51b5;-ms-transform:scale(.375);transform:scale(.375);transition:transform .18s cubic-bezier(.4,0,.2,1),background .28s cubic-bezier(.4,0,.2,1)}.mdl-slider.is-upgraded:focus:not(:active)::-ms-thumb{background:radial-gradient(circle closest-side,#3f51b5 0%,#3f51b5 37.5%,rgba(63,81,181,.26)37.5%,rgba(63,81,181,.26)100%);-ms-transform:scale(1);transform:scale(1)}.mdl-slider.is-upgraded:active::-ms-thumb{background:#3f51b5;-ms-transform:scale(.5625);transform:scale(.5625)}.mdl-slider.is-upgraded.is-lowest-value::-webkit-slider-thumb{border:2px solid rgba(0,0,0,.26);background:0 0}.mdl-slider.is-upgraded.is-lowest-value::-moz-range-thumb{border:2px solid rgba(0,0,0,.26);background:0 0}.mdl-slider.is-upgraded.is-lowest-value+.mdl-slider__background-flex>.mdl-slider__background-upper{left:6px}.mdl-slider.is-upgraded.is-lowest-value:focus:not(:active)::-webkit-slider-thumb{box-shadow:0 0 0 10px rgba(0,0,0,.12);background:rgba(0,0,0,.12)}.mdl-slider.is-upgraded.is-lowest-value:focus:not(:active)::-moz-range-thumb{box-shadow:0 0 0 10px rgba(0,0,0,.12);background:rgba(0,0,0,.12)}.mdl-slider.is-upgraded.is-lowest-value:active::-webkit-slider-thumb{border:1.6px solid rgba(0,0,0,.26);-webkit-transform:scale(1.5);transform:scale(1.5)}.mdl-slider.is-upgraded.is-lowest-value:active+.mdl-slider__background-flex>.mdl-slider__background-upper{left:9px}.mdl-slider.is-upgraded.is-lowest-value:active::-moz-range-thumb{border:1.5px solid rgba(0,0,0,.26);transform:scale(1.5)}.mdl-slider.is-upgraded.is-lowest-value::-ms-thumb{background:radial-gradient(circle closest-side,transparent 0%,transparent 66.67%,rgba(0,0,0,.26)66.67%,rgba(0,0,0,.26)100%)}.mdl-slider.is-upgraded.is-lowest-value:focus:not(:active)::-ms-thumb{background:radial-gradient(circle closest-side,rgba(0,0,0,.12)0%,rgba(0,0,0,.12)25%,rgba(0,0,0,.26)25%,rgba(0,0,0,.26)37.5%,rgba(0,0,0,.12)37.5%,rgba(0,0,0,.12)100%);-ms-transform:scale(1);transform:scale(1)}.mdl-slider.is-upgraded.is-lowest-value:active::-ms-thumb{-ms-transform:scale(.5625);transform:scale(.5625);background:radial-gradient(circle closest-side,transparent 0%,transparent 77.78%,rgba(0,0,0,.26)77.78%,rgba(0,0,0,.26)100%)}.mdl-slider.is-upgraded.is-lowest-value::-ms-fill-lower{background:0 0}.mdl-slider.is-upgraded.is-lowest-value::-ms-fill-upper{margin-left:6px}.mdl-slider.is-upgraded.is-lowest-value:active::-ms-fill-upper{margin-left:9px}.mdl-slider.is-upgraded:disabled:focus::-webkit-slider-thumb,.mdl-slider.is-upgraded:disabled:active::-webkit-slider-thumb,.mdl-slider.is-upgraded:disabled::-webkit-slider-thumb{-webkit-transform:scale(.667);transform:scale(.667);background:rgba(0,0,0,.26)}.mdl-slider.is-upgraded:disabled:focus::-moz-range-thumb,.mdl-slider.is-upgraded:disabled:active::-moz-range-thumb,.mdl-slider.is-upgraded:disabled::-moz-range-thumb{transform:scale(.667);background:rgba(0,0,0,.26)}.mdl-slider.is-upgraded:disabled+.mdl-slider__background-flex>.mdl-slider__background-lower{background-color:rgba(0,0,0,.26);left:-6px}.mdl-slider.is-upgraded:disabled+.mdl-slider__background-flex>.mdl-slider__background-upper{left:6px}.mdl-slider.is-upgraded.is-lowest-value:disabled:focus::-webkit-slider-thumb,.mdl-slider.is-upgraded.is-lowest-value:disabled:active::-webkit-slider-thumb,.mdl-slider.is-upgraded.is-lowest-value:disabled::-webkit-slider-thumb{border:3px solid rgba(0,0,0,.26);background:0 0;-webkit-transform:scale(.667);transform:scale(.667)}.mdl-slider.is-upgraded.is-lowest-value:disabled:focus::-moz-range-thumb,.mdl-slider.is-upgraded.is-lowest-value:disabled:active::-moz-range-thumb,.mdl-slider.is-upgraded.is-lowest-value:disabled::-moz-range-thumb{border:3px solid rgba(0,0,0,.26);background:0 0;transform:scale(.667)}.mdl-slider.is-upgraded.is-lowest-value:disabled:active+.mdl-slider__background-flex>.mdl-slider__background-upper{left:6px}.mdl-slider.is-upgraded:disabled:focus::-ms-thumb,.mdl-slider.is-upgraded:disabled:active::-ms-thumb,.mdl-slider.is-upgraded:disabled::-ms-thumb{-ms-transform:scale(.25);transform:scale(.25);background:rgba(0,0,0,.26)}.mdl-slider.is-upgraded.is-lowest-value:disabled:focus::-ms-thumb,.mdl-slider.is-upgraded.is-lowest-value:disabled:active::-ms-thumb,.mdl-slider.is-upgraded.is-lowest-value:disabled::-ms-thumb{-ms-transform:scale(.25);transform:scale(.25);background:radial-gradient(circle closest-side,transparent 0%,transparent 50%,rgba(0,0,0,.26)50%,rgba(0,0,0,.26)100%)}.mdl-slider.is-upgraded:disabled::-ms-fill-lower{margin-right:6px;background:linear-gradient(to right,transparent,transparent 25px,rgba(0,0,0,.26)25px,rgba(0,0,0,.26)0)}.mdl-slider.is-upgraded:disabled::-ms-fill-upper{margin-left:6px}.mdl-slider.is-upgraded.is-lowest-value:disabled:active::-ms-fill-upper{margin-left:6px}.mdl-slider__ie-container{height:18px;overflow:visible;border:none;margin:none;padding:none}.mdl-slider__container{height:18px;position:relative;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row}.mdl-slider__container,.mdl-slider__background-flex{background:0 0;display:-webkit-flex;display:-ms-flexbox;display:flex}.mdl-slider__background-flex{position:absolute;height:2px;width:calc(100% - 52px);top:50%;left:0;margin:0 26px;overflow:hidden;border:0;padding:0;-webkit-transform:translate(0,-1px);-ms-transform:translate(0,-1px);transform:translate(0,-1px)}.mdl-slider__background-lower{background:#3f51b5}.mdl-slider__background-lower,.mdl-slider__background-upper{-webkit-box-flex:0;-webkit-flex:0;-ms-flex:0;flex:0;position:relative;border:0;padding:0}.mdl-slider__background-upper{background:rgba(0,0,0,.26);-webkit-transition:left .18s cubic-bezier(.4,0,.2,1);transition:left .18s cubic-bezier(.4,0,.2,1)}.mdl-spinner{display:inline-block;position:relative;width:28px;height:28px}.mdl-spinner:not(.is-upgraded).is-active:after{content:"Loading..."}.mdl-spinner.is-upgraded.is-active{-webkit-animation:mdl-spinner__container-rotate 1568.23529412ms linear infinite;animation:mdl-spinner__container-rotate 1568.23529412ms linear infinite}@-webkit-keyframes mdl-spinner__container-rotate{to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes mdl-spinner__container-rotate{to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}.mdl-spinner__layer{position:absolute;width:100%;height:100%;opacity:0}.mdl-spinner__layer-1{border-color:#42a5f5}.mdl-spinner--single-color .mdl-spinner__layer-1{border-color:#3f51b5}.mdl-spinner.is-active .mdl-spinner__layer-1{-webkit-animation:mdl-spinner__fill-unfill-rotate 5332ms cubic-bezier(.4,0,.2,1)infinite both,mdl-spinner__layer-1-fade-in-out 5332ms cubic-bezier(.4,0,.2,1)infinite both;animation:mdl-spinner__fill-unfill-rotate 5332ms cubic-bezier(.4,0,.2,1)infinite both,mdl-spinner__layer-1-fade-in-out 5332ms cubic-bezier(.4,0,.2,1)infinite both}.mdl-spinner__layer-2{border-color:#f44336}.mdl-spinner--single-color .mdl-spinner__layer-2{border-color:#3f51b5}.mdl-spinner.is-active .mdl-spinner__layer-2{-webkit-animation:mdl-spinner__fill-unfill-rotate 5332ms cubic-bezier(.4,0,.2,1)infinite both,mdl-spinner__layer-2-fade-in-out 5332ms cubic-bezier(.4,0,.2,1)infinite both;animation:mdl-spinner__fill-unfill-rotate 5332ms cubic-bezier(.4,0,.2,1)infinite both,mdl-spinner__layer-2-fade-in-out 5332ms cubic-bezier(.4,0,.2,1)infinite both}.mdl-spinner__layer-3{border-color:#fdd835}.mdl-spinner--single-color .mdl-spinner__layer-3{border-color:#3f51b5}.mdl-spinner.is-active .mdl-spinner__layer-3{-webkit-animation:mdl-spinner__fill-unfill-rotate 5332ms cubic-bezier(.4,0,.2,1)infinite both,mdl-spinner__layer-3-fade-in-out 5332ms cubic-bezier(.4,0,.2,1)infinite both;animation:mdl-spinner__fill-unfill-rotate 5332ms cubic-bezier(.4,0,.2,1)infinite both,mdl-spinner__layer-3-fade-in-out 5332ms cubic-bezier(.4,0,.2,1)infinite both}.mdl-spinner__layer-4{border-color:#4caf50}.mdl-spinner--single-color .mdl-spinner__layer-4{border-color:#3f51b5}.mdl-spinner.is-active .mdl-spinner__layer-4{-webkit-animation:mdl-spinner__fill-unfill-rotate 5332ms cubic-bezier(.4,0,.2,1)infinite both,mdl-spinner__layer-4-fade-in-out 5332ms cubic-bezier(.4,0,.2,1)infinite both;animation:mdl-spinner__fill-unfill-rotate 5332ms cubic-bezier(.4,0,.2,1)infinite both,mdl-spinner__layer-4-fade-in-out 5332ms cubic-bezier(.4,0,.2,1)infinite both}@-webkit-keyframes mdl-spinner__fill-unfill-rotate{12.5%{-webkit-transform:rotate(135deg);transform:rotate(135deg)}25%{-webkit-transform:rotate(270deg);transform:rotate(270deg)}37.5%{-webkit-transform:rotate(405deg);transform:rotate(405deg)}50%{-webkit-transform:rotate(540deg);transform:rotate(540deg)}62.5%{-webkit-transform:rotate(675deg);transform:rotate(675deg)}75%{-webkit-transform:rotate(810deg);transform:rotate(810deg)}87.5%{-webkit-transform:rotate(945deg);transform:rotate(945deg)}to{-webkit-transform:rotate(1080deg);transform:rotate(1080deg)}}@keyframes mdl-spinner__fill-unfill-rotate{12.5%{-webkit-transform:rotate(135deg);transform:rotate(135deg)}25%{-webkit-transform:rotate(270deg);transform:rotate(270deg)}37.5%{-webkit-transform:rotate(405deg);transform:rotate(405deg)}50%{-webkit-transform:rotate(540deg);transform:rotate(540deg)}62.5%{-webkit-transform:rotate(675deg);transform:rotate(675deg)}75%{-webkit-transform:rotate(810deg);transform:rotate(810deg)}87.5%{-webkit-transform:rotate(945deg);transform:rotate(945deg)}to{-webkit-transform:rotate(1080deg);transform:rotate(1080deg)}}@-webkit-keyframes mdl-spinner__layer-1-fade-in-out{from,25%{opacity:.99}26%,89%{opacity:0}90%,100%{opacity:.99}}@keyframes mdl-spinner__layer-1-fade-in-out{from,25%{opacity:.99}26%,89%{opacity:0}90%,100%{opacity:.99}}@-webkit-keyframes mdl-spinner__layer-2-fade-in-out{from,15%{opacity:0}25%,50%{opacity:.99}51%{opacity:0}}@keyframes mdl-spinner__layer-2-fade-in-out{from,15%{opacity:0}25%,50%{opacity:.99}51%{opacity:0}}@-webkit-keyframes mdl-spinner__layer-3-fade-in-out{from,40%{opacity:0}50%,75%{opacity:.99}76%{opacity:0}}@keyframes mdl-spinner__layer-3-fade-in-out{from,40%{opacity:0}50%,75%{opacity:.99}76%{opacity:0}}@-webkit-keyframes mdl-spinner__layer-4-fade-in-out{from,65%{opacity:0}75%,90%{opacity:.99}100%{opacity:0}}@keyframes mdl-spinner__layer-4-fade-in-out{from,65%{opacity:0}75%,90%{opacity:.99}100%{opacity:0}}.mdl-spinner__gap-patch{position:absolute;box-sizing:border-box;top:0;left:45%;width:10%;height:100%;overflow:hidden;border-color:inherit}.mdl-spinner__gap-patch .mdl-spinner__circle{width:1000%;left:-450%}.mdl-spinner__circle-clipper{display:inline-block;position:relative;width:50%;height:100%;overflow:hidden;border-color:inherit}.mdl-spinner__circle-clipper .mdl-spinner__circle{width:200%}.mdl-spinner__circle{box-sizing:border-box;height:100%;border-width:3px;border-style:solid;border-color:inherit;border-bottom-color:transparent!important;border-radius:50%;-webkit-animation:none;animation:none;position:absolute;top:0;right:0;bottom:0;left:0}.mdl-spinner__left .mdl-spinner__circle{border-right-color:transparent!important;-webkit-transform:rotate(129deg);-ms-transform:rotate(129deg);transform:rotate(129deg)}.mdl-spinner.is-active .mdl-spinner__left .mdl-spinner__circle{-webkit-animation:mdl-spinner__left-spin 1333ms cubic-bezier(.4,0,.2,1)infinite both;animation:mdl-spinner__left-spin 1333ms cubic-bezier(.4,0,.2,1)infinite both}.mdl-spinner__right .mdl-spinner__circle{left:-100%;border-left-color:transparent!important;-webkit-transform:rotate(-129deg);-ms-transform:rotate(-129deg);transform:rotate(-129deg)}.mdl-spinner.is-active .mdl-spinner__right .mdl-spinner__circle{-webkit-animation:mdl-spinner__right-spin 1333ms cubic-bezier(.4,0,.2,1)infinite both;animation:mdl-spinner__right-spin 1333ms cubic-bezier(.4,0,.2,1)infinite both}@-webkit-keyframes mdl-spinner__left-spin{from{-webkit-transform:rotate(130deg);transform:rotate(130deg)}50%{-webkit-transform:rotate(-5deg);transform:rotate(-5deg)}to{-webkit-transform:rotate(130deg);transform:rotate(130deg)}}@keyframes mdl-spinner__left-spin{from{-webkit-transform:rotate(130deg);transform:rotate(130deg)}50%{-webkit-transform:rotate(-5deg);transform:rotate(-5deg)}to{-webkit-transform:rotate(130deg);transform:rotate(130deg)}}@-webkit-keyframes mdl-spinner__right-spin{from{-webkit-transform:rotate(-130deg);transform:rotate(-130deg)}50%{-webkit-transform:rotate(5deg);transform:rotate(5deg)}to{-webkit-transform:rotate(-130deg);transform:rotate(-130deg)}}@keyframes mdl-spinner__right-spin{from{-webkit-transform:rotate(-130deg);transform:rotate(-130deg)}50%{-webkit-transform:rotate(5deg);transform:rotate(5deg)}to{-webkit-transform:rotate(-130deg);transform:rotate(-130deg)}}.mdl-switch{position:relative;z-index:1;vertical-align:middle;display:inline-block;box-sizing:border-box;width:100%;height:24px;margin:0;padding:0;overflow:visible;-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.mdl-switch.is-upgraded{padding-left:28px}.mdl-switch__input{line-height:24px}.mdl-switch.is-upgraded .mdl-switch__input{position:absolute;width:0;height:0;margin:0;padding:0;opacity:0;-ms-appearance:none;-moz-appearance:none;-webkit-appearance:none;appearance:none;border:none}.mdl-switch__track{background:rgba(0,0,0,.26);position:absolute;left:0;top:5px;height:14px;width:36px;border-radius:14px;cursor:pointer}.mdl-switch.is-checked .mdl-switch__track{background:rgba(63,81,181,.5)}.mdl-switch.is-disabled .mdl-switch__track{background:rgba(0,0,0,.12);cursor:auto}.mdl-switch__thumb{background:#fafafa;position:absolute;left:0;top:2px;height:20px;width:20px;border-radius:50%;cursor:pointer;box-shadow:0 2px 2px 0 rgba(0,0,0,.14),0 3px 1px -2px rgba(0,0,0,.2),0 1px 5px 0 rgba(0,0,0,.12);-webkit-transition-duration:.28s;transition-duration:.28s;-webkit-transition-timing-function:cubic-bezier(.4,0,.2,1);transition-timing-function:cubic-bezier(.4,0,.2,1);-webkit-transition-property:left;transition-property:left}.mdl-switch.is-checked .mdl-switch__thumb{background:#3f51b5;left:16px;box-shadow:0 3px 4px 0 rgba(0,0,0,.14),0 3px 3px -2px rgba(0,0,0,.2),0 1px 8px 0 rgba(0,0,0,.12)}.mdl-switch.is-disabled .mdl-switch__thumb{background:#bdbdbd;cursor:auto}.mdl-switch__focus-helper{position:absolute;top:50%;left:50%;-webkit-transform:translate(-4px,-4px);-ms-transform:translate(-4px,-4px);transform:translate(-4px,-4px);display:inline-block;box-sizing:border-box;width:8px;height:8px;border-radius:50%;background-color:transparent}.mdl-switch.is-focused .mdl-switch__focus-helper{box-shadow:0 0 0 20px rgba(0,0,0,.1);background-color:rgba(0,0,0,.1)}.mdl-switch.is-focused.is-checked .mdl-switch__focus-helper{box-shadow:0 0 0 20px rgba(63,81,181,.26);background-color:rgba(63,81,181,.26)}.mdl-switch__label{position:relative;cursor:pointer;font-size:16px;line-height:24px;margin:0;left:24px}.mdl-switch.is-disabled .mdl-switch__label{color:#bdbdbd;cursor:auto}.mdl-switch__ripple-container{position:absolute;z-index:2;top:-12px;left:-14px;box-sizing:border-box;width:48px;height:48px;border-radius:50%;cursor:pointer;overflow:hidden;-webkit-mask-image:-webkit-radial-gradient(circle,#fff,#000);-webkit-transition-duration:.4s;transition-duration:.4s;-webkit-transition-timing-function:step-end;transition-timing-function:step-end;-webkit-transition-property:left;transition-property:left}.mdl-switch__ripple-container .mdl-ripple{background:#3f51b5}.mdl-switch.is-disabled .mdl-switch__ripple-container{cursor:auto}.mdl-switch.is-disabled .mdl-switch__ripple-container .mdl-ripple{background:0 0}.mdl-switch.is-checked .mdl-switch__ripple-container{cursor:auto;left:2px}.mdl-tabs{display:block;width:100%}.mdl-tabs__tab-bar{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;-webkit-align-content:space-between;-ms-flex-line-pack:justify;align-content:space-between;-webkit-box-align:start;-webkit-align-items:flex-start;-ms-flex-align:start;align-items:flex-start;height:48px;padding:0;margin:0;border-bottom:1px solid #e0e0e0}.mdl-tabs__tab{margin:0;border:none;padding:0 24px;float:left;position:relative;display:block;color:red;text-decoration:none;height:48px;line-height:48px;text-align:center;font-weight:500;font-size:14px;text-transform:uppercase;color:rgba(0,0,0,.54);overflow:hidden}.mdl-tabs.is-upgraded .mdl-tabs__tab.is-active{color:rgba(0,0,0,.87)}.mdl-tabs.is-upgraded .mdl-tabs__tab.is-active:after{height:2px;width:100%;display:block;content:" ";bottom:0;left:0;position:absolute;background:#3f51b5;-webkit-animation:border-expand .2s cubic-bezier(.4,0,.4,1).01s alternate forwards;animation:border-expand .2s cubic-bezier(.4,0,.4,1).01s alternate forwards;-webkit-transition:all 1s cubic-bezier(.4,0,1,1);transition:all 1s cubic-bezier(.4,0,1,1)}.mdl-tabs__tab .mdl-tabs__ripple-container{display:block;position:absolute;height:100%;width:100%;left:0;top:0;z-index:1;overflow:hidden}.mdl-tabs__tab .mdl-tabs__ripple-container .mdl-ripple{background:#3f51b5}.mdl-tabs__panel{display:block}.mdl-tabs.is-upgraded .mdl-tabs__panel{display:none}.mdl-tabs.is-upgraded .mdl-tabs__panel.is-active{display:block}@-webkit-keyframes border-expand{0%{opacity:0;width:0}100%{opacity:1;width:100%}}@keyframes border-expand{0%{opacity:0;width:0}100%{opacity:1;width:100%}}.mdl-textfield{position:relative;font-size:16px;display:inline-block;box-sizing:border-box;width:300px;max-width:100%;margin:0;padding:20px 0}.mdl-textfield .mdl-button{position:absolute;bottom:20px}.mdl-textfield--align-right{text-align:right}.mdl-textfield--full-width{width:100%}.mdl-textfield--expandable{min-width:32px;width:auto;min-height:32px}.mdl-textfield__input{border:none;border-bottom:1px solid rgba(0,0,0,.12);display:block;font-size:16px;margin:0;padding:4px 0;width:100%;background:0 0;text-align:left;color:inherit}.mdl-textfield.is-focused .mdl-textfield__input{outline:none}.mdl-textfield.is-invalid .mdl-textfield__input{border-color:#de3226;box-shadow:none}.mdl-textfield.is-disabled .mdl-textfield__input{background-color:transparent;border-bottom:1px dotted rgba(0,0,0,.12);color:rgba(0,0,0,.26)}.mdl-textfield textarea.mdl-textfield__input{display:block}.mdl-textfield__label{bottom:0;color:rgba(0,0,0,.26);font-size:16px;left:0;right:0;pointer-events:none;position:absolute;display:block;top:24px;width:100%;overflow:hidden;white-space:nowrap;text-align:left}.mdl-textfield.is-dirty .mdl-textfield__label{visibility:hidden}.mdl-textfield--floating-label .mdl-textfield__label{-webkit-transition-duration:.2s;transition-duration:.2s;-webkit-transition-timing-function:cubic-bezier(.4,0,.2,1);transition-timing-function:cubic-bezier(.4,0,.2,1)}.mdl-textfield.is-disabled.is-disabled .mdl-textfield__label{color:rgba(0,0,0,.26)}.mdl-textfield--floating-label.is-focused .mdl-textfield__label,.mdl-textfield--floating-label.is-dirty .mdl-textfield__label{color:#3f51b5;font-size:12px;top:4px;visibility:visible}.mdl-textfield--floating-label.is-focused .mdl-textfield__expandable-holder .mdl-textfield__label,.mdl-textfield--floating-label.is-dirty .mdl-textfield__expandable-holder .mdl-textfield__label{top:-16px}.mdl-textfield--floating-label.is-invalid .mdl-textfield__label{color:#de3226;font-size:12px}.mdl-textfield__label:after{background-color:#3f51b5;bottom:20px;content:'';height:2px;left:45%;position:absolute;-webkit-transition-duration:.2s;transition-duration:.2s;-webkit-transition-timing-function:cubic-bezier(.4,0,.2,1);transition-timing-function:cubic-bezier(.4,0,.2,1);visibility:hidden;width:10px}.mdl-textfield.is-focused .mdl-textfield__label:after{left:0;visibility:visible;width:100%}.mdl-textfield.is-invalid .mdl-textfield__label:after{background-color:#de3226}.mdl-textfield__error{color:#de3226;position:absolute;font-size:12px;margin-top:3px;visibility:hidden;display:block}.mdl-textfield.is-invalid .mdl-textfield__error{visibility:visible}.mdl-textfield__expandable-holder{position:relative;margin-left:32px;-webkit-transition-duration:.2s;transition-duration:.2s;-webkit-transition-timing-function:cubic-bezier(.4,0,.2,1);transition-timing-function:cubic-bezier(.4,0,.2,1);display:inline-block;max-width:.1px}.mdl-textfield.is-focused .mdl-textfield__expandable-holder,.mdl-textfield.is-dirty .mdl-textfield__expandable-holder{max-width:600px}.mdl-textfield__expandable-holder .mdl-textfield__label:after{bottom:0}.mdl-tooltip{-webkit-transform:scale(0);-ms-transform:scale(0);transform:scale(0);-webkit-transform-origin:top center;-ms-transform-origin:top center;transform-origin:top center;will-change:transform;z-index:999;background:rgba(97,97,97,.9);border-radius:2px;color:#fff;display:inline-block;font-size:10px;font-weight:500;line-height:14px;max-width:170px;position:fixed;top:-500px;left:-500px;padding:8px;text-align:center}.mdl-tooltip.is-active{-webkit-animation:pulse 200ms cubic-bezier(0,0,.2,1)forwards;animation:pulse 200ms cubic-bezier(0,0,.2,1)forwards}.mdl-tooltip--large{line-height:14px;font-size:14px;padding:16px}@-webkit-keyframes pulse{0%{-webkit-transform:scale(0);transform:scale(0);opacity:0}50%{-webkit-transform:scale(.99);transform:scale(.99)}100%{-webkit-transform:scale(1);transform:scale(1);opacity:1;visibility:visible}}@keyframes pulse{0%{-webkit-transform:scale(0);transform:scale(0);opacity:0}50%{-webkit-transform:scale(.99);transform:scale(.99)}100%{-webkit-transform:scale(1);transform:scale(1);opacity:1;visibility:visible}}.mdl-shadow--2dp{box-shadow:0 2px 2px 0 rgba(0,0,0,.14),0 3px 1px -2px rgba(0,0,0,.2),0 1px 5px 0 rgba(0,0,0,.12)}.mdl-shadow--3dp{box-shadow:0 3px 4px 0 rgba(0,0,0,.14),0 3px 3px -2px rgba(0,0,0,.2),0 1px 8px 0 rgba(0,0,0,.12)}.mdl-shadow--4dp{box-shadow:0 4px 5px 0 rgba(0,0,0,.14),0 1px 10px 0 rgba(0,0,0,.12),0 2px 4px -1px rgba(0,0,0,.2)}.mdl-shadow--6dp{box-shadow:0 6px 10px 0 rgba(0,0,0,.14),0 1px 18px 0 rgba(0,0,0,.12),0 3px 5px -1px rgba(0,0,0,.2)}.mdl-shadow--8dp{box-shadow:0 8px 10px 1px rgba(0,0,0,.14),0 3px 14px 2px rgba(0,0,0,.12),0 5px 5px -3px rgba(0,0,0,.2)}.mdl-shadow--16dp{box-shadow:0 16px 24px 2px rgba(0,0,0,.14),0 6px 30px 5px rgba(0,0,0,.12),0 8px 10px -5px rgba(0,0,0,.2)}.mdl-grid{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-flow:row wrap;-ms-flex-flow:row wrap;flex-flow:row wrap;margin:0 auto;-webkit-box-align:stretch;-webkit-align-items:stretch;-ms-flex-align:stretch;align-items:stretch}.mdl-grid.mdl-grid--no-spacing{padding:0}.mdl-cell{box-sizing:border-box}.mdl-cell--top{-webkit-align-self:flex-start;-ms-flex-item-align:start;align-self:flex-start}.mdl-cell--middle{-webkit-align-self:center;-ms-flex-item-align:center;align-self:center}.mdl-cell--bottom{-webkit-align-self:flex-end;-ms-flex-item-align:end;align-self:flex-end}.mdl-cell--stretch{-webkit-align-self:stretch;-ms-flex-item-align:stretch;align-self:stretch}.mdl-grid.mdl-grid--no-spacing>.mdl-cell{margin:0}@media (max-width:479px){.mdl-grid{padding:8px}.mdl-cell{margin:8px;width:calc(100% - 16px)}.mdl-grid--no-spacing>.mdl-cell{width:100%}.mdl-cell--hide-phone{display:none!important}.mdl-cell--1-col,.mdl-cell--1-col-phone.mdl-cell--1-col-phone{width:calc(25% - 16px)}.mdl-grid--no-spacing>.mdl-cell--1-col,.mdl-grid--no-spacing>.mdl-cell--1-col-phone.mdl-cell--1-col-phone{width:25%}.mdl-cell--2-col,.mdl-cell--2-col-phone.mdl-cell--2-col-phone{width:calc(50% - 16px)}.mdl-grid--no-spacing>.mdl-cell--2-col,.mdl-grid--no-spacing>.mdl-cell--2-col-phone.mdl-cell--2-col-phone{width:50%}.mdl-cell--3-col,.mdl-cell--3-col-phone.mdl-cell--3-col-phone{width:calc(75% - 16px)}.mdl-grid--no-spacing>.mdl-cell--3-col,.mdl-grid--no-spacing>.mdl-cell--3-col-phone.mdl-cell--3-col-phone{width:75%}.mdl-cell--4-col,.mdl-cell--4-col-phone.mdl-cell--4-col-phone{width:calc(100% - 16px)}.mdl-grid--no-spacing>.mdl-cell--4-col,.mdl-grid--no-spacing>.mdl-cell--4-col-phone.mdl-cell--4-col-phone{width:100%}.mdl-cell--5-col,.mdl-cell--5-col-phone.mdl-cell--5-col-phone{width:calc(100% - 16px)}.mdl-grid--no-spacing>.mdl-cell--5-col,.mdl-grid--no-spacing>.mdl-cell--5-col-phone.mdl-cell--5-col-phone{width:100%}.mdl-cell--6-col,.mdl-cell--6-col-phone.mdl-cell--6-col-phone{width:calc(100% - 16px)}.mdl-grid--no-spacing>.mdl-cell--6-col,.mdl-grid--no-spacing>.mdl-cell--6-col-phone.mdl-cell--6-col-phone{width:100%}.mdl-cell--7-col,.mdl-cell--7-col-phone.mdl-cell--7-col-phone{width:calc(100% - 16px)}.mdl-grid--no-spacing>.mdl-cell--7-col,.mdl-grid--no-spacing>.mdl-cell--7-col-phone.mdl-cell--7-col-phone{width:100%}.mdl-cell--8-col,.mdl-cell--8-col-phone.mdl-cell--8-col-phone{width:calc(100% - 16px)}.mdl-grid--no-spacing>.mdl-cell--8-col,.mdl-grid--no-spacing>.mdl-cell--8-col-phone.mdl-cell--8-col-phone{width:100%}.mdl-cell--9-col,.mdl-cell--9-col-phone.mdl-cell--9-col-phone{width:calc(100% - 16px)}.mdl-grid--no-spacing>.mdl-cell--9-col,.mdl-grid--no-spacing>.mdl-cell--9-col-phone.mdl-cell--9-col-phone{width:100%}.mdl-cell--10-col,.mdl-cell--10-col-phone.mdl-cell--10-col-phone{width:calc(100% - 16px)}.mdl-grid--no-spacing>.mdl-cell--10-col,.mdl-grid--no-spacing>.mdl-cell--10-col-phone.mdl-cell--10-col-phone{width:100%}.mdl-cell--11-col,.mdl-cell--11-col-phone.mdl-cell--11-col-phone{width:calc(100% - 16px)}.mdl-grid--no-spacing>.mdl-cell--11-col,.mdl-grid--no-spacing>.mdl-cell--11-col-phone.mdl-cell--11-col-phone{width:100%}.mdl-cell--12-col,.mdl-cell--12-col-phone.mdl-cell--12-col-phone{width:calc(100% - 16px)}.mdl-grid--no-spacing>.mdl-cell--12-col,.mdl-grid--no-spacing>.mdl-cell--12-col-phone.mdl-cell--12-col-phone{width:100%}}@media (min-width:480px) and (max-width:839px){.mdl-grid{padding:8px}.mdl-cell{margin:8px;width:calc(50% - 16px)}.mdl-grid--no-spacing>.mdl-cell{width:50%}.mdl-cell--hide-tablet{display:none!important}.mdl-cell--1-col,.mdl-cell--1-col-tablet.mdl-cell--1-col-tablet{width:calc(12.5% - 16px)}.mdl-grid--no-spacing>.mdl-cell--1-col,.mdl-grid--no-spacing>.mdl-cell--1-col-tablet.mdl-cell--1-col-tablet{width:12.5%}.mdl-cell--2-col,.mdl-cell--2-col-tablet.mdl-cell--2-col-tablet{width:calc(25% - 16px)}.mdl-grid--no-spacing>.mdl-cell--2-col,.mdl-grid--no-spacing>.mdl-cell--2-col-tablet.mdl-cell--2-col-tablet{width:25%}.mdl-cell--3-col,.mdl-cell--3-col-tablet.mdl-cell--3-col-tablet{width:calc(37.5% - 16px)}.mdl-grid--no-spacing>.mdl-cell--3-col,.mdl-grid--no-spacing>.mdl-cell--3-col-tablet.mdl-cell--3-col-tablet{width:37.5%}.mdl-cell--4-col,.mdl-cell--4-col-tablet.mdl-cell--4-col-tablet{width:calc(50% - 16px)}.mdl-grid--no-spacing>.mdl-cell--4-col,.mdl-grid--no-spacing>.mdl-cell--4-col-tablet.mdl-cell--4-col-tablet{width:50%}.mdl-cell--5-col,.mdl-cell--5-col-tablet.mdl-cell--5-col-tablet{width:calc(62.5% - 16px)}.mdl-grid--no-spacing>.mdl-cell--5-col,.mdl-grid--no-spacing>.mdl-cell--5-col-tablet.mdl-cell--5-col-tablet{width:62.5%}.mdl-cell--6-col,.mdl-cell--6-col-tablet.mdl-cell--6-col-tablet{width:calc(75% - 16px)}.mdl-grid--no-spacing>.mdl-cell--6-col,.mdl-grid--no-spacing>.mdl-cell--6-col-tablet.mdl-cell--6-col-tablet{width:75%}.mdl-cell--7-col,.mdl-cell--7-col-tablet.mdl-cell--7-col-tablet{width:calc(87.5% - 16px)}.mdl-grid--no-spacing>.mdl-cell--7-col,.mdl-grid--no-spacing>.mdl-cell--7-col-tablet.mdl-cell--7-col-tablet{width:87.5%}.mdl-cell--8-col,.mdl-cell--8-col-tablet.mdl-cell--8-col-tablet{width:calc(100% - 16px)}.mdl-grid--no-spacing>.mdl-cell--8-col,.mdl-grid--no-spacing>.mdl-cell--8-col-tablet.mdl-cell--8-col-tablet{width:100%}.mdl-cell--9-col,.mdl-cell--9-col-tablet.mdl-cell--9-col-tablet{width:calc(100% - 16px)}.mdl-grid--no-spacing>.mdl-cell--9-col,.mdl-grid--no-spacing>.mdl-cell--9-col-tablet.mdl-cell--9-col-tablet{width:100%}.mdl-cell--10-col,.mdl-cell--10-col-tablet.mdl-cell--10-col-tablet{width:calc(100% - 16px)}.mdl-grid--no-spacing>.mdl-cell--10-col,.mdl-grid--no-spacing>.mdl-cell--10-col-tablet.mdl-cell--10-col-tablet{width:100%}.mdl-cell--11-col,.mdl-cell--11-col-tablet.mdl-cell--11-col-tablet{width:calc(100% - 16px)}.mdl-grid--no-spacing>.mdl-cell--11-col,.mdl-grid--no-spacing>.mdl-cell--11-col-tablet.mdl-cell--11-col-tablet{width:100%}.mdl-cell--12-col,.mdl-cell--12-col-tablet.mdl-cell--12-col-tablet{width:calc(100% - 16px)}.mdl-grid--no-spacing>.mdl-cell--12-col,.mdl-grid--no-spacing>.mdl-cell--12-col-tablet.mdl-cell--12-col-tablet{width:100%}}@media (min-width:840px){.mdl-grid{padding:8px}.mdl-cell{margin:8px;width:calc(33.3333333333% - 16px)}.mdl-grid--no-spacing>.mdl-cell{width:33.3333333333%}.mdl-cell--hide-desktop{display:none!important}.mdl-cell--1-col,.mdl-cell--1-col-desktop.mdl-cell--1-col-desktop{width:calc(8.3333333333% - 16px)}.mdl-grid--no-spacing>.mdl-cell--1-col,.mdl-grid--no-spacing>.mdl-cell--1-col-desktop.mdl-cell--1-col-desktop{width:8.3333333333%}.mdl-cell--2-col,.mdl-cell--2-col-desktop.mdl-cell--2-col-desktop{width:calc(16.6666666667% - 16px)}.mdl-grid--no-spacing>.mdl-cell--2-col,.mdl-grid--no-spacing>.mdl-cell--2-col-desktop.mdl-cell--2-col-desktop{width:16.6666666667%}.mdl-cell--3-col,.mdl-cell--3-col-desktop.mdl-cell--3-col-desktop{width:calc(25% - 16px)}.mdl-grid--no-spacing>.mdl-cell--3-col,.mdl-grid--no-spacing>.mdl-cell--3-col-desktop.mdl-cell--3-col-desktop{width:25%}.mdl-cell--4-col,.mdl-cell--4-col-desktop.mdl-cell--4-col-desktop{width:calc(33.3333333333% - 16px)}.mdl-grid--no-spacing>.mdl-cell--4-col,.mdl-grid--no-spacing>.mdl-cell--4-col-desktop.mdl-cell--4-col-desktop{width:33.3333333333%}.mdl-cell--5-col,.mdl-cell--5-col-desktop.mdl-cell--5-col-desktop{width:calc(41.6666666667% - 16px)}.mdl-grid--no-spacing>.mdl-cell--5-col,.mdl-grid--no-spacing>.mdl-cell--5-col-desktop.mdl-cell--5-col-desktop{width:41.6666666667%}.mdl-cell--6-col,.mdl-cell--6-col-desktop.mdl-cell--6-col-desktop{width:calc(50% - 16px)}.mdl-grid--no-spacing>.mdl-cell--6-col,.mdl-grid--no-spacing>.mdl-cell--6-col-desktop.mdl-cell--6-col-desktop{width:50%}.mdl-cell--7-col,.mdl-cell--7-col-desktop.mdl-cell--7-col-desktop{width:calc(58.3333333333% - 16px)}.mdl-grid--no-spacing>.mdl-cell--7-col,.mdl-grid--no-spacing>.mdl-cell--7-col-desktop.mdl-cell--7-col-desktop{width:58.3333333333%}.mdl-cell--8-col,.mdl-cell--8-col-desktop.mdl-cell--8-col-desktop{width:calc(66.6666666667% - 16px)}.mdl-grid--no-spacing>.mdl-cell--8-col,.mdl-grid--no-spacing>.mdl-cell--8-col-desktop.mdl-cell--8-col-desktop{width:66.6666666667%}.mdl-cell--9-col,.mdl-cell--9-col-desktop.mdl-cell--9-col-desktop{width:calc(75% - 16px)}.mdl-grid--no-spacing>.mdl-cell--9-col,.mdl-grid--no-spacing>.mdl-cell--9-col-desktop.mdl-cell--9-col-desktop{width:75%}.mdl-cell--10-col,.mdl-cell--10-col-desktop.mdl-cell--10-col-desktop{width:calc(83.3333333333% - 16px)}.mdl-grid--no-spacing>.mdl-cell--10-col,.mdl-grid--no-spacing>.mdl-cell--10-col-desktop.mdl-cell--10-col-desktop{width:83.3333333333%}.mdl-cell--11-col,.mdl-cell--11-col-desktop.mdl-cell--11-col-desktop{width:calc(91.6666666667% - 16px)}.mdl-grid--no-spacing>.mdl-cell--11-col,.mdl-grid--no-spacing>.mdl-cell--11-col-desktop.mdl-cell--11-col-desktop{width:91.6666666667%}.mdl-cell--12-col,.mdl-cell--12-col-desktop.mdl-cell--12-col-desktop{width:calc(100% - 16px)}.mdl-grid--no-spacing>.mdl-cell--12-col,.mdl-grid--no-spacing>.mdl-cell--12-col-desktop.mdl-cell--12-col-desktop{width:100%}} -/*# sourceMappingURL=material.min.css.map */ diff --git a/examples/scalajs-play-core-react/server/src/main/assets/material/material.min.css.map b/examples/scalajs-play-core-react/server/src/main/assets/material/material.min.css.map deleted file mode 100644 index 0e20fbc..0000000 --- a/examples/scalajs-play-core-react/server/src/main/assets/material/material.min.css.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["material.min.css","material.css","material-design-lite.css","material-design-lite.scss","_variables.scss","_color-definitions.scss","_functions.scss","_mixins.scss","resets/_resets.scss","resets/_h5bp.scss","resets/_mobile.scss","typography/_typography.scss","palette/_palette.scss","ripple/_ripple.scss","animation/_animation.scss","badge/_badge.scss","button/_button.scss","card/_card.scss","checkbox/_checkbox.scss","data-table/_data-table.scss","footer/_mega_footer.scss","footer/_mini_footer.scss","icon-toggle/_icon-toggle.scss","menu/_menu.scss","progress/_progress.scss","layout/_layout.scss","radio/_radio.scss","slider/_slider.scss","spinner/_spinner.scss","switch/_switch.scss","tabs/_tabs.scss","textfield/_textfield.scss","tooltip/_tooltip.scss","shadow/_shadow.scss","grid/_grid.scss"],"names":[],"mappings":"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;AAED;EACE,4CAAiE,EAClE;;AAED;EACE,+BAAsD,EACvD;;AAED;EACE,0CAAiE,EAClE;;AAID;EACE,iCAAsD,EACvD;;AAED;EACE,4CAAiE,EAClE;;AAED;EACE,mCAAqD,EACtD;;AAED;EACE,8CAAgE,EACjE;;AAED;EACE,mCAAsD,EACvD;;AAED;EACE,8CAAiE,EAClE;;AAED;EACE,mCAAsD,EACvD;;AAED;EACE,8CAAiE,EAClE;;AAED;EACE,kCAAsD,EACvD;;AAED;EACE,6CAAiE,EAClE;;AAED;EACE,kCAAsD,EACvD;;AAED;EACE,6CAAiE,EAClE;;AAED;EACE,iCAAsD,EACvD;;AAED;EACE,4CAAiE,EAClE;;AAED;EACE,iCAAsD,EACvD;;AAED;EACE,4CAAiE,EAClE;;AAED;EACE,iCAAsD,EACvD;;AAED;EACE,4CAAiE,EAClE;;AAED;EACE,iCAAsD,EACvD;;AAED;EACE,4CAAiE,EAClE;;AAED;EACE,iCAAsD,EACvD;;AAED;EACE,4CAAiE,EAClE;;AAED;EACE,mCAAuD,EACxD;;AAED;EACE,8CAAkE,EACnE;;AAED;EACE,kCAAuD,EACxD;;AAED;EACE,6CAAkE,EACnE;;AAED;EACE,gCAAuD,EACxD;;AAED;EACE,2CAAkE,EACnE;;AAED;EACE,iCAAuD,EACxD;;AAED;EACE,4CAAkE,EACnE;;AAID;EACE,kCAAwD,EACzD;;AAED;EACE,6CAAmE,EACpE;;AAED;EACE,mCAAuD,EACxD;;AAED;EACE,8CAAkE,EACnE;;AAED;EACE,mCAAwD,EACzD;;AAED;EACE,8CAAmE,EACpE;;AAED;EACE,mCAAwD,EACzD;;AAED;EACE,8CAAmE,EACpE;;AAED;EACE,mCAAwD,EACzD;;AAED;EACE,8CAAmE,EACpE;;AAED;EACE,kCAAwD,EACzD;;AAED;EACE,6CAAmE,EACpE;;AAED;EACE,kCAAwD,EACzD;;AAED;EACE,6CAAmE,EACpE;;AAED;EACE,kCAAwD,EACzD;;AAED;EACE,6CAAmE,EACpE;;AAED;EACE,kCAAwD,EACzD;;AAED;EACE,6CAAmE,EACpE;;AAED;EACE,kCAAwD,EACzD;;AAED;EACE,6CAAmE,EACpE;;AAED;EACE,iCAAwD,EACzD;;AAED;EACE,4CAAmE,EACpE;;AAED;EACE,mCAAyD,EAC1D;;AAED;EACE,8CAAoE,EACrE;;AAED;EACE,kCAAyD,EAC1D;;AAED;EACE,6CAAoE,EACrE;;AAED;EACE,iCAAyD,EAC1D;;AAED;EACE,4CAAoE,EACrE;;AAED;EACE,iCAAyD,EAC1D;;AAED;EACE,4CAAoE,EACrE;;AAID;EACE,kCAA6D,EAC9D;;AAED;EACE,6CAAwE,EACzE;;AAED;EACE,mCAA4D,EAC7D;;AAED;EACE,8CAAuE,EACxE;;AAED;EACE,mCAA6D,EAC9D;;AAED;EACE,8CAAwE,EACzE;;AAED;EACE,mCAA6D,EAC9D;;AAED;EACE,8CAAwE,EACzE;;AAED;EACE,mCAA6D,EAC9D;;AAED;EACE,8CAAwE,EACzE;;AAED;EACE,kCAA6D,EAC9D;;AAED;EACE,6CAAwE,EACzE;;AAED;EACE,kCAA6D,EAC9D;;AAED;EACE,6CAAwE,EACzE;;AAED;EACE,iCAA6D,EAC9D;;AAED;EACE,4CAAwE,EACzE;;AAED;EACE,iCAA6D,EAC9D;;AAED;EACE,4CAAwE,EACzE;;AAED;EACE,iCAA6D,EAC9D;;AAED;EACE,4CAAwE,EACzE;;AAED;EACE,iCAA6D,EAC9D;;AAED;EACE,4CAAwE,EACzE;;AAED;EACE,mCAA8D,EAC/D;;AAED;EACE,8CAAyE,EAC1E;;AAED;EACE,kCAA8D,EAC/D;;AAED;EACE,6CAAyE,EAC1E;;AAED;EACE,kCAA8D,EAC/D;;AAED;EACE,6CAAyE,EAC1E;;AAED;EACE,gCAA8D,EAC/D;;AAED;EACE,2CAAyE,EAC1E;;AAID;EACE,iCAAwD,EACzD;;AAED;EACE,4CAAmE,EACpE;;AAED;EACE,mCAAuD,EACxD;;AAED;EACE,8CAAkE,EACnE;;AAED;EACE,mCAAwD,EACzD;;AAED;EACE,8CAAmE,EACpE;;AAED;EACE,mCAAwD,EACzD;;AAED;EACE,8CAAmE,EACpE;;AAED;EACE,mCAAwD,EACzD;;AAED;EACE,8CAAmE,EACpE;;AAED;EACE,kCAAwD,EACzD;;AAED;EACE,6CAAmE,EACpE;;AAED;EACE,iCAAwD,EACzD;;AAED;EACE,4CAAmE,EACpE;;AAED;EACE,iCAAwD,EACzD;;AAED;EACE,4CAAmE,EACpE;;AAED;EACE,iCAAwD,EACzD;;AAED;EACE,4CAAmE,EACpE;;AAED;EACE,iCAAwD,EACzD;;AAED;EACE,4CAAmE,EACpE;;AAED;EACE,iCAAwD,EACzD;;AAED;EACE,4CAAmE,EACpE;;AAED;EACE,mCAAyD,EAC1D;;AAED;EACE,8CAAoE,EACrE;;AAED;EACE,kCAAyD,EAC1D;;AAED;EACE,6CAAoE,EACrE;;AAED;EACE,iCAAyD,EAC1D;;AAED;EACE,4CAAoE,EACrE;;AAED;EACE,iCAAyD,EAC1D;;AAED;EACE,4CAAoE,EACrE;;AAID;EACE,kCAAsD,EACvD;;AAED;EACE,6CAAiE,EAClE;;AAED;EACE,mCAAqD,EACtD;;AAED;EACE,8CAAgE,EACjE;;AAED;EACE,mCAAsD,EACvD;;AAED;EACE,8CAAiE,EAClE;;AAED;EACE,mCAAsD,EACvD;;AAED;EACE,8CAAiE,EAClE;;AAED;EACE,mCAAsD,EACvD;;AAED;EACE,8CAAiE,EAClE;;AAED;EACE,kCAAsD,EACvD;;AAED;EACE,6CAAiE,EAClE;;AAED;EACE,kCAAsD,EACvD;;AAED;EACE,6CAAiE,EAClE;;AAED;EACE,kCAAsD,EACvD;;AAED;EACE,6CAAiE,EAClE;;AAED;EACE,kCAAsD,EACvD;;AAED;EACE,6CAAiE,EAClE;;AAED;EACE,kCAAsD,EACvD;;AAED;EACE,6CAAiE,EAClE;;AAED;EACE,iCAAsD,EACvD;;AAED;EACE,4CAAiE,EAClE;;AAED;EACE,mCAAuD,EACxD;;AAED;EACE,8CAAkE,EACnE;;AAED;EACE,kCAAuD,EACxD;;AAED;EACE,6CAAkE,EACnE;;AAED;EACE,kCAAuD,EACxD;;AAED;EACE,6CAAkE,EACnE;;AAED;EACE,iCAAuD,EACxD;;AAED;EACE,4CAAkE,EACnE;;AAID;EACE,iCAA4D,EAC7D;;AAED;EACE,4CAAuE,EACxE;;AAED;EACE,mCAA2D,EAC5D;;AAED;EACE,8CAAsE,EACvE;;AAED;EACE,mCAA4D,EAC7D;;AAED;EACE,8CAAuE,EACxE;;AAED;EACE,mCAA4D,EAC7D;;AAED;EACE,8CAAuE,EACxE;;AAED;EACE,kCAA4D,EAC7D;;AAED;EACE,6CAAuE,EACxE;;AAED;EACE,kCAA4D,EAC7D;;AAED;EACE,6CAAuE,EACxE;;AAED;EACE,iCAA4D,EAC7D;;AAED;EACE,4CAAuE,EACxE;;AAED;EACE,iCAA4D,EAC7D;;AAED;EACE,4CAAuE,EACxE;;AAED;EACE,iCAA4D,EAC7D;;AAED;EACE,4CAAuE,EACxE;;AAED;EACE,iCAA4D,EAC7D;;AAED;EACE,4CAAuE,EACxE;;AAED;EACE,gCAA4D,EAC7D;;AAED;EACE,2CAAuE,EACxE;;AAED;EACE,mCAA6D,EAC9D;;AAED;EACE,8CAAwE,EACzE;;AAED;EACE,kCAA6D,EAC9D;;AAED;EACE,6CAAwE,EACzE;;AAED;EACE,iCAA6D,EAC9D;;AAED;EACE,4CAAwE,EACzE;;AAED;EACE,iCAA6D,EAC9D;;AAED;EACE,4CAAwE,EACzE;;AAID;EACE,iCAAsD,EACvD;;AAED;EACE,4CAAiE,EAClE;;AAED;EACE,mCAAqD,EACtD;;AAED;EACE,8CAAgE,EACjE;;AAED;EACE,mCAAsD,EACvD;;AAED;EACE,8CAAiE,EAClE;;AAED;EACE,mCAAsD,EACvD;;AAED;EACE,8CAAiE,EAClE;;AAED;EACE,kCAAsD,EACvD;;AAED;EACE,6CAAiE,EAClE;;AAED;EACE,kCAAsD,EACvD;;AAED;EACE,6CAAiE,EAClE;;AAED;EACE,iCAAsD,EACvD;;AAED;EACE,4CAAiE,EAClE;;AAED;EACE,iCAAsD,EACvD;;AAED;EACE,4CAAiE,EAClE;;AAED;EACE,iCAAsD,EACvD;;AAED;EACE,4CAAiE,EAClE;;AAED;EACE,iCAAsD,EACvD;;AAED;EACE,4CAAiE,EAClE;;AAED;EACE,gCAAsD,EACvD;;AAED;EACE,2CAAiE,EAClE;;AAED;EACE,mCAAuD,EACxD;;AAED;EACE,8CAAkE,EACnE;;AAED;EACE,kCAAuD,EACxD;;AAED;EACE,6CAAkE,EACnE;;AAED;EACE,iCAAuD,EACxD;;AAED;EACE,4CAAkE,EACnE;;AAED;EACE,iCAAuD,EACxD;;AAED;EACE,4CAAkE,EACnE;;AAID;EACE,iCAAsD,EACvD;;AAED;EACE,4CAAiE,EAClE;;AAED;EACE,mCAAqD,EACtD;;AAED;EACE,8CAAgE,EACjE;;AAED;EACE,mCAAsD,EACvD;;AAED;EACE,8CAAiE,EAClE;;AAED;EACE,mCAAsD,EACvD;;AAED;EACE,8CAAiE,EAClE;;AAED;EACE,kCAAsD,EACvD;;AAED;EACE,6CAAiE,EAClE;;AAED;EACE,kCAAsD,EACvD;;AAED;EACE,6CAAiE,EAClE;;AAED;EACE,iCAAsD,EACvD;;AAED;EACE,4CAAiE,EAClE;;AAED;EACE,iCAAsD,EACvD;;AAED;EACE,4CAAiE,EAClE;;AAED;EACE,iCAAsD,EACvD;;AAED;EACE,4CAAiE,EAClE;;AAED;EACE,gCAAsD,EACvD;;AAED;EACE,2CAAiE,EAClE;;AAED;EACE,+BAAsD,EACvD;;AAED;EACE,0CAAiE,EAClE;;AAED;EACE,mCAAuD,EACxD;;AAED;EACE,8CAAkE,EACnE;;AAED;EACE,mCAAuD,EACxD;;AAED;EACE,8CAAkE,EACnE;;AAED;EACE,kCAAuD,EACxD;;AAED;EACE,6CAAkE,EACnE;;AAED;EACE,iCAAuD,EACxD;;AAED;EACE,4CAAkE,EACnE;;AAID;EACE,iCAAuD,EACxD;;AAED;EACE,4CAAkE,EACnE;;AAED;EACE,mCAAsD,EACvD;;AAED;EACE,8CAAiE,EAClE;;AAED;EACE,mCAAuD,EACxD;;AAED;EACE,8CAAkE,EACnE;;AAED;EACE,mCAAuD,EACxD;;AAED;EACE,8CAAkE,EACnE;;AAED;EACE,mCAAuD,EACxD;;AAED;EACE,8CAAkE,EACnE;;AAED;EACE,mCAAuD,EACxD;;AAED;EACE,8CAAkE,EACnE;;AAED;EACE,iCAAuD,EACxD;;AAED;EACE,4CAAkE,EACnE;;AAED;EACE,iCAAuD,EACxD;;AAED;EACE,4CAAkE,EACnE;;AAED;EACE,iCAAuD,EACxD;;AAED;EACE,4CAAkE,EACnE;;AAED;EACE,iCAAuD,EACxD;;AAED;EACE,4CAAkE,EACnE;;AAED;EACE,gCAAuD,EACxD;;AAED;EACE,2CAAkE,EACnE;;AAED;EACE,mCAAwD,EACzD;;AAED;EACE,8CAAmE,EACpE;;AAED;EACE,mCAAwD,EACzD;;AAED;EACE,8CAAmE,EACpE;;AAED;EACE,iCAAwD,EACzD;;AAED;EACE,4CAAmE,EACpE;;AAED;EACE,gCAAwD,EACzD;;AAED;EACE,2CAAmE,EACpE;;AAID;EACE,kCAA6D,EAC9D;;AAED;EACE,6CAAwE,EACzE;;AAED;EACE,mCAA4D,EAC7D;;AAED;EACE,8CAAuE,EACxE;;AAED;EACE,mCAA6D,EAC9D;;AAED;EACE,8CAAwE,EACzE;;AAED;EACE,mCAA6D,EAC9D;;AAED;EACE,8CAAwE,EACzE;;AAED;EACE,mCAA6D,EAC9D;;AAED;EACE,8CAAwE,EACzE;;AAED;EACE,mCAA6D,EAC9D;;AAED;EACE,8CAAwE,EACzE;;AAED;EACE,kCAA6D,EAC9D;;AAED;EACE,6CAAwE,EACzE;;AAED;EACE,kCAA6D,EAC9D;;AAED;EACE,6CAAwE,EACzE;;AAED;EACE,kCAA6D,EAC9D;;AAED;EACE,6CAAwE,EACzE;;AAED;EACE,iCAA6D,EAC9D;;AAED;EACE,4CAAwE,EACzE;;AAED;EACE,iCAA6D,EAC9D;;AAED;EACE,4CAAwE,EACzE;;AAED;EACE,mCAA8D,EAC/D;;AAED;EACE,8CAAyE,EAC1E;;AAED;EACE,kCAA8D,EAC/D;;AAED;EACE,6CAAyE,EAC1E;;AAED;EACE,iCAA8D,EAC/D;;AAED;EACE,4CAAyE,EAC1E;;AAED;EACE,kCAA8D,EAC/D;;AAED;EACE,6CAAyE,EAC1E;;AAID;EACE,kCAAsD,EACvD;;AAED;EACE,6CAAiE,EAClE;;AAED;EACE,mCAAqD,EACtD;;AAED;EACE,8CAAgE,EACjE;;AAED;EACE,mCAAsD,EACvD;;AAED;EACE,8CAAiE,EAClE;;AAED;EACE,mCAAsD,EACvD;;AAED;EACE,8CAAiE,EAClE;;AAED;EACE,mCAAsD,EACvD;;AAED;EACE,8CAAiE,EAClE;;AAED;EACE,kCAAsD,EACvD;;AAED;EACE,6CAAiE,EAClE;;AAED;EACE,kCAAsD,EACvD;;AAED;EACE,6CAAiE,EAClE;;AAED;EACE,kCAAsD,EACvD;;AAED;EACE,6CAAiE,EAClE;;AAED;EACE,kCAAsD,EACvD;;AAED;EACE,6CAAiE,EAClE;;AAED;EACE,kCAAsD,EACvD;;AAED;EACE,6CAAiE,EAClE;;AAED;EACE,kCAAsD,EACvD;;AAED;EACE,6CAAiE,EAClE;;AAED;EACE,mCAAuD,EACxD;;AAED;EACE,8CAAkE,EACnE;;AAED;EACE,kCAAuD,EACxD;;AAED;EACE,6CAAkE,EACnE;;AAED;EACE,iCAAuD,EACxD;;AAED;EACE,4CAAkE,EACnE;;AAED;EACE,iCAAuD,EACxD;;AAED;EACE,4CAAkE,EACnE;;AAID;EACE,kCAAwD,EACzD;;AAED;EACE,6CAAmE,EACpE;;AAED;EACE,mCAAuD,EACxD;;AAED;EACE,8CAAkE,EACnE;;AAED;EACE,mCAAwD,EACzD;;AAED;EACE,8CAAmE,EACpE;;AAED;EACE,mCAAwD,EACzD;;AAED;EACE,8CAAmE,EACpE;;AAED;EACE,mCAAwD,EACzD;;AAED;EACE,8CAAmE,EACpE;;AAED;EACE,kCAAwD,EACzD;;AAED;EACE,6CAAmE,EACpE;;AAED;EACE,kCAAwD,EACzD;;AAED;EACE,6CAAmE,EACpE;;AAED;EACE,kCAAwD,EACzD;;AAED;EACE,6CAAmE,EACpE;;AAED;EACE,kCAAwD,EACzD;;AAED;EACE,6CAAmE,EACpE;;AAED;EACE,kCAAwD,EACzD;;AAED;EACE,6CAAmE,EACpE;;AAED;EACE,kCAAwD,EACzD;;AAED;EACE,6CAAmE,EACpE;;AAED;EACE,mCAAyD,EAC1D;;AAED;EACE,8CAAoE,EACrE;;AAED;EACE,iCAAyD,EAC1D;;AAED;EACE,4CAAoE,EACrE;;AAED;EACE,iCAAyD,EAC1D;;AAED;EACE,4CAAoE,EACrE;;AAED;EACE,iCAAyD,EAC1D;;AAED;EACE,4CAAoE,EACrE;;AAID;EACE,iCAAuD,EACxD;;AAED;EACE,4CAAkE,EACnE;;AAED;EACE,mCAAsD,EACvD;;AAED;EACE,8CAAiE,EAClE;;AAED;EACE,mCAAuD,EACxD;;AAED;EACE,8CAAkE,EACnE;;AAED;EACE,mCAAuD,EACxD;;AAED;EACE,8CAAkE,EACnE;;AAED;EACE,kCAAuD,EACxD;;AAED;EACE,6CAAkE,EACnE;;AAED;EACE,kCAAuD,EACxD;;AAED;EACE,6CAAkE,EACnE;;AAED;EACE,iCAAuD,EACxD;;AAED;EACE,4CAAkE,EACnE;;AAED;EACE,iCAAuD,EACxD;;AAED;EACE,4CAAkE,EACnE;;AAED;EACE,iCAAuD,EACxD;;AAED;EACE,4CAAkE,EACnE;;AAED;EACE,iCAAuD,EACxD;;AAED;EACE,4CAAkE,EACnE;;AAED;EACE,iCAAuD,EACxD;;AAED;EACE,4CAAkE,EACnE;;AAED;EACE,mCAAwD,EACzD;;AAED;EACE,8CAAmE,EACpE;;AAED;EACE,kCAAwD,EACzD;;AAED;EACE,6CAAmE,EACpE;;AAED;EACE,iCAAwD,EACzD;;AAED;EACE,4CAAmE,EACpE;;AAED;EACE,iCAAwD,EACzD;;AAED;EACE,4CAAmE,EACpE;;AAID;EACE,iCAAwD,EACzD;;AAED;EACE,4CAAmE,EACpE;;AAED;EACE,mCAAuD,EACxD;;AAED;EACE,8CAAkE,EACnE;;AAED;EACE,mCAAwD,EACzD;;AAED;EACE,8CAAmE,EACpE;;AAED;EACE,mCAAwD,EACzD;;AAED;EACE,8CAAmE,EACpE;;AAED;EACE,kCAAwD,EACzD;;AAED;EACE,6CAAmE,EACpE;;AAED;EACE,kCAAwD,EACzD;;AAED;EACE,6CAAmE,EACpE;;AAED;EACE,iCAAwD,EACzD;;AAED;EACE,4CAAmE,EACpE;;AAED;EACE,iCAAwD,EACzD;;AAED;EACE,4CAAmE,EACpE;;AAED;EACE,iCAAwD,EACzD;;AAED;EACE,4CAAmE,EACpE;;AAED;EACE,iCAAwD,EACzD;;AAED;EACE,4CAAmE,EACpE;;AAED;EACE,gCAAwD,EACzD;;AAED;EACE,2CAAmE,EACpE;;AAED;EACE,mCAAyD,EAC1D;;AAED;EACE,8CAAoE,EACrE;;AAED;EACE,kCAAyD,EAC1D;;AAED;EACE,6CAAoE,EACrE;;AAED;EACE,iCAAyD,EAC1D;;AAED;EACE,4CAAoE,EACrE;;AAED;EACE,iCAAyD,EAC1D;;AAED;EACE,4CAAoE,EACrE;;AAID;EACE,iCAA6D,EAC9D;;AAED;EACE,4CAAwE,EACzE;;AAED;EACE,mCAA4D,EAC7D;;AAED;EACE,8CAAuE,EACxE;;AAED;EACE,mCAA6D,EAC9D;;AAED;EACE,8CAAwE,EACzE;;AAED;EACE,mCAA6D,EAC9D;;AAED;EACE,8CAAwE,EACzE;;AAED;EACE,mCAA6D,EAC9D;;AAED;EACE,8CAAwE,EACzE;;AAED;EACE,kCAA6D,EAC9D;;AAED;EACE,6CAAwE,EACzE;;AAED;EACE,iCAA6D,EAC9D;;AAED;EACE,4CAAwE,EACzE;;AAED;EACE,iCAA6D,EAC9D;;AAED;EACE,4CAAwE,EACzE;;AAED;EACE,iCAA6D,EAC9D;;AAED;EACE,4CAAwE,EACzE;;AAED;EACE,iCAA6D,EAC9D;;AAED;EACE,4CAAwE,EACzE;;AAED;EACE,iCAA6D,EAC9D;;AAED;EACE,4CAAwE,EACzE;;AAED;EACE,mCAA8D,EAC/D;;AAED;EACE,8CAAyE,EAC1E;;AAED;EACE,kCAA8D,EAC/D;;AAED;EACE,6CAAyE,EAC1E;;AAED;EACE,gCAA8D,EAC/D;;AAED;EACE,2CAAyE,EAC1E;;AAED;EACE,gCAA8D,EAC/D;;AAED;EACE,2CAAyE,EAC1E;;AAID;EACE,iCAAuD,EACxD;;AAED;EACE,4CAAkE,EACnE;;AAED;EACE,mCAAsD,EACvD;;AAED;EACE,8CAAiE,EAClE;;AAED;EACE,mCAAuD,EACxD;;AAED;EACE,8CAAkE,EACnE;;AAED;EACE,mCAAuD,EACxD;;AAED;EACE,8CAAkE,EACnE;;AAED;EACE,mCAAuD,EACxD;;AAED;EACE,8CAAkE,EACnE;;AAED;EACE,kCAAuD,EACxD;;AAED;EACE,6CAAkE,EACnE;;AAED;EACE,iCAAuD,EACxD;;AAED;EACE,4CAAkE,EACnE;;AAED;EACE,iCAAuD,EACxD;;AAED;EACE,4CAAkE,EACnE;;AAED;EACE,gCAAuD,EACxD;;AAED;EACE,2CAAkE,EACnE;;AAED;EACE,gCAAuD,EACxD;;AAED;EACE,2CAAkE,EACnE;;AAED;EACE,gCAAuD,EACxD;;AAED;EACE,2CAAkE,EACnE;;AAID;EACE,mCAAsD,EACvD;;AAED;EACE,8CAAiE,EAClE;;AAED;EACE,mCAAqD,EACtD;;AAED;EACE,8CAAgE,EACjE;;AAED;EACE,mCAAsD,EACvD;;AAED;EACE,8CAAiE,EAClE;;AAED;EACE,mCAAsD,EACvD;;AAED;EACE,8CAAiE,EAClE;;AAED;EACE,mCAAsD,EACvD;;AAED;EACE,8CAAiE,EAClE;;AAED;EACE,mCAAsD,EACvD;;AAED;EACE,8CAAiE,EAClE;;AAED;EACE,mCAAsD,EACvD;;AAED;EACE,8CAAiE,EAClE;;AAED;EACE,mCAAsD,EACvD;;AAED;EACE,8CAAiE,EAClE;;AAED;EACE,gCAAsD,EACvD;;AAED;EACE,2CAAiE,EAClE;;AAED;EACE,gCAAsD,EACvD;;AAED;EACE,2CAAiE,EAClE;;AAED;EACE,gCAAsD,EACvD;;AAED;EACE,2CAAiE,EAClE;;AAID;EACE,kCAA2D,EAC5D;;AAED;EACE,6CAAsE,EACvE;;AAED;EACE,mCAA0D,EAC3D;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","file":"material.min.css","sourcesContent":["/**\n * material-design-lite - Material Design Components in CSS, JS and HTML\n * @version v1.0.6\n * @license Apache-2.0\n * @copyright 2015 Google, Inc.\n * @link https://github.com/google/material-design-lite\n */\n@charset \"UTF-8\";html{color:rgba(0,0,0,.87)}::-moz-selection{background:#b3d4fc;text-shadow:none}::selection{background:#b3d4fc;text-shadow:none}hr{display:block;height:1px;border:0;border-top:1px solid #ccc;margin:1em 0;padding:0}audio,canvas,iframe,img,svg,video{vertical-align:middle}fieldset{border:0;margin:0;padding:0}textarea{resize:vertical}.browserupgrade{margin:.2em 0;background:#ccc;color:#000;padding:.2em 0}.hidden{display:none!important}.visuallyhidden{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.visuallyhidden.focusable:active,.visuallyhidden.focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}.invisible{visibility:hidden}.clearfix:before,.clearfix:after{content:\" \";display:table}.clearfix:after{clear:both}@media print{*,*:before,*:after,*:first-letter,*:first-line{background:0 0!important;color:#000!important;box-shadow:none!important;text-shadow:none!important}a,a:visited{text-decoration:underline}a[href]:after{content:\" (\" attr(href)\")\"}abbr[title]:after{content:\" (\" attr(title)\")\"}a[href^=\"#\"]:after,a[href^=\"javascript:\"]:after{content:\"\"}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100%!important}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}}a,.mdl-accordion,.mdl-button,.mdl-card,.mdl-checkbox,.mdl-dropdown-menu,.mdl-icon-toggle,.mdl-item,.mdl-radio,.mdl-slider,.mdl-switch,.mdl-tabs__tab{-webkit-tap-highlight-color:transparent;-webkit-tap-highlight-color:rgba(255,255,255,0)}html{width:100%;height:100%;-ms-touch-action:manipulation;touch-action:manipulation}body{width:100%;min-height:100%;margin:0}main{display:block}*[hidden]{display:none!important}html,body{font-family:\"Helvetica\",\"Arial\",sans-serif;font-size:14px;font-weight:400;line-height:20px}h1,h2,h3,h4,h5,h6,p{padding:0}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small{font-family:\"Roboto\",\"Helvetica\",\"Arial\",sans-serif;font-weight:400;line-height:1.35;letter-spacing:-.02em;opacity:.54;font-size:.6em}h1{font-size:56px;line-height:1.35;letter-spacing:-.02em;margin:24px 0}h1,h2{font-family:\"Roboto\",\"Helvetica\",\"Arial\",sans-serif;font-weight:400}h2{font-size:45px;line-height:48px}h2,h3{margin:24px 0}h3{font-size:34px;line-height:40px}h3,h4{font-family:\"Roboto\",\"Helvetica\",\"Arial\",sans-serif;font-weight:400}h4{font-size:24px;line-height:32px;-moz-osx-font-smoothing:grayscale;margin:24px 0 16px}h5{font-size:20px;font-weight:500;line-height:1;letter-spacing:.02em}h5,h6{font-family:\"Roboto\",\"Helvetica\",\"Arial\",sans-serif;margin:24px 0 16px}h6{font-size:16px;letter-spacing:.04em}h6,p{font-weight:400;line-height:24px}p{font-size:14px;letter-spacing:0;margin:0 0 16px}a{color:#ff4081;font-weight:500}blockquote{font-family:\"Roboto\",\"Helvetica\",\"Arial\",sans-serif;position:relative;font-size:24px;font-weight:300;font-style:italic;line-height:1.35;letter-spacing:.08em}blockquote:before{position:absolute;left:-.5em;content:'“'}blockquote:after{content:'”';margin-left:-.05em}mark{background-color:#f4ff81}dt{font-weight:700}address{font-size:12px;line-height:1;font-style:normal}address,ul,ol{font-weight:400;letter-spacing:0}ul,ol{font-size:14px;line-height:24px}.mdl-typography--display-4,.mdl-typography--display-4-color-contrast{font-family:\"Roboto\",\"Helvetica\",\"Arial\",sans-serif;font-size:112px;font-weight:300;line-height:1;letter-spacing:-.04em}.mdl-typography--display-4-color-contrast{opacity:.54}.mdl-typography--display-3,.mdl-typography--display-3-color-contrast{font-family:\"Roboto\",\"Helvetica\",\"Arial\",sans-serif;font-size:56px;font-weight:400;line-height:1.35;letter-spacing:-.02em}.mdl-typography--display-3-color-contrast{opacity:.54}.mdl-typography--display-2,.mdl-typography--display-2-color-contrast{font-family:\"Roboto\",\"Helvetica\",\"Arial\",sans-serif;font-size:45px;font-weight:400;line-height:48px}.mdl-typography--display-2-color-contrast{opacity:.54}.mdl-typography--display-1,.mdl-typography--display-1-color-contrast{font-family:\"Roboto\",\"Helvetica\",\"Arial\",sans-serif;font-size:34px;font-weight:400;line-height:40px}.mdl-typography--display-1-color-contrast{opacity:.54}.mdl-typography--headline,.mdl-typography--headline-color-contrast{font-family:\"Roboto\",\"Helvetica\",\"Arial\",sans-serif;font-size:24px;font-weight:400;line-height:32px;-moz-osx-font-smoothing:grayscale}.mdl-typography--headline-color-contrast{opacity:.87}.mdl-typography--title,.mdl-typography--title-color-contrast{font-family:\"Roboto\",\"Helvetica\",\"Arial\",sans-serif;font-size:20px;font-weight:500;line-height:1;letter-spacing:.02em}.mdl-typography--title-color-contrast{opacity:.87}.mdl-typography--subhead,.mdl-typography--subhead-color-contrast{font-family:\"Roboto\",\"Helvetica\",\"Arial\",sans-serif;font-size:16px;font-weight:400;line-height:24px;letter-spacing:.04em}.mdl-typography--subhead-color-contrast{opacity:.87}.mdl-typography--body-2,.mdl-typography--body-2-color-contrast{font-size:14px;font-weight:700;line-height:24px;letter-spacing:0}.mdl-typography--body-2-color-contrast{opacity:.87}.mdl-typography--body-1,.mdl-typography--body-1-color-contrast{font-size:14px;font-weight:400;line-height:24px;letter-spacing:0}.mdl-typography--body-1-color-contrast{opacity:.87}.mdl-typography--body-2-force-preferred-font,.mdl-typography--body-2-force-preferred-font-color-contrast{font-family:\"Roboto\",\"Helvetica\",\"Arial\",sans-serif;font-size:14px;font-weight:500;line-height:24px;letter-spacing:0}.mdl-typography--body-2-force-preferred-font-color-contrast{opacity:.87}.mdl-typography--body-1-force-preferred-font,.mdl-typography--body-1-force-preferred-font-color-contrast{font-family:\"Roboto\",\"Helvetica\",\"Arial\",sans-serif;font-size:14px;font-weight:400;line-height:24px;letter-spacing:0}.mdl-typography--body-1-force-preferred-font-color-contrast{opacity:.87}.mdl-typography--caption,.mdl-typography--caption-force-preferred-font{font-size:12px;font-weight:400;line-height:1;letter-spacing:0}.mdl-typography--caption-force-preferred-font{font-family:\"Roboto\",\"Helvetica\",\"Arial\",sans-serif}.mdl-typography--caption-color-contrast,.mdl-typography--caption-force-preferred-font-color-contrast{font-size:12px;font-weight:400;line-height:1;letter-spacing:0;opacity:.54}.mdl-typography--caption-force-preferred-font-color-contrast,.mdl-typography--menu{font-family:\"Roboto\",\"Helvetica\",\"Arial\",sans-serif}.mdl-typography--menu{font-size:14px;font-weight:500;line-height:1;letter-spacing:0}.mdl-typography--menu-color-contrast{opacity:.87}.mdl-typography--menu-color-contrast,.mdl-typography--button,.mdl-typography--button-color-contrast{font-family:\"Roboto\",\"Helvetica\",\"Arial\",sans-serif;font-size:14px;font-weight:500;line-height:1;letter-spacing:0}.mdl-typography--button,.mdl-typography--button-color-contrast{text-transform:uppercase}.mdl-typography--button-color-contrast{opacity:.87}.mdl-typography--text-left{text-align:left}.mdl-typography--text-right{text-align:right}.mdl-typography--text-center{text-align:center}.mdl-typography--text-justify{text-align:justify}.mdl-typography--text-nowrap{white-space:nowrap}.mdl-typography--text-lowercase{text-transform:lowercase}.mdl-typography--text-uppercase{text-transform:uppercase}.mdl-typography--text-capitalize{text-transform:capitalize}.mdl-typography--font-thin{font-weight:200!important}.mdl-typography--font-light{font-weight:300!important}.mdl-typography--font-regular{font-weight:400!important}.mdl-typography--font-medium{font-weight:500!important}.mdl-typography--font-bold{font-weight:700!important}.mdl-typography--font-black{font-weight:900!important}.mdl-color-text--red{color:#f44336 !important}.mdl-color--red{background-color:#f44336 !important}.mdl-color-text--red-50{color:#ffebee !important}.mdl-color--red-50{background-color:#ffebee !important}.mdl-color-text--red-100{color:#ffcdd2 !important}.mdl-color--red-100{background-color:#ffcdd2 !important}.mdl-color-text--red-200{color:#ef9a9a !important}.mdl-color--red-200{background-color:#ef9a9a !important}.mdl-color-text--red-300{color:#e57373 !important}.mdl-color--red-300{background-color:#e57373 !important}.mdl-color-text--red-400{color:#ef5350 !important}.mdl-color--red-400{background-color:#ef5350 !important}.mdl-color-text--red-500{color:#f44336 !important}.mdl-color--red-500{background-color:#f44336 !important}.mdl-color-text--red-600{color:#e53935 !important}.mdl-color--red-600{background-color:#e53935 !important}.mdl-color-text--red-700{color:#d32f2f !important}.mdl-color--red-700{background-color:#d32f2f !important}.mdl-color-text--red-800{color:#c62828 !important}.mdl-color--red-800{background-color:#c62828 !important}.mdl-color-text--red-900{color:#b71c1c !important}.mdl-color--red-900{background-color:#b71c1c !important}.mdl-color-text--red-A100{color:#ff8a80 !important}.mdl-color--red-A100{background-color:#ff8a80 !important}.mdl-color-text--red-A200{color:#ff5252 !important}.mdl-color--red-A200{background-color:#ff5252 !important}.mdl-color-text--red-A400{color:#ff1744 !important}.mdl-color--red-A400{background-color:#ff1744 !important}.mdl-color-text--red-A700{color:#d50000 !important}.mdl-color--red-A700{background-color:#d50000 !important}.mdl-color-text--pink{color:#e91e63 !important}.mdl-color--pink{background-color:#e91e63 !important}.mdl-color-text--pink-50{color:#fce4ec !important}.mdl-color--pink-50{background-color:#fce4ec !important}.mdl-color-text--pink-100{color:#f8bbd0 !important}.mdl-color--pink-100{background-color:#f8bbd0 !important}.mdl-color-text--pink-200{color:#f48fb1 !important}.mdl-color--pink-200{background-color:#f48fb1 !important}.mdl-color-text--pink-300{color:#f06292 !important}.mdl-color--pink-300{background-color:#f06292 !important}.mdl-color-text--pink-400{color:#ec407a !important}.mdl-color--pink-400{background-color:#ec407a !important}.mdl-color-text--pink-500{color:#e91e63 !important}.mdl-color--pink-500{background-color:#e91e63 !important}.mdl-color-text--pink-600{color:#d81b60 !important}.mdl-color--pink-600{background-color:#d81b60 !important}.mdl-color-text--pink-700{color:#c2185b !important}.mdl-color--pink-700{background-color:#c2185b !important}.mdl-color-text--pink-800{color:#ad1457 !important}.mdl-color--pink-800{background-color:#ad1457 !important}.mdl-color-text--pink-900{color:#880e4f !important}.mdl-color--pink-900{background-color:#880e4f !important}.mdl-color-text--pink-A100{color:#ff80ab !important}.mdl-color--pink-A100{background-color:#ff80ab !important}.mdl-color-text--pink-A200{color:#ff4081 !important}.mdl-color--pink-A200{background-color:#ff4081 !important}.mdl-color-text--pink-A400{color:#f50057 !important}.mdl-color--pink-A400{background-color:#f50057 !important}.mdl-color-text--pink-A700{color:#c51162 !important}.mdl-color--pink-A700{background-color:#c51162 !important}.mdl-color-text--purple{color:#9c27b0 !important}.mdl-color--purple{background-color:#9c27b0 !important}.mdl-color-text--purple-50{color:#f3e5f5 !important}.mdl-color--purple-50{background-color:#f3e5f5 !important}.mdl-color-text--purple-100{color:#e1bee7 !important}.mdl-color--purple-100{background-color:#e1bee7 !important}.mdl-color-text--purple-200{color:#ce93d8 !important}.mdl-color--purple-200{background-color:#ce93d8 !important}.mdl-color-text--purple-300{color:#ba68c8 !important}.mdl-color--purple-300{background-color:#ba68c8 !important}.mdl-color-text--purple-400{color:#ab47bc !important}.mdl-color--purple-400{background-color:#ab47bc !important}.mdl-color-text--purple-500{color:#9c27b0 !important}.mdl-color--purple-500{background-color:#9c27b0 !important}.mdl-color-text--purple-600{color:#8e24aa !important}.mdl-color--purple-600{background-color:#8e24aa !important}.mdl-color-text--purple-700{color:#7b1fa2 !important}.mdl-color--purple-700{background-color:#7b1fa2 !important}.mdl-color-text--purple-800{color:#6a1b9a !important}.mdl-color--purple-800{background-color:#6a1b9a !important}.mdl-color-text--purple-900{color:#4a148c !important}.mdl-color--purple-900{background-color:#4a148c !important}.mdl-color-text--purple-A100{color:#ea80fc !important}.mdl-color--purple-A100{background-color:#ea80fc !important}.mdl-color-text--purple-A200{color:#e040fb !important}.mdl-color--purple-A200{background-color:#e040fb !important}.mdl-color-text--purple-A400{color:#d500f9 !important}.mdl-color--purple-A400{background-color:#d500f9 !important}.mdl-color-text--purple-A700{color:#a0f !important}.mdl-color--purple-A700{background-color:#a0f !important}.mdl-color-text--deep-purple{color:#673ab7 !important}.mdl-color--deep-purple{background-color:#673ab7 !important}.mdl-color-text--deep-purple-50{color:#ede7f6 !important}.mdl-color--deep-purple-50{background-color:#ede7f6 !important}.mdl-color-text--deep-purple-100{color:#d1c4e9 !important}.mdl-color--deep-purple-100{background-color:#d1c4e9 !important}.mdl-color-text--deep-purple-200{color:#b39ddb !important}.mdl-color--deep-purple-200{background-color:#b39ddb !important}.mdl-color-text--deep-purple-300{color:#9575cd !important}.mdl-color--deep-purple-300{background-color:#9575cd !important}.mdl-color-text--deep-purple-400{color:#7e57c2 !important}.mdl-color--deep-purple-400{background-color:#7e57c2 !important}.mdl-color-text--deep-purple-500{color:#673ab7 !important}.mdl-color--deep-purple-500{background-color:#673ab7 !important}.mdl-color-text--deep-purple-600{color:#5e35b1 !important}.mdl-color--deep-purple-600{background-color:#5e35b1 !important}.mdl-color-text--deep-purple-700{color:#512da8 !important}.mdl-color--deep-purple-700{background-color:#512da8 !important}.mdl-color-text--deep-purple-800{color:#4527a0 !important}.mdl-color--deep-purple-800{background-color:#4527a0 !important}.mdl-color-text--deep-purple-900{color:#311b92 !important}.mdl-color--deep-purple-900{background-color:#311b92 !important}.mdl-color-text--deep-purple-A100{color:#b388ff !important}.mdl-color--deep-purple-A100{background-color:#b388ff !important}.mdl-color-text--deep-purple-A200{color:#7c4dff !important}.mdl-color--deep-purple-A200{background-color:#7c4dff !important}.mdl-color-text--deep-purple-A400{color:#651fff !important}.mdl-color--deep-purple-A400{background-color:#651fff !important}.mdl-color-text--deep-purple-A700{color:#6200ea !important}.mdl-color--deep-purple-A700{background-color:#6200ea !important}.mdl-color-text--indigo{color:#3f51b5 !important}.mdl-color--indigo{background-color:#3f51b5 !important}.mdl-color-text--indigo-50{color:#e8eaf6 !important}.mdl-color--indigo-50{background-color:#e8eaf6 !important}.mdl-color-text--indigo-100{color:#c5cae9 !important}.mdl-color--indigo-100{background-color:#c5cae9 !important}.mdl-color-text--indigo-200{color:#9fa8da !important}.mdl-color--indigo-200{background-color:#9fa8da !important}.mdl-color-text--indigo-300{color:#7986cb !important}.mdl-color--indigo-300{background-color:#7986cb !important}.mdl-color-text--indigo-400{color:#5c6bc0 !important}.mdl-color--indigo-400{background-color:#5c6bc0 !important}.mdl-color-text--indigo-500{color:#3f51b5 !important}.mdl-color--indigo-500{background-color:#3f51b5 !important}.mdl-color-text--indigo-600{color:#3949ab !important}.mdl-color--indigo-600{background-color:#3949ab !important}.mdl-color-text--indigo-700{color:#303f9f !important}.mdl-color--indigo-700{background-color:#303f9f !important}.mdl-color-text--indigo-800{color:#283593 !important}.mdl-color--indigo-800{background-color:#283593 !important}.mdl-color-text--indigo-900{color:#1a237e !important}.mdl-color--indigo-900{background-color:#1a237e !important}.mdl-color-text--indigo-A100{color:#8c9eff !important}.mdl-color--indigo-A100{background-color:#8c9eff !important}.mdl-color-text--indigo-A200{color:#536dfe !important}.mdl-color--indigo-A200{background-color:#536dfe !important}.mdl-color-text--indigo-A400{color:#3d5afe !important}.mdl-color--indigo-A400{background-color:#3d5afe !important}.mdl-color-text--indigo-A700{color:#304ffe !important}.mdl-color--indigo-A700{background-color:#304ffe !important}.mdl-color-text--blue{color:#2196f3 !important}.mdl-color--blue{background-color:#2196f3 !important}.mdl-color-text--blue-50{color:#e3f2fd !important}.mdl-color--blue-50{background-color:#e3f2fd !important}.mdl-color-text--blue-100{color:#bbdefb !important}.mdl-color--blue-100{background-color:#bbdefb !important}.mdl-color-text--blue-200{color:#90caf9 !important}.mdl-color--blue-200{background-color:#90caf9 !important}.mdl-color-text--blue-300{color:#64b5f6 !important}.mdl-color--blue-300{background-color:#64b5f6 !important}.mdl-color-text--blue-400{color:#42a5f5 !important}.mdl-color--blue-400{background-color:#42a5f5 !important}.mdl-color-text--blue-500{color:#2196f3 !important}.mdl-color--blue-500{background-color:#2196f3 !important}.mdl-color-text--blue-600{color:#1e88e5 !important}.mdl-color--blue-600{background-color:#1e88e5 !important}.mdl-color-text--blue-700{color:#1976d2 !important}.mdl-color--blue-700{background-color:#1976d2 !important}.mdl-color-text--blue-800{color:#1565c0 !important}.mdl-color--blue-800{background-color:#1565c0 !important}.mdl-color-text--blue-900{color:#0d47a1 !important}.mdl-color--blue-900{background-color:#0d47a1 !important}.mdl-color-text--blue-A100{color:#82b1ff !important}.mdl-color--blue-A100{background-color:#82b1ff !important}.mdl-color-text--blue-A200{color:#448aff !important}.mdl-color--blue-A200{background-color:#448aff !important}.mdl-color-text--blue-A400{color:#2979ff !important}.mdl-color--blue-A400{background-color:#2979ff !important}.mdl-color-text--blue-A700{color:#2962ff !important}.mdl-color--blue-A700{background-color:#2962ff !important}.mdl-color-text--light-blue{color:#03a9f4 !important}.mdl-color--light-blue{background-color:#03a9f4 !important}.mdl-color-text--light-blue-50{color:#e1f5fe !important}.mdl-color--light-blue-50{background-color:#e1f5fe !important}.mdl-color-text--light-blue-100{color:#b3e5fc !important}.mdl-color--light-blue-100{background-color:#b3e5fc !important}.mdl-color-text--light-blue-200{color:#81d4fa !important}.mdl-color--light-blue-200{background-color:#81d4fa !important}.mdl-color-text--light-blue-300{color:#4fc3f7 !important}.mdl-color--light-blue-300{background-color:#4fc3f7 !important}.mdl-color-text--light-blue-400{color:#29b6f6 !important}.mdl-color--light-blue-400{background-color:#29b6f6 !important}.mdl-color-text--light-blue-500{color:#03a9f4 !important}.mdl-color--light-blue-500{background-color:#03a9f4 !important}.mdl-color-text--light-blue-600{color:#039be5 !important}.mdl-color--light-blue-600{background-color:#039be5 !important}.mdl-color-text--light-blue-700{color:#0288d1 !important}.mdl-color--light-blue-700{background-color:#0288d1 !important}.mdl-color-text--light-blue-800{color:#0277bd !important}.mdl-color--light-blue-800{background-color:#0277bd !important}.mdl-color-text--light-blue-900{color:#01579b !important}.mdl-color--light-blue-900{background-color:#01579b !important}.mdl-color-text--light-blue-A100{color:#80d8ff !important}.mdl-color--light-blue-A100{background-color:#80d8ff !important}.mdl-color-text--light-blue-A200{color:#40c4ff !important}.mdl-color--light-blue-A200{background-color:#40c4ff !important}.mdl-color-text--light-blue-A400{color:#00b0ff !important}.mdl-color--light-blue-A400{background-color:#00b0ff !important}.mdl-color-text--light-blue-A700{color:#0091ea !important}.mdl-color--light-blue-A700{background-color:#0091ea !important}.mdl-color-text--cyan{color:#00bcd4 !important}.mdl-color--cyan{background-color:#00bcd4 !important}.mdl-color-text--cyan-50{color:#e0f7fa !important}.mdl-color--cyan-50{background-color:#e0f7fa !important}.mdl-color-text--cyan-100{color:#b2ebf2 !important}.mdl-color--cyan-100{background-color:#b2ebf2 !important}.mdl-color-text--cyan-200{color:#80deea !important}.mdl-color--cyan-200{background-color:#80deea !important}.mdl-color-text--cyan-300{color:#4dd0e1 !important}.mdl-color--cyan-300{background-color:#4dd0e1 !important}.mdl-color-text--cyan-400{color:#26c6da !important}.mdl-color--cyan-400{background-color:#26c6da !important}.mdl-color-text--cyan-500{color:#00bcd4 !important}.mdl-color--cyan-500{background-color:#00bcd4 !important}.mdl-color-text--cyan-600{color:#00acc1 !important}.mdl-color--cyan-600{background-color:#00acc1 !important}.mdl-color-text--cyan-700{color:#0097a7 !important}.mdl-color--cyan-700{background-color:#0097a7 !important}.mdl-color-text--cyan-800{color:#00838f !important}.mdl-color--cyan-800{background-color:#00838f !important}.mdl-color-text--cyan-900{color:#006064 !important}.mdl-color--cyan-900{background-color:#006064 !important}.mdl-color-text--cyan-A100{color:#84ffff !important}.mdl-color--cyan-A100{background-color:#84ffff !important}.mdl-color-text--cyan-A200{color:#18ffff !important}.mdl-color--cyan-A200{background-color:#18ffff !important}.mdl-color-text--cyan-A400{color:#00e5ff !important}.mdl-color--cyan-A400{background-color:#00e5ff !important}.mdl-color-text--cyan-A700{color:#00b8d4 !important}.mdl-color--cyan-A700{background-color:#00b8d4 !important}.mdl-color-text--teal{color:#009688 !important}.mdl-color--teal{background-color:#009688 !important}.mdl-color-text--teal-50{color:#e0f2f1 !important}.mdl-color--teal-50{background-color:#e0f2f1 !important}.mdl-color-text--teal-100{color:#b2dfdb !important}.mdl-color--teal-100{background-color:#b2dfdb !important}.mdl-color-text--teal-200{color:#80cbc4 !important}.mdl-color--teal-200{background-color:#80cbc4 !important}.mdl-color-text--teal-300{color:#4db6ac !important}.mdl-color--teal-300{background-color:#4db6ac !important}.mdl-color-text--teal-400{color:#26a69a !important}.mdl-color--teal-400{background-color:#26a69a !important}.mdl-color-text--teal-500{color:#009688 !important}.mdl-color--teal-500{background-color:#009688 !important}.mdl-color-text--teal-600{color:#00897b !important}.mdl-color--teal-600{background-color:#00897b !important}.mdl-color-text--teal-700{color:#00796b !important}.mdl-color--teal-700{background-color:#00796b !important}.mdl-color-text--teal-800{color:#00695c !important}.mdl-color--teal-800{background-color:#00695c !important}.mdl-color-text--teal-900{color:#004d40 !important}.mdl-color--teal-900{background-color:#004d40 !important}.mdl-color-text--teal-A100{color:#a7ffeb !important}.mdl-color--teal-A100{background-color:#a7ffeb !important}.mdl-color-text--teal-A200{color:#64ffda !important}.mdl-color--teal-A200{background-color:#64ffda !important}.mdl-color-text--teal-A400{color:#1de9b6 !important}.mdl-color--teal-A400{background-color:#1de9b6 !important}.mdl-color-text--teal-A700{color:#00bfa5 !important}.mdl-color--teal-A700{background-color:#00bfa5 !important}.mdl-color-text--green{color:#4caf50 !important}.mdl-color--green{background-color:#4caf50 !important}.mdl-color-text--green-50{color:#e8f5e9 !important}.mdl-color--green-50{background-color:#e8f5e9 !important}.mdl-color-text--green-100{color:#c8e6c9 !important}.mdl-color--green-100{background-color:#c8e6c9 !important}.mdl-color-text--green-200{color:#a5d6a7 !important}.mdl-color--green-200{background-color:#a5d6a7 !important}.mdl-color-text--green-300{color:#81c784 !important}.mdl-color--green-300{background-color:#81c784 !important}.mdl-color-text--green-400{color:#66bb6a !important}.mdl-color--green-400{background-color:#66bb6a !important}.mdl-color-text--green-500{color:#4caf50 !important}.mdl-color--green-500{background-color:#4caf50 !important}.mdl-color-text--green-600{color:#43a047 !important}.mdl-color--green-600{background-color:#43a047 !important}.mdl-color-text--green-700{color:#388e3c !important}.mdl-color--green-700{background-color:#388e3c !important}.mdl-color-text--green-800{color:#2e7d32 !important}.mdl-color--green-800{background-color:#2e7d32 !important}.mdl-color-text--green-900{color:#1b5e20 !important}.mdl-color--green-900{background-color:#1b5e20 !important}.mdl-color-text--green-A100{color:#b9f6ca !important}.mdl-color--green-A100{background-color:#b9f6ca !important}.mdl-color-text--green-A200{color:#69f0ae !important}.mdl-color--green-A200{background-color:#69f0ae !important}.mdl-color-text--green-A400{color:#00e676 !important}.mdl-color--green-A400{background-color:#00e676 !important}.mdl-color-text--green-A700{color:#00c853 !important}.mdl-color--green-A700{background-color:#00c853 !important}.mdl-color-text--light-green{color:#8bc34a !important}.mdl-color--light-green{background-color:#8bc34a !important}.mdl-color-text--light-green-50{color:#f1f8e9 !important}.mdl-color--light-green-50{background-color:#f1f8e9 !important}.mdl-color-text--light-green-100{color:#dcedc8 !important}.mdl-color--light-green-100{background-color:#dcedc8 !important}.mdl-color-text--light-green-200{color:#c5e1a5 !important}.mdl-color--light-green-200{background-color:#c5e1a5 !important}.mdl-color-text--light-green-300{color:#aed581 !important}.mdl-color--light-green-300{background-color:#aed581 !important}.mdl-color-text--light-green-400{color:#9ccc65 !important}.mdl-color--light-green-400{background-color:#9ccc65 !important}.mdl-color-text--light-green-500{color:#8bc34a !important}.mdl-color--light-green-500{background-color:#8bc34a !important}.mdl-color-text--light-green-600{color:#7cb342 !important}.mdl-color--light-green-600{background-color:#7cb342 !important}.mdl-color-text--light-green-700{color:#689f38 !important}.mdl-color--light-green-700{background-color:#689f38 !important}.mdl-color-text--light-green-800{color:#558b2f !important}.mdl-color--light-green-800{background-color:#558b2f !important}.mdl-color-text--light-green-900{color:#33691e !important}.mdl-color--light-green-900{background-color:#33691e !important}.mdl-color-text--light-green-A100{color:#ccff90 !important}.mdl-color--light-green-A100{background-color:#ccff90 !important}.mdl-color-text--light-green-A200{color:#b2ff59 !important}.mdl-color--light-green-A200{background-color:#b2ff59 !important}.mdl-color-text--light-green-A400{color:#76ff03 !important}.mdl-color--light-green-A400{background-color:#76ff03 !important}.mdl-color-text--light-green-A700{color:#64dd17 !important}.mdl-color--light-green-A700{background-color:#64dd17 !important}.mdl-color-text--lime{color:#cddc39 !important}.mdl-color--lime{background-color:#cddc39 !important}.mdl-color-text--lime-50{color:#f9fbe7 !important}.mdl-color--lime-50{background-color:#f9fbe7 !important}.mdl-color-text--lime-100{color:#f0f4c3 !important}.mdl-color--lime-100{background-color:#f0f4c3 !important}.mdl-color-text--lime-200{color:#e6ee9c !important}.mdl-color--lime-200{background-color:#e6ee9c !important}.mdl-color-text--lime-300{color:#dce775 !important}.mdl-color--lime-300{background-color:#dce775 !important}.mdl-color-text--lime-400{color:#d4e157 !important}.mdl-color--lime-400{background-color:#d4e157 !important}.mdl-color-text--lime-500{color:#cddc39 !important}.mdl-color--lime-500{background-color:#cddc39 !important}.mdl-color-text--lime-600{color:#c0ca33 !important}.mdl-color--lime-600{background-color:#c0ca33 !important}.mdl-color-text--lime-700{color:#afb42b !important}.mdl-color--lime-700{background-color:#afb42b !important}.mdl-color-text--lime-800{color:#9e9d24 !important}.mdl-color--lime-800{background-color:#9e9d24 !important}.mdl-color-text--lime-900{color:#827717 !important}.mdl-color--lime-900{background-color:#827717 !important}.mdl-color-text--lime-A100{color:#f4ff81 !important}.mdl-color--lime-A100{background-color:#f4ff81 !important}.mdl-color-text--lime-A200{color:#eeff41 !important}.mdl-color--lime-A200{background-color:#eeff41 !important}.mdl-color-text--lime-A400{color:#c6ff00 !important}.mdl-color--lime-A400{background-color:#c6ff00 !important}.mdl-color-text--lime-A700{color:#aeea00 !important}.mdl-color--lime-A700{background-color:#aeea00 !important}.mdl-color-text--yellow{color:#ffeb3b !important}.mdl-color--yellow{background-color:#ffeb3b !important}.mdl-color-text--yellow-50{color:#fffde7 !important}.mdl-color--yellow-50{background-color:#fffde7 !important}.mdl-color-text--yellow-100{color:#fff9c4 !important}.mdl-color--yellow-100{background-color:#fff9c4 !important}.mdl-color-text--yellow-200{color:#fff59d !important}.mdl-color--yellow-200{background-color:#fff59d !important}.mdl-color-text--yellow-300{color:#fff176 !important}.mdl-color--yellow-300{background-color:#fff176 !important}.mdl-color-text--yellow-400{color:#ffee58 !important}.mdl-color--yellow-400{background-color:#ffee58 !important}.mdl-color-text--yellow-500{color:#ffeb3b !important}.mdl-color--yellow-500{background-color:#ffeb3b !important}.mdl-color-text--yellow-600{color:#fdd835 !important}.mdl-color--yellow-600{background-color:#fdd835 !important}.mdl-color-text--yellow-700{color:#fbc02d !important}.mdl-color--yellow-700{background-color:#fbc02d !important}.mdl-color-text--yellow-800{color:#f9a825 !important}.mdl-color--yellow-800{background-color:#f9a825 !important}.mdl-color-text--yellow-900{color:#f57f17 !important}.mdl-color--yellow-900{background-color:#f57f17 !important}.mdl-color-text--yellow-A100{color:#ffff8d !important}.mdl-color--yellow-A100{background-color:#ffff8d !important}.mdl-color-text--yellow-A200{color:#ff0 !important}.mdl-color--yellow-A200{background-color:#ff0 !important}.mdl-color-text--yellow-A400{color:#ffea00 !important}.mdl-color--yellow-A400{background-color:#ffea00 !important}.mdl-color-text--yellow-A700{color:#ffd600 !important}.mdl-color--yellow-A700{background-color:#ffd600 !important}.mdl-color-text--amber{color:#ffc107 !important}.mdl-color--amber{background-color:#ffc107 !important}.mdl-color-text--amber-50{color:#fff8e1 !important}.mdl-color--amber-50{background-color:#fff8e1 !important}.mdl-color-text--amber-100{color:#ffecb3 !important}.mdl-color--amber-100{background-color:#ffecb3 !important}.mdl-color-text--amber-200{color:#ffe082 !important}.mdl-color--amber-200{background-color:#ffe082 !important}.mdl-color-text--amber-300{color:#ffd54f !important}.mdl-color--amber-300{background-color:#ffd54f !important}.mdl-color-text--amber-400{color:#ffca28 !important}.mdl-color--amber-400{background-color:#ffca28 !important}.mdl-color-text--amber-500{color:#ffc107 !important}.mdl-color--amber-500{background-color:#ffc107 !important}.mdl-color-text--amber-600{color:#ffb300 !important}.mdl-color--amber-600{background-color:#ffb300 !important}.mdl-color-text--amber-700{color:#ffa000 !important}.mdl-color--amber-700{background-color:#ffa000 !important}.mdl-color-text--amber-800{color:#ff8f00 !important}.mdl-color--amber-800{background-color:#ff8f00 !important}.mdl-color-text--amber-900{color:#ff6f00 !important}.mdl-color--amber-900{background-color:#ff6f00 !important}.mdl-color-text--amber-A100{color:#ffe57f !important}.mdl-color--amber-A100{background-color:#ffe57f !important}.mdl-color-text--amber-A200{color:#ffd740 !important}.mdl-color--amber-A200{background-color:#ffd740 !important}.mdl-color-text--amber-A400{color:#ffc400 !important}.mdl-color--amber-A400{background-color:#ffc400 !important}.mdl-color-text--amber-A700{color:#ffab00 !important}.mdl-color--amber-A700{background-color:#ffab00 !important}.mdl-color-text--orange{color:#ff9800 !important}.mdl-color--orange{background-color:#ff9800 !important}.mdl-color-text--orange-50{color:#fff3e0 !important}.mdl-color--orange-50{background-color:#fff3e0 !important}.mdl-color-text--orange-100{color:#ffe0b2 !important}.mdl-color--orange-100{background-color:#ffe0b2 !important}.mdl-color-text--orange-200{color:#ffcc80 !important}.mdl-color--orange-200{background-color:#ffcc80 !important}.mdl-color-text--orange-300{color:#ffb74d !important}.mdl-color--orange-300{background-color:#ffb74d !important}.mdl-color-text--orange-400{color:#ffa726 !important}.mdl-color--orange-400{background-color:#ffa726 !important}.mdl-color-text--orange-500{color:#ff9800 !important}.mdl-color--orange-500{background-color:#ff9800 !important}.mdl-color-text--orange-600{color:#fb8c00 !important}.mdl-color--orange-600{background-color:#fb8c00 !important}.mdl-color-text--orange-700{color:#f57c00 !important}.mdl-color--orange-700{background-color:#f57c00 !important}.mdl-color-text--orange-800{color:#ef6c00 !important}.mdl-color--orange-800{background-color:#ef6c00 !important}.mdl-color-text--orange-900{color:#e65100 !important}.mdl-color--orange-900{background-color:#e65100 !important}.mdl-color-text--orange-A100{color:#ffd180 !important}.mdl-color--orange-A100{background-color:#ffd180 !important}.mdl-color-text--orange-A200{color:#ffab40 !important}.mdl-color--orange-A200{background-color:#ffab40 !important}.mdl-color-text--orange-A400{color:#ff9100 !important}.mdl-color--orange-A400{background-color:#ff9100 !important}.mdl-color-text--orange-A700{color:#ff6d00 !important}.mdl-color--orange-A700{background-color:#ff6d00 !important}.mdl-color-text--deep-orange{color:#ff5722 !important}.mdl-color--deep-orange{background-color:#ff5722 !important}.mdl-color-text--deep-orange-50{color:#fbe9e7 !important}.mdl-color--deep-orange-50{background-color:#fbe9e7 !important}.mdl-color-text--deep-orange-100{color:#ffccbc !important}.mdl-color--deep-orange-100{background-color:#ffccbc !important}.mdl-color-text--deep-orange-200{color:#ffab91 !important}.mdl-color--deep-orange-200{background-color:#ffab91 !important}.mdl-color-text--deep-orange-300{color:#ff8a65 !important}.mdl-color--deep-orange-300{background-color:#ff8a65 !important}.mdl-color-text--deep-orange-400{color:#ff7043 !important}.mdl-color--deep-orange-400{background-color:#ff7043 !important}.mdl-color-text--deep-orange-500{color:#ff5722 !important}.mdl-color--deep-orange-500{background-color:#ff5722 !important}.mdl-color-text--deep-orange-600{color:#f4511e !important}.mdl-color--deep-orange-600{background-color:#f4511e !important}.mdl-color-text--deep-orange-700{color:#e64a19 !important}.mdl-color--deep-orange-700{background-color:#e64a19 !important}.mdl-color-text--deep-orange-800{color:#d84315 !important}.mdl-color--deep-orange-800{background-color:#d84315 !important}.mdl-color-text--deep-orange-900{color:#bf360c !important}.mdl-color--deep-orange-900{background-color:#bf360c !important}.mdl-color-text--deep-orange-A100{color:#ff9e80 !important}.mdl-color--deep-orange-A100{background-color:#ff9e80 !important}.mdl-color-text--deep-orange-A200{color:#ff6e40 !important}.mdl-color--deep-orange-A200{background-color:#ff6e40 !important}.mdl-color-text--deep-orange-A400{color:#ff3d00 !important}.mdl-color--deep-orange-A400{background-color:#ff3d00 !important}.mdl-color-text--deep-orange-A700{color:#dd2c00 !important}.mdl-color--deep-orange-A700{background-color:#dd2c00 !important}.mdl-color-text--brown{color:#795548 !important}.mdl-color--brown{background-color:#795548 !important}.mdl-color-text--brown-50{color:#efebe9 !important}.mdl-color--brown-50{background-color:#efebe9 !important}.mdl-color-text--brown-100{color:#d7ccc8 !important}.mdl-color--brown-100{background-color:#d7ccc8 !important}.mdl-color-text--brown-200{color:#bcaaa4 !important}.mdl-color--brown-200{background-color:#bcaaa4 !important}.mdl-color-text--brown-300{color:#a1887f !important}.mdl-color--brown-300{background-color:#a1887f !important}.mdl-color-text--brown-400{color:#8d6e63 !important}.mdl-color--brown-400{background-color:#8d6e63 !important}.mdl-color-text--brown-500{color:#795548 !important}.mdl-color--brown-500{background-color:#795548 !important}.mdl-color-text--brown-600{color:#6d4c41 !important}.mdl-color--brown-600{background-color:#6d4c41 !important}.mdl-color-text--brown-700{color:#5d4037 !important}.mdl-color--brown-700{background-color:#5d4037 !important}.mdl-color-text--brown-800{color:#4e342e !important}.mdl-color--brown-800{background-color:#4e342e !important}.mdl-color-text--brown-900{color:#3e2723 !important}.mdl-color--brown-900{background-color:#3e2723 !important}.mdl-color-text--grey{color:#9e9e9e !important}.mdl-color--grey{background-color:#9e9e9e !important}.mdl-color-text--grey-50{color:#fafafa !important}.mdl-color--grey-50{background-color:#fafafa !important}.mdl-color-text--grey-100{color:#f5f5f5 !important}.mdl-color--grey-100{background-color:#f5f5f5 !important}.mdl-color-text--grey-200{color:#eee !important}.mdl-color--grey-200{background-color:#eee !important}.mdl-color-text--grey-300{color:#e0e0e0 !important}.mdl-color--grey-300{background-color:#e0e0e0 !important}.mdl-color-text--grey-400{color:#bdbdbd !important}.mdl-color--grey-400{background-color:#bdbdbd !important}.mdl-color-text--grey-500{color:#9e9e9e !important}.mdl-color--grey-500{background-color:#9e9e9e !important}.mdl-color-text--grey-600{color:#757575 !important}.mdl-color--grey-600{background-color:#757575 !important}.mdl-color-text--grey-700{color:#616161 !important}.mdl-color--grey-700{background-color:#616161 !important}.mdl-color-text--grey-800{color:#424242 !important}.mdl-color--grey-800{background-color:#424242 !important}.mdl-color-text--grey-900{color:#212121 !important}.mdl-color--grey-900{background-color:#212121 !important}.mdl-color-text--blue-grey{color:#607d8b !important}.mdl-color--blue-grey{background-color:#607d8b !important}.mdl-color-text--blue-grey-50{color:#eceff1 !important}.mdl-color--blue-grey-50{background-color:#eceff1 !important}.mdl-color-text--blue-grey-100{color:#cfd8dc !important}.mdl-color--blue-grey-100{background-color:#cfd8dc !important}.mdl-color-text--blue-grey-200{color:#b0bec5 !important}.mdl-color--blue-grey-200{background-color:#b0bec5 !important}.mdl-color-text--blue-grey-300{color:#90a4ae !important}.mdl-color--blue-grey-300{background-color:#90a4ae !important}.mdl-color-text--blue-grey-400{color:#78909c !important}.mdl-color--blue-grey-400{background-color:#78909c !important}.mdl-color-text--blue-grey-500{color:#607d8b !important}.mdl-color--blue-grey-500{background-color:#607d8b !important}.mdl-color-text--blue-grey-600{color:#546e7a !important}.mdl-color--blue-grey-600{background-color:#546e7a !important}.mdl-color-text--blue-grey-700{color:#455a64 !important}.mdl-color--blue-grey-700{background-color:#455a64 !important}.mdl-color-text--blue-grey-800{color:#37474f !important}.mdl-color--blue-grey-800{background-color:#37474f !important}.mdl-color-text--blue-grey-900{color:#263238 !important}.mdl-color--blue-grey-900{background-color:#263238 !important}.mdl-color--black{background-color:#000 !important}.mdl-color-text--black{color:#000 !important}.mdl-color--white{background-color:#fff !important}.mdl-color-text--white{color:#fff !important}.mdl-color--primary{background-color:#3f51b5 !important}.mdl-color--primary-contrast{background-color:#fff !important}.mdl-color--primary-dark{background-color:#303f9f !important}.mdl-color--accent{background-color:#ff4081 !important}.mdl-color--accent-contrast{background-color:#fff !important}.mdl-color-text--primary{color:#3f51b5 !important}.mdl-color-text--primary-contrast{color:#fff !important}.mdl-color-text--primary-dark{color:#303f9f !important}.mdl-color-text--accent{color:#ff4081 !important}.mdl-color-text--accent-contrast{color:#fff !important}.mdl-ripple{background:#000;border-radius:50%;height:50px;left:0;opacity:0;pointer-events:none;position:absolute;top:0;-webkit-transform:translate(-50%,-50%);-ms-transform:translate(-50%,-50%);transform:translate(-50%,-50%);width:50px;overflow:hidden}.mdl-ripple.is-animating{-webkit-transition:-webkit-transform .3s cubic-bezier(0,0,.2,1),width .3s cubic-bezier(0,0,.2,1),height .3s cubic-bezier(0,0,.2,1),opacity .6s cubic-bezier(0,0,.2,1);transition:transform .3s cubic-bezier(0,0,.2,1),width .3s cubic-bezier(0,0,.2,1),height .3s cubic-bezier(0,0,.2,1),opacity .6s cubic-bezier(0,0,.2,1)}.mdl-ripple.is-visible{opacity:.3}.mdl-animation--default,.mdl-animation--fast-out-slow-in{-webkit-transition-timing-function:cubic-bezier(.4,0,.2,1);transition-timing-function:cubic-bezier(.4,0,.2,1)}.mdl-animation--linear-out-slow-in{-webkit-transition-timing-function:cubic-bezier(0,0,.2,1);transition-timing-function:cubic-bezier(0,0,.2,1)}.mdl-animation--fast-out-linear-in{-webkit-transition-timing-function:cubic-bezier(.4,0,1,1);transition-timing-function:cubic-bezier(.4,0,1,1)}.mdl-badge{position:relative;white-space:nowrap;margin-right:24px}.mdl-badge:not([data-badge]){margin-right:auto}.mdl-badge[data-badge]:after{content:attr(data-badge);display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row;-webkit-flex-wrap:wrap;-ms-flex-wrap:wrap;flex-wrap:wrap;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;-webkit-align-content:center;-ms-flex-line-pack:center;align-content:center;-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;position:absolute;top:-11px;right:-24px;font-family:\"Roboto\",\"Helvetica\",\"Arial\",sans-serif;font-weight:600;font-size:12px;width:22px;height:22px;border-radius:50%;background:#ff4081;color:#fff}.mdl-button .mdl-badge[data-badge]:after{top:-10px;right:-5px}.mdl-badge.mdl-badge--no-background[data-badge]:after{color:#ff4081;background:#fff;box-shadow:0 0 1px gray}.mdl-button{background:0 0;border:none;border-radius:2px;color:#000;position:relative;height:36px;min-width:64px;padding:0 16px;display:inline-block;font-family:\"Roboto\",\"Helvetica\",\"Arial\",sans-serif;font-size:14px;font-weight:500;text-transform:uppercase;letter-spacing:0;overflow:hidden;will-change:box-shadow,transform;-webkit-transition:box-shadow .2s cubic-bezier(.4,0,1,1),background-color .2s cubic-bezier(.4,0,.2,1),color .2s cubic-bezier(.4,0,.2,1);transition:box-shadow .2s cubic-bezier(.4,0,1,1),background-color .2s cubic-bezier(.4,0,.2,1),color .2s cubic-bezier(.4,0,.2,1);outline:none;cursor:pointer;text-decoration:none;text-align:center;line-height:36px;vertical-align:middle}.mdl-button::-moz-focus-inner{border:0}.mdl-button:hover{background-color:rgba(158,158,158,.2)}.mdl-button:focus:not(:active){background-color:rgba(0,0,0,.12)}.mdl-button:active{background-color:rgba(158,158,158,.4)}.mdl-button.mdl-button--colored{color:#3f51b5}.mdl-button.mdl-button--colored:focus:not(:active){background-color:rgba(0,0,0,.12)}input.mdl-button[type=\"submit\"]{-webkit-appearance:none}.mdl-button--raised{background:rgba(158,158,158,.2);box-shadow:0 2px 2px 0 rgba(0,0,0,.14),0 3px 1px -2px rgba(0,0,0,.2),0 1px 5px 0 rgba(0,0,0,.12)}.mdl-button--raised:active{box-shadow:0 4px 5px 0 rgba(0,0,0,.14),0 1px 10px 0 rgba(0,0,0,.12),0 2px 4px -1px rgba(0,0,0,.2);background-color:rgba(158,158,158,.4)}.mdl-button--raised:focus:not(:active){box-shadow:0 0 8px rgba(0,0,0,.18),0 8px 16px rgba(0,0,0,.36);background-color:rgba(158,158,158,.4)}.mdl-button--raised.mdl-button--colored{background:#3f51b5;color:#fff}.mdl-button--raised.mdl-button--colored:hover{background-color:#3f51b5}.mdl-button--raised.mdl-button--colored:active{background-color:#3f51b5}.mdl-button--raised.mdl-button--colored:focus:not(:active){background-color:#3f51b5}.mdl-button--raised.mdl-button--colored .mdl-ripple{background:#fff}.mdl-button--fab{border-radius:50%;font-size:24px;height:56px;margin:auto;min-width:56px;width:56px;padding:0;overflow:hidden;background:rgba(158,158,158,.2);box-shadow:0 1px 1.5px 0 rgba(0,0,0,.12),0 1px 1px 0 rgba(0,0,0,.24);position:relative;line-height:normal}.mdl-button--fab .material-icons{position:absolute;top:50%;left:50%;-webkit-transform:translate(-12px,-12px);-ms-transform:translate(-12px,-12px);transform:translate(-12px,-12px);line-height:24px;width:24px}.mdl-button--fab.mdl-button--mini-fab{height:40px;min-width:40px;width:40px}.mdl-button--fab .mdl-button__ripple-container{border-radius:50%;-webkit-mask-image:-webkit-radial-gradient(circle,#fff,#000)}.mdl-button--fab:active{box-shadow:0 4px 5px 0 rgba(0,0,0,.14),0 1px 10px 0 rgba(0,0,0,.12),0 2px 4px -1px rgba(0,0,0,.2);background-color:rgba(158,158,158,.4)}.mdl-button--fab:focus:not(:active){box-shadow:0 0 8px rgba(0,0,0,.18),0 8px 16px rgba(0,0,0,.36);background-color:rgba(158,158,158,.4)}.mdl-button--fab.mdl-button--colored{background:#ff4081;color:#fff}.mdl-button--fab.mdl-button--colored:hover{background-color:#ff4081}.mdl-button--fab.mdl-button--colored:focus:not(:active){background-color:#ff4081}.mdl-button--fab.mdl-button--colored:active{background-color:#ff4081}.mdl-button--fab.mdl-button--colored .mdl-ripple{background:#fff}.mdl-button--icon{border-radius:50%;font-size:24px;height:32px;margin-left:0;margin-right:0;min-width:32px;width:32px;padding:0;overflow:hidden;color:inherit;line-height:normal}.mdl-button--icon .material-icons{position:absolute;top:50%;left:50%;-webkit-transform:translate(-12px,-12px);-ms-transform:translate(-12px,-12px);transform:translate(-12px,-12px);line-height:24px;width:24px}.mdl-button--icon.mdl-button--mini-icon{height:24px;min-width:24px;width:24px}.mdl-button--icon.mdl-button--mini-icon .material-icons{top:0;left:0}.mdl-button--icon .mdl-button__ripple-container{border-radius:50%;-webkit-mask-image:-webkit-radial-gradient(circle,#fff,#000)}.mdl-button__ripple-container{display:block;height:100%;left:0;position:absolute;top:0;width:100%;z-index:0;overflow:hidden}.mdl-button[disabled] .mdl-button__ripple-container .mdl-ripple,.mdl-button.mdl-button--disabled .mdl-button__ripple-container .mdl-ripple{background-color:transparent}.mdl-button--primary.mdl-button--primary{color:#3f51b5}.mdl-button--primary.mdl-button--primary .mdl-ripple{background:#fff}.mdl-button--primary.mdl-button--primary.mdl-button--raised,.mdl-button--primary.mdl-button--primary.mdl-button--fab{color:#fff;background-color:#3f51b5}.mdl-button--accent.mdl-button--accent{color:#ff4081}.mdl-button--accent.mdl-button--accent .mdl-ripple{background:#fff}.mdl-button--accent.mdl-button--accent.mdl-button--raised,.mdl-button--accent.mdl-button--accent.mdl-button--fab{color:#fff;background-color:#ff4081}.mdl-button[disabled][disabled],.mdl-button.mdl-button--disabled.mdl-button--disabled{color:rgba(0,0,0,.26);cursor:default;background-color:transparent}.mdl-button--fab[disabled][disabled],.mdl-button--fab.mdl-button--disabled.mdl-button--disabled,.mdl-button--raised[disabled][disabled],.mdl-button--raised.mdl-button--disabled.mdl-button--disabled{background-color:rgba(0,0,0,.12);color:rgba(0,0,0,.26);box-shadow:0 2px 2px 0 rgba(0,0,0,.14),0 3px 1px -2px rgba(0,0,0,.2),0 1px 5px 0 rgba(0,0,0,.12)}.mdl-button--colored[disabled][disabled],.mdl-button--colored.mdl-button--disabled.mdl-button--disabled{color:rgba(0,0,0,.26)}.mdl-button .material-icons{vertical-align:middle}.mdl-card{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;font-size:16px;font-weight:400;min-height:200px;overflow:hidden;width:330px;z-index:1;position:relative;background:#fff;border-radius:2px;box-sizing:border-box}.mdl-card__media{background-color:#ff4081;background-repeat:repeat;background-position:50% 50%;background-size:cover;background-origin:padding-box;background-attachment:scroll;box-sizing:border-box}.mdl-card__title{-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;color:#000;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-pack:stretch;-webkit-justify-content:stretch;-ms-flex-pack:stretch;justify-content:stretch;line-height:normal;padding:16px;-webkit-perspective-origin:165px 56px;perspective-origin:165px 56px;-webkit-transform-origin:165px 56px;-ms-transform-origin:165px 56px;transform-origin:165px 56px;box-sizing:border-box}.mdl-card__title.mdl-card--border{border-bottom:1px solid rgba(0,0,0,.1)}.mdl-card__title-text{-webkit-align-self:flex-end;-ms-flex-item-align:end;align-self:flex-end;color:inherit;display:-webkit-flex;display:-ms-flexbox;display:flex;font-size:24px;font-weight:300;line-height:normal;overflow:hidden;-webkit-transform-origin:149px 48px;-ms-transform-origin:149px 48px;transform-origin:149px 48px;margin:0}.mdl-card__subtitle-text{font-size:14px;color:rgba(0,0,0,.54);margin:0}.mdl-card__supporting-text{color:rgba(0,0,0,.54);font-size:13px;line-height:18px;overflow:hidden;padding:16px;width:90%}.mdl-card__actions{font-size:16px;line-height:normal;width:100%;background-color:transparent;padding:8px;box-sizing:border-box}.mdl-card__actions.mdl-card--border{border-top:1px solid rgba(0,0,0,.1)}.mdl-card--expand{-webkit-box-flex:1;-webkit-flex-grow:1;-ms-flex-positive:1;flex-grow:1}.mdl-card__menu{position:absolute;right:16px;top:16px}.mdl-checkbox{position:relative;z-index:1;vertical-align:middle;display:inline-block;box-sizing:border-box;width:100%;height:24px;margin:0;padding:0}.mdl-checkbox.is-upgraded{padding-left:24px}.mdl-checkbox__input{line-height:24px}.mdl-checkbox.is-upgraded .mdl-checkbox__input{position:absolute;width:0;height:0;margin:0;padding:0;opacity:0;-ms-appearance:none;-moz-appearance:none;-webkit-appearance:none;appearance:none;border:none}.mdl-checkbox__box-outline{position:absolute;top:3px;left:0;display:inline-block;box-sizing:border-box;width:16px;height:16px;margin:0;cursor:pointer;overflow:hidden;border:2px solid rgba(0,0,0,.54);border-radius:2px;z-index:2}.mdl-checkbox.is-checked .mdl-checkbox__box-outline{border:2px solid #3f51b5}.mdl-checkbox.is-disabled .mdl-checkbox__box-outline{border:2px solid rgba(0,0,0,.26);cursor:auto}.mdl-checkbox__focus-helper{position:absolute;top:3px;left:0;display:inline-block;box-sizing:border-box;width:16px;height:16px;border-radius:50%;background-color:transparent}.mdl-checkbox.is-focused .mdl-checkbox__focus-helper{box-shadow:0 0 0 8px rgba(0,0,0,.1);background-color:rgba(0,0,0,.1)}.mdl-checkbox.is-focused.is-checked .mdl-checkbox__focus-helper{box-shadow:0 0 0 8px rgba(63,81,181,.26);background-color:rgba(63,81,181,.26)}.mdl-checkbox__tick-outline{position:absolute;top:0;left:0;height:100%;width:100%;-webkit-mask:url(\"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcKICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICB4bWxuczpjYz0iaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbnMjIgogICB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiCiAgIHhtbG5zOnN2Zz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgdmVyc2lvbj0iMS4xIgogICB2aWV3Qm94PSIwIDAgMSAxIgogICBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWluWU1pbiBtZWV0Ij4KICA8ZGVmcz4KICAgIDxjbGlwUGF0aCBpZD0iY2xpcCI+CiAgICAgIDxwYXRoCiAgICAgICAgIGQ9Ik0gMCwwIDAsMSAxLDEgMSwwIDAsMCB6IE0gMC44NTM0Mzc1LDAuMTY3MTg3NSAwLjk1OTY4NzUsMC4yNzMxMjUgMC40MjkzNzUsMC44MDM0Mzc1IDAuMzIzMTI1LDAuOTA5Njg3NSAwLjIxNzE4NzUsMC44MDM0Mzc1IDAuMDQwMzEyNSwwLjYyNjg3NSAwLjE0NjU2MjUsMC41MjA2MjUgMC4zMjMxMjUsMC42OTc1IDAuODUzNDM3NSwwLjE2NzE4NzUgeiIKICAgICAgICAgc3R5bGU9ImZpbGw6I2ZmZmZmZjtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZSIgLz4KICAgIDwvY2xpcFBhdGg+CiAgICA8bWFzayBpZD0ibWFzayIgbWFza1VuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgbWFza0NvbnRlbnRVbml0cz0ib2JqZWN0Qm91bmRpbmdCb3giPgogICAgICA8cGF0aAogICAgICAgICBkPSJNIDAsMCAwLDEgMSwxIDEsMCAwLDAgeiBNIDAuODUzNDM3NSwwLjE2NzE4NzUgMC45NTk2ODc1LDAuMjczMTI1IDAuNDI5Mzc1LDAuODAzNDM3NSAwLjMyMzEyNSwwLjkwOTY4NzUgMC4yMTcxODc1LDAuODAzNDM3NSAwLjA0MDMxMjUsMC42MjY4NzUgMC4xNDY1NjI1LDAuNTIwNjI1IDAuMzIzMTI1LDAuNjk3NSAwLjg1MzQzNzUsMC4xNjcxODc1IHoiCiAgICAgICAgIHN0eWxlPSJmaWxsOiNmZmZmZmY7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmUiIC8+CiAgICA8L21hc2s+CiAgPC9kZWZzPgogIDxyZWN0CiAgICAgd2lkdGg9IjEiCiAgICAgaGVpZ2h0PSIxIgogICAgIHg9IjAiCiAgICAgeT0iMCIKICAgICBjbGlwLXBhdGg9InVybCgjY2xpcCkiCiAgICAgc3R5bGU9ImZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZSIgLz4KPC9zdmc+Cg==\");mask:url(\"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcKICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICB4bWxuczpjYz0iaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbnMjIgogICB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiCiAgIHhtbG5zOnN2Zz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgdmVyc2lvbj0iMS4xIgogICB2aWV3Qm94PSIwIDAgMSAxIgogICBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWluWU1pbiBtZWV0Ij4KICA8ZGVmcz4KICAgIDxjbGlwUGF0aCBpZD0iY2xpcCI+CiAgICAgIDxwYXRoCiAgICAgICAgIGQ9Ik0gMCwwIDAsMSAxLDEgMSwwIDAsMCB6IE0gMC44NTM0Mzc1LDAuMTY3MTg3NSAwLjk1OTY4NzUsMC4yNzMxMjUgMC40MjkzNzUsMC44MDM0Mzc1IDAuMzIzMTI1LDAuOTA5Njg3NSAwLjIxNzE4NzUsMC44MDM0Mzc1IDAuMDQwMzEyNSwwLjYyNjg3NSAwLjE0NjU2MjUsMC41MjA2MjUgMC4zMjMxMjUsMC42OTc1IDAuODUzNDM3NSwwLjE2NzE4NzUgeiIKICAgICAgICAgc3R5bGU9ImZpbGw6I2ZmZmZmZjtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZSIgLz4KICAgIDwvY2xpcFBhdGg+CiAgICA8bWFzayBpZD0ibWFzayIgbWFza1VuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgbWFza0NvbnRlbnRVbml0cz0ib2JqZWN0Qm91bmRpbmdCb3giPgogICAgICA8cGF0aAogICAgICAgICBkPSJNIDAsMCAwLDEgMSwxIDEsMCAwLDAgeiBNIDAuODUzNDM3NSwwLjE2NzE4NzUgMC45NTk2ODc1LDAuMjczMTI1IDAuNDI5Mzc1LDAuODAzNDM3NSAwLjMyMzEyNSwwLjkwOTY4NzUgMC4yMTcxODc1LDAuODAzNDM3NSAwLjA0MDMxMjUsMC42MjY4NzUgMC4xNDY1NjI1LDAuNTIwNjI1IDAuMzIzMTI1LDAuNjk3NSAwLjg1MzQzNzUsMC4xNjcxODc1IHoiCiAgICAgICAgIHN0eWxlPSJmaWxsOiNmZmZmZmY7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmUiIC8+CiAgICA8L21hc2s+CiAgPC9kZWZzPgogIDxyZWN0CiAgICAgd2lkdGg9IjEiCiAgICAgaGVpZ2h0PSIxIgogICAgIHg9IjAiCiAgICAgeT0iMCIKICAgICBjbGlwLXBhdGg9InVybCgjY2xpcCkiCiAgICAgc3R5bGU9ImZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZSIgLz4KPC9zdmc+Cg==\");background:0 0;-webkit-transition-duration:.28s;transition-duration:.28s;-webkit-transition-timing-function:cubic-bezier(.4,0,.2,1);transition-timing-function:cubic-bezier(.4,0,.2,1);-webkit-transition-property:background;transition-property:background}.mdl-checkbox.is-checked .mdl-checkbox__tick-outline{background:#3f51b5 url(\"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcKICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICB4bWxuczpjYz0iaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbnMjIgogICB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiCiAgIHhtbG5zOnN2Zz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgdmVyc2lvbj0iMS4xIgogICB2aWV3Qm94PSIwIDAgMSAxIgogICBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWluWU1pbiBtZWV0Ij4KICA8cGF0aAogICAgIGQ9Ik0gMC4wNDAzODA1OSwwLjYyNjc3NjcgMC4xNDY0NDY2MSwwLjUyMDcxMDY4IDAuNDI5Mjg5MzIsMC44MDM1NTMzOSAwLjMyMzIyMzMsMC45MDk2MTk0MSB6IE0gMC4yMTcxNTcyOSwwLjgwMzU1MzM5IDAuODUzNTUzMzksMC4xNjcxNTcyOSAwLjk1OTYxOTQxLDAuMjczMjIzMyAwLjMyMzIyMzMsMC45MDk2MTk0MSB6IgogICAgIGlkPSJyZWN0Mzc4MCIKICAgICBzdHlsZT0iZmlsbDojZmZmZmZmO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lIiAvPgo8L3N2Zz4K\")}.mdl-checkbox.is-checked.is-disabled .mdl-checkbox__tick-outline{background:rgba(0,0,0,.26)url(\"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcKICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICB4bWxuczpjYz0iaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbnMjIgogICB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiCiAgIHhtbG5zOnN2Zz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgdmVyc2lvbj0iMS4xIgogICB2aWV3Qm94PSIwIDAgMSAxIgogICBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWluWU1pbiBtZWV0Ij4KICA8cGF0aAogICAgIGQ9Ik0gMC4wNDAzODA1OSwwLjYyNjc3NjcgMC4xNDY0NDY2MSwwLjUyMDcxMDY4IDAuNDI5Mjg5MzIsMC44MDM1NTMzOSAwLjMyMzIyMzMsMC45MDk2MTk0MSB6IE0gMC4yMTcxNTcyOSwwLjgwMzU1MzM5IDAuODUzNTUzMzksMC4xNjcxNTcyOSAwLjk1OTYxOTQxLDAuMjczMjIzMyAwLjMyMzIyMzMsMC45MDk2MTk0MSB6IgogICAgIGlkPSJyZWN0Mzc4MCIKICAgICBzdHlsZT0iZmlsbDojZmZmZmZmO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lIiAvPgo8L3N2Zz4K\")}.mdl-checkbox__label{position:relative;cursor:pointer;font-size:16px;line-height:24px;margin:0}.mdl-checkbox.is-disabled .mdl-checkbox__label{color:rgba(0,0,0,.26);cursor:auto}.mdl-checkbox__ripple-container{position:absolute;z-index:2;top:-6px;left:-10px;box-sizing:border-box;width:36px;height:36px;border-radius:50%;cursor:pointer;overflow:hidden;-webkit-mask-image:-webkit-radial-gradient(circle,#fff,#000)}.mdl-checkbox__ripple-container .mdl-ripple{background:#3f51b5}.mdl-checkbox.is-disabled .mdl-checkbox__ripple-container{cursor:auto}.mdl-checkbox.is-disabled .mdl-checkbox__ripple-container .mdl-ripple{background:0 0}.mdl-data-table{position:relative;border:1px solid rgba(0,0,0,.12);border-collapse:collapse;white-space:nowrap;font-size:13px;background-color:#fff}.mdl-data-table thead{padding-bottom:3px}.mdl-data-table thead .mdl-data-table__select{margin-top:0}.mdl-data-table tbody tr{position:relative;height:48px;-webkit-transition-duration:.28s;transition-duration:.28s;-webkit-transition-timing-function:cubic-bezier(.4,0,.2,1);transition-timing-function:cubic-bezier(.4,0,.2,1);-webkit-transition-property:background-color;transition-property:background-color}.mdl-data-table tbody tr.is-selected{background-color:#e0e0e0}.mdl-data-table tbody tr:hover{background-color:#eee}.mdl-data-table td{text-align:right}.mdl-data-table th{padding:0 18px;text-align:right}.mdl-data-table td:first-of-type,.mdl-data-table th:first-of-type{padding-left:24px}.mdl-data-table td:last-of-type,.mdl-data-table th:last-of-type{padding-right:24px}.mdl-data-table td{position:relative;vertical-align:top;height:48px;border-top:1px solid rgba(0,0,0,.12);border-bottom:1px solid rgba(0,0,0,.12);padding:12px 18px 0;box-sizing:border-box}.mdl-data-table td .mdl-data-table__select{vertical-align:top;position:absolute;left:24px}.mdl-data-table th{position:relative;vertical-align:bottom;text-overflow:ellipsis;font-weight:700;line-height:24px;letter-spacing:0;height:48px;font-size:12px;color:rgba(0,0,0,.54);padding-bottom:8px;box-sizing:border-box}.mdl-data-table th .mdl-data-table__select{position:absolute;bottom:8px;left:24px}.mdl-data-table__select{width:16px}.mdl-data-table__cell--non-numeric.mdl-data-table__cell--non-numeric{text-align:left}.mdl-mega-footer{padding:16px 40px;color:#9e9e9e;background-color:#424242}.mdl-mega-footer--top-section:after,.mdl-mega-footer--middle-section:after,.mdl-mega-footer--bottom-section:after,.mdl-mega-footer__top-section:after,.mdl-mega-footer__middle-section:after,.mdl-mega-footer__bottom-section:after{content:'';display:block;clear:both}.mdl-mega-footer--left-section,.mdl-mega-footer__left-section,.mdl-mega-footer--right-section,.mdl-mega-footer__right-section{margin-bottom:16px}.mdl-mega-footer--right-section a,.mdl-mega-footer__right-section a{display:block;margin-bottom:16px;color:inherit;text-decoration:none}@media screen and (min-width:760px){.mdl-mega-footer--left-section,.mdl-mega-footer__left-section{float:left}.mdl-mega-footer--right-section,.mdl-mega-footer__right-section{float:right}.mdl-mega-footer--right-section a,.mdl-mega-footer__right-section a{display:inline-block;margin-left:16px;line-height:36px;vertical-align:middle}}.mdl-mega-footer--social-btn,.mdl-mega-footer__social-btn{width:36px;height:36px;padding:0;margin:0;background-color:#9e9e9e;border:none}.mdl-mega-footer--drop-down-section,.mdl-mega-footer__drop-down-section{display:block;position:relative}@media screen and (min-width:760px){.mdl-mega-footer--drop-down-section,.mdl-mega-footer__drop-down-section{width:33%}.mdl-mega-footer--drop-down-section:nth-child(1),.mdl-mega-footer--drop-down-section:nth-child(2),.mdl-mega-footer__drop-down-section:nth-child(1),.mdl-mega-footer__drop-down-section:nth-child(2){float:left}.mdl-mega-footer--drop-down-section:nth-child(3),.mdl-mega-footer__drop-down-section:nth-child(3){float:right}.mdl-mega-footer--drop-down-section:nth-child(3):after,.mdl-mega-footer__drop-down-section:nth-child(3):after{clear:right}.mdl-mega-footer--drop-down-section:nth-child(4),.mdl-mega-footer__drop-down-section:nth-child(4){clear:right;float:right}.mdl-mega-footer--middle-section:after,.mdl-mega-footer__middle-section:after{content:'';display:block;clear:both}.mdl-mega-footer--bottom-section,.mdl-mega-footer__bottom-section{padding-top:0}}@media screen and (min-width:1024px){.mdl-mega-footer--drop-down-section,.mdl-mega-footer--drop-down-section:nth-child(3),.mdl-mega-footer--drop-down-section:nth-child(4),.mdl-mega-footer__drop-down-section,.mdl-mega-footer__drop-down-section:nth-child(3),.mdl-mega-footer__drop-down-section:nth-child(4){width:24%;float:left}}.mdl-mega-footer--heading-checkbox,.mdl-mega-footer__heading-checkbox{position:absolute;width:100%;height:55.8px;padding:32px;margin:-16px 0 0;cursor:pointer;z-index:1;opacity:0}.mdl-mega-footer--heading-checkbox+.mdl-mega-footer--heading:after,.mdl-mega-footer--heading-checkbox+.mdl-mega-footer__heading:after,.mdl-mega-footer__heading-checkbox+.mdl-mega-footer--heading:after,.mdl-mega-footer__heading-checkbox+.mdl-mega-footer__heading:after{font-family:'Material Icons';content:'\\E5CE'}.mdl-mega-footer--heading-checkbox:checked~.mdl-mega-footer--link-list,.mdl-mega-footer--heading-checkbox:checked~.mdl-mega-footer__link-list,.mdl-mega-footer--heading-checkbox:checked+.mdl-mega-footer--heading+.mdl-mega-footer--link-list,.mdl-mega-footer--heading-checkbox:checked+.mdl-mega-footer__heading+.mdl-mega-footer__link-list,.mdl-mega-footer__heading-checkbox:checked~.mdl-mega-footer--link-list,.mdl-mega-footer__heading-checkbox:checked~.mdl-mega-footer__link-list,.mdl-mega-footer__heading-checkbox:checked+.mdl-mega-footer--heading+.mdl-mega-footer--link-list,.mdl-mega-footer__heading-checkbox:checked+.mdl-mega-footer__heading+.mdl-mega-footer__link-list{display:none}.mdl-mega-footer--heading-checkbox:checked+.mdl-mega-footer--heading:after,.mdl-mega-footer--heading-checkbox:checked+.mdl-mega-footer__heading:after,.mdl-mega-footer__heading-checkbox:checked+.mdl-mega-footer--heading:after,.mdl-mega-footer__heading-checkbox:checked+.mdl-mega-footer__heading:after{font-family:'Material Icons';content:'\\E5CF'}.mdl-mega-footer--heading,.mdl-mega-footer__heading{position:relative;width:100%;padding-right:39.8px;margin-bottom:16px;box-sizing:border-box;font-size:14px;line-height:23.8px;font-weight:500;white-space:nowrap;text-overflow:ellipsis;overflow:hidden;color:#e0e0e0}.mdl-mega-footer--heading:after,.mdl-mega-footer__heading:after{content:'';position:absolute;top:0;right:0;display:block;width:23.8px;height:23.8px;background-size:cover}.mdl-mega-footer--link-list,.mdl-mega-footer__link-list{list-style:none;padding:0;margin:0 0 32px}.mdl-mega-footer--link-list:after,.mdl-mega-footer__link-list:after{clear:both;display:block;content:''}.mdl-mega-footer--link-list li,.mdl-mega-footer__link-list li{font-size:14px;font-weight:400;letter-spacing:0;line-height:20px}.mdl-mega-footer--link-list a,.mdl-mega-footer__link-list a{color:inherit;text-decoration:none;white-space:nowrap}@media screen and (min-width:760px){.mdl-mega-footer--heading-checkbox,.mdl-mega-footer__heading-checkbox{display:none}.mdl-mega-footer--heading-checkbox+.mdl-mega-footer--heading:after,.mdl-mega-footer--heading-checkbox+.mdl-mega-footer__heading:after,.mdl-mega-footer__heading-checkbox+.mdl-mega-footer--heading:after,.mdl-mega-footer__heading-checkbox+.mdl-mega-footer__heading:after{background-image:none}.mdl-mega-footer--heading-checkbox:checked~.mdl-mega-footer--link-list,.mdl-mega-footer--heading-checkbox:checked~.mdl-mega-footer__link-list,.mdl-mega-footer--heading-checkbox:checked+.mdl-mega-footer__heading+.mdl-mega-footer__link-list,.mdl-mega-footer--heading-checkbox:checked+.mdl-mega-footer--heading+.mdl-mega-footer--link-list,.mdl-mega-footer__heading-checkbox:checked~.mdl-mega-footer--link-list,.mdl-mega-footer__heading-checkbox:checked~.mdl-mega-footer__link-list,.mdl-mega-footer__heading-checkbox:checked+.mdl-mega-footer__heading+.mdl-mega-footer__link-list,.mdl-mega-footer__heading-checkbox:checked+.mdl-mega-footer--heading+.mdl-mega-footer--link-list{display:block}.mdl-mega-footer--heading-checkbox:checked+.mdl-mega-footer--heading:after,.mdl-mega-footer--heading-checkbox:checked+.mdl-mega-footer__heading:after,.mdl-mega-footer__heading-checkbox:checked+.mdl-mega-footer--heading:after,.mdl-mega-footer__heading-checkbox:checked+.mdl-mega-footer__heading:after{content:''}}.mdl-mega-footer--bottom-section,.mdl-mega-footer__bottom-section{padding-top:16px;margin-bottom:16px}.mdl-logo{margin-bottom:16px;color:#fff}.mdl-mega-footer--bottom-section .mdl-mega-footer--link-list li,.mdl-mega-footer__bottom-section .mdl-mega-footer__link-list li{float:left;margin-bottom:0;margin-right:16px}@media screen and (min-width:760px){.mdl-logo{float:left;margin-bottom:0;margin-right:16px}}.mdl-mini-footer{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-flow:row wrap;-ms-flex-flow:row wrap;flex-flow:row wrap;-webkit-box-pack:justify;-webkit-justify-content:space-between;-ms-flex-pack:justify;justify-content:space-between;padding:32px 16px;color:#9e9e9e;background-color:#424242}.mdl-mini-footer:after{content:'';display:block}.mdl-mini-footer .mdl-logo{line-height:36px}.mdl-mini-footer--link-list,.mdl-mini-footer__link-list{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-flow:row nowrap;-ms-flex-flow:row nowrap;flex-flow:row nowrap;list-style:none;margin:0;padding:0}.mdl-mini-footer--link-list li,.mdl-mini-footer__link-list li{margin-bottom:0;margin-right:16px}@media screen and (min-width:760px){.mdl-mini-footer--link-list li,.mdl-mini-footer__link-list li{line-height:36px}}.mdl-mini-footer--link-list a,.mdl-mini-footer__link-list a{color:inherit;text-decoration:none;white-space:nowrap}.mdl-mini-footer--left-section,.mdl-mini-footer__left-section{display:inline-block;-webkit-box-ordinal-group:1;-webkit-order:0;-ms-flex-order:0;order:0}.mdl-mini-footer--right-section,.mdl-mini-footer__right-section{display:inline-block;-webkit-box-ordinal-group:2;-webkit-order:1;-ms-flex-order:1;order:1}.mdl-mini-footer--social-btn,.mdl-mini-footer__social-btn{width:36px;height:36px;padding:0;margin:0;background-color:#9e9e9e;border:none}.mdl-icon-toggle{position:relative;z-index:1;vertical-align:middle;display:inline-block;height:32px;margin:0;padding:0}.mdl-icon-toggle__input{line-height:32px}.mdl-icon-toggle.is-upgraded .mdl-icon-toggle__input{position:absolute;width:0;height:0;margin:0;padding:0;opacity:0;-ms-appearance:none;-moz-appearance:none;-webkit-appearance:none;appearance:none;border:none}.mdl-icon-toggle__label{display:inline-block;position:relative;cursor:pointer;height:32px;width:32px;min-width:32px;color:#616161;border-radius:50%;padding:0;margin-left:0;margin-right:0;text-align:center;background-color:transparent;will-change:background-color;-webkit-transition:background-color .2s cubic-bezier(.4,0,.2,1),color .2s cubic-bezier(.4,0,.2,1);transition:background-color .2s cubic-bezier(.4,0,.2,1),color .2s cubic-bezier(.4,0,.2,1)}.mdl-icon-toggle__label.material-icons{line-height:32px;font-size:24px}.mdl-icon-toggle.is-checked .mdl-icon-toggle__label{color:#3f51b5}.mdl-icon-toggle.is-disabled .mdl-icon-toggle__label{color:rgba(0,0,0,.26);cursor:auto;-webkit-transition:none;transition:none}.mdl-icon-toggle.is-focused .mdl-icon-toggle__label{background-color:rgba(0,0,0,.12)}.mdl-icon-toggle.is-focused.is-checked .mdl-icon-toggle__label{background-color:rgba(63,81,181,.26)}.mdl-icon-toggle__ripple-container{position:absolute;z-index:2;top:-2px;left:-2px;box-sizing:border-box;width:36px;height:36px;border-radius:50%;cursor:pointer;overflow:hidden;-webkit-mask-image:-webkit-radial-gradient(circle,#fff,#000)}.mdl-icon-toggle__ripple-container .mdl-ripple{background:#616161}.mdl-icon-toggle.is-disabled .mdl-icon-toggle__ripple-container{cursor:auto}.mdl-icon-toggle.is-disabled .mdl-icon-toggle__ripple-container .mdl-ripple{background:0 0}.mdl-menu__container{display:block;margin:0;padding:0;border:none;position:absolute;overflow:visible;height:0;width:0;visibility:hidden;z-index:-1}.mdl-menu__container.is-visible,.mdl-menu__container.is-animating{z-index:999;visibility:visible}.mdl-menu__outline{display:block;background:#fff;margin:0;padding:0;border:none;border-radius:2px;position:absolute;top:0;left:0;overflow:hidden;opacity:0;-webkit-transform:scale(0);-ms-transform:scale(0);transform:scale(0);-webkit-transform-origin:0 0;-ms-transform-origin:0 0;transform-origin:0 0;box-shadow:0 2px 2px 0 rgba(0,0,0,.14),0 3px 1px -2px rgba(0,0,0,.2),0 1px 5px 0 rgba(0,0,0,.12);will-change:transform;-webkit-transition:-webkit-transform .3s cubic-bezier(.4,0,.2,1),opacity .2s cubic-bezier(.4,0,.2,1);transition:transform .3s cubic-bezier(.4,0,.2,1),opacity .2s cubic-bezier(.4,0,.2,1);z-index:-1}.mdl-menu__container.is-visible .mdl-menu__outline{opacity:1;-webkit-transform:scale(1);-ms-transform:scale(1);transform:scale(1);z-index:999}.mdl-menu__outline.mdl-menu--bottom-right{-webkit-transform-origin:100% 0;-ms-transform-origin:100% 0;transform-origin:100% 0}.mdl-menu__outline.mdl-menu--top-left{-webkit-transform-origin:0 100%;-ms-transform-origin:0 100%;transform-origin:0 100%}.mdl-menu__outline.mdl-menu--top-right{-webkit-transform-origin:100% 100%;-ms-transform-origin:100% 100%;transform-origin:100% 100%}.mdl-menu{position:absolute;list-style:none;top:0;left:0;height:auto;width:auto;min-width:124px;padding:8px 0;margin:0;opacity:0;clip:rect(0 0 0 0);z-index:-1}.mdl-menu__container.is-visible .mdl-menu{opacity:1;z-index:999}.mdl-menu.is-animating{-webkit-transition:opacity .2s cubic-bezier(.4,0,.2,1),clip .3s cubic-bezier(.4,0,.2,1);transition:opacity .2s cubic-bezier(.4,0,.2,1),clip .3s cubic-bezier(.4,0,.2,1)}.mdl-menu.mdl-menu--bottom-right{left:auto;right:0}.mdl-menu.mdl-menu--top-left{top:auto;bottom:0}.mdl-menu.mdl-menu--top-right{top:auto;left:auto;bottom:0;right:0}.mdl-menu.mdl-menu--unaligned{top:auto;left:auto}.mdl-menu__item{display:block;border:none;color:rgba(0,0,0,.87);background-color:transparent;text-align:left;margin:0;padding:0 16px;outline-color:#bdbdbd;position:relative;overflow:hidden;font-size:14px;font-weight:400;letter-spacing:0;text-decoration:none;cursor:pointer;height:48px;line-height:48px;white-space:nowrap;opacity:0;-webkit-transition:opacity .2s cubic-bezier(.4,0,.2,1);transition:opacity .2s cubic-bezier(.4,0,.2,1);-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.mdl-menu__container.is-visible .mdl-menu__item{opacity:1}.mdl-menu__item::-moz-focus-inner{border:0}.mdl-menu__item[disabled]{color:#bdbdbd;background-color:transparent;cursor:auto}.mdl-menu__item[disabled]:hover{background-color:transparent}.mdl-menu__item[disabled]:focus{background-color:transparent}.mdl-menu__item[disabled] .mdl-ripple{background:0 0}.mdl-menu__item:hover{background-color:#eee}.mdl-menu__item:focus{outline:none;background-color:#eee}.mdl-menu__item:active{background-color:#e0e0e0}.mdl-menu__item--ripple-container{display:block;height:100%;left:0;position:absolute;top:0;width:100%;z-index:0;overflow:hidden}.mdl-progress{display:block;position:relative;height:4px;width:500px}.mdl-progress>.bar{display:block;position:absolute;top:0;bottom:0;width:0%;-webkit-transition:width .2s cubic-bezier(.4,0,.2,1);transition:width .2s cubic-bezier(.4,0,.2,1)}.mdl-progress>.progressbar{background-color:#3f51b5;z-index:1;left:0}.mdl-progress>.bufferbar{background-image:-webkit-linear-gradient(left,rgba(255,255,255,.7),rgba(255,255,255,.7)),-webkit-linear-gradient(left,#3f51b5 ,#3f51b5);background-image:linear-gradient(to right,rgba(255,255,255,.7),rgba(255,255,255,.7)),linear-gradient(to right,#3f51b5 ,#3f51b5);z-index:0;left:0}.mdl-progress>.auxbar{right:0}@supports (-webkit-appearance:none){.mdl-progress:not(.mdl-progress__indeterminate):not(.mdl-progress__indeterminate)>.auxbar{background-image:-webkit-linear-gradient(left,rgba(255,255,255,.7),rgba(255,255,255,.7)),-webkit-linear-gradient(left,#3f51b5 ,#3f51b5);background-image:linear-gradient(to right,rgba(255,255,255,.7),rgba(255,255,255,.7)),linear-gradient(to right,#3f51b5 ,#3f51b5);-webkit-mask:url(\"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIj8+Cjxzdmcgd2lkdGg9IjEyIiBoZWlnaHQ9IjQiIHZpZXdQb3J0PSIwIDAgMTIgNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgogIDxlbGxpcHNlIGN4PSIyIiBjeT0iMiIgcng9IjIiIHJ5PSIyIj4KICAgIDxhbmltYXRlIGF0dHJpYnV0ZU5hbWU9ImN4IiBmcm9tPSIyIiB0bz0iLTEwIiBkdXI9IjAuNnMiIHJlcGVhdENvdW50PSJpbmRlZmluaXRlIiAvPgogIDwvZWxsaXBzZT4KICA8ZWxsaXBzZSBjeD0iMTQiIGN5PSIyIiByeD0iMiIgcnk9IjIiIGNsYXNzPSJsb2FkZXIiPgogICAgPGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iY3giIGZyb209IjE0IiB0bz0iMiIgZHVyPSIwLjZzIiByZXBlYXRDb3VudD0iaW5kZWZpbml0ZSIgLz4KICA8L2VsbGlwc2U+Cjwvc3ZnPgo=\");mask:url(\"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIj8+Cjxzdmcgd2lkdGg9IjEyIiBoZWlnaHQ9IjQiIHZpZXdQb3J0PSIwIDAgMTIgNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgogIDxlbGxpcHNlIGN4PSIyIiBjeT0iMiIgcng9IjIiIHJ5PSIyIj4KICAgIDxhbmltYXRlIGF0dHJpYnV0ZU5hbWU9ImN4IiBmcm9tPSIyIiB0bz0iLTEwIiBkdXI9IjAuNnMiIHJlcGVhdENvdW50PSJpbmRlZmluaXRlIiAvPgogIDwvZWxsaXBzZT4KICA8ZWxsaXBzZSBjeD0iMTQiIGN5PSIyIiByeD0iMiIgcnk9IjIiIGNsYXNzPSJsb2FkZXIiPgogICAgPGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iY3giIGZyb209IjE0IiB0bz0iMiIgZHVyPSIwLjZzIiByZXBlYXRDb3VudD0iaW5kZWZpbml0ZSIgLz4KICA8L2VsbGlwc2U+Cjwvc3ZnPgo=\")}}.mdl-progress:not(.mdl-progress__indeterminate)>.auxbar{background-image:-webkit-linear-gradient(left,rgba(255,255,255,.9),rgba(255,255,255,.9)),-webkit-linear-gradient(left,#3f51b5 ,#3f51b5);background-image:linear-gradient(to right,rgba(255,255,255,.9),rgba(255,255,255,.9)),linear-gradient(to right,#3f51b5 ,#3f51b5)}.mdl-progress.mdl-progress__indeterminate>.bar1{-webkit-animation-name:indeterminate1;animation-name:indeterminate1}.mdl-progress.mdl-progress__indeterminate>.bar1,.mdl-progress.mdl-progress__indeterminate>.bar3{background-color:#3f51b5;-webkit-animation-duration:2s;animation-duration:2s;-webkit-animation-iteration-count:infinite;animation-iteration-count:infinite;-webkit-animation-timing-function:linear;animation-timing-function:linear}.mdl-progress.mdl-progress__indeterminate>.bar3{background-image:none;-webkit-animation-name:indeterminate2;animation-name:indeterminate2}@-webkit-keyframes indeterminate1{0%{left:0%;width:0%}50%{left:25%;width:75%}75%{left:100%;width:0%}}@keyframes indeterminate1{0%{left:0%;width:0%}50%{left:25%;width:75%}75%{left:100%;width:0%}}@-webkit-keyframes indeterminate2{0%,50%{left:0%;width:0%}75%{left:0%;width:25%}100%{left:100%;width:0%}}@keyframes indeterminate2{0%,50%{left:0%;width:0%}75%{left:0%;width:25%}100%{left:100%;width:0%}}.mdl-navigation{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-wrap:nowrap;-ms-flex-wrap:nowrap;flex-wrap:nowrap;box-sizing:border-box}.mdl-navigation__link{color:#424242;text-decoration:none;font-weight:500;font-size:13px;margin:0}.mdl-layout{width:100%;height:100%;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;overflow-y:auto;overflow-x:hidden;position:relative;-webkit-overflow-scrolling:touch}.mdl-layout.is-small-screen .mdl-layout--large-screen-only{display:none}.mdl-layout:not(.is-small-screen) .mdl-layout--small-screen-only{display:none}.mdl-layout__container{position:absolute;width:100%;height:100%}.mdl-layout__title,.mdl-layout-title{display:block;position:relative;font-family:\"Roboto\",\"Helvetica\",\"Arial\",sans-serif;font-size:20px;line-height:1;letter-spacing:.02em;font-weight:400;box-sizing:border-box}.mdl-layout-spacer{-webkit-box-flex:1;-webkit-flex-grow:1;-ms-flex-positive:1;flex-grow:1}.mdl-layout__drawer{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;-webkit-flex-wrap:nowrap;-ms-flex-wrap:nowrap;flex-wrap:nowrap;width:240px;height:100%;max-height:100%;position:absolute;top:0;left:0;box-shadow:0 2px 2px 0 rgba(0,0,0,.14),0 3px 1px -2px rgba(0,0,0,.2),0 1px 5px 0 rgba(0,0,0,.12);box-sizing:border-box;border-right:1px solid #e0e0e0;background:#fafafa;-webkit-transform:translateX(-250px);-ms-transform:translateX(-250px);transform:translateX(-250px);-webkit-transform-style:preserve-3d;transform-style:preserve-3d;will-change:transform;-webkit-transition-duration:.2s;transition-duration:.2s;-webkit-transition-timing-function:cubic-bezier(.4,0,.2,1);transition-timing-function:cubic-bezier(.4,0,.2,1);-webkit-transition-property:-webkit-transform;transition-property:transform;color:#424242;overflow:visible;overflow-y:auto;z-index:5}.mdl-layout__drawer.is-visible{-webkit-transform:translateX(0);-ms-transform:translateX(0);transform:translateX(0)}.mdl-layout__drawer.is-visible~.mdl-layout__content.mdl-layout__content{overflow:hidden}.mdl-layout__drawer>*{-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0}.mdl-layout__drawer>.mdl-layout__title,.mdl-layout__drawer>.mdl-layout-title{line-height:64px;padding-left:40px}@media screen and (max-width:1024px){.mdl-layout__drawer>.mdl-layout__title,.mdl-layout__drawer>.mdl-layout-title{line-height:56px;padding-left:16px}}.mdl-layout__drawer .mdl-navigation{-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;-webkit-box-align:stretch;-webkit-align-items:stretch;-ms-flex-align:stretch;align-items:stretch;padding-top:16px}.mdl-layout__drawer .mdl-navigation .mdl-navigation__link{display:block;-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0;padding:16px 40px;margin:0;color:#757575}@media screen and (max-width:1024px){.mdl-layout__drawer .mdl-navigation .mdl-navigation__link{padding:16px}}.mdl-layout__drawer .mdl-navigation .mdl-navigation__link:hover{background-color:#e0e0e0}.mdl-layout__drawer .mdl-navigation .mdl-navigation__link--current{background-color:#000;color:#e0e0e0}@media screen and (min-width:1025px){.mdl-layout--fixed-drawer>.mdl-layout__drawer{-webkit-transform:translateX(0);-ms-transform:translateX(0);transform:translateX(0)}}.mdl-layout__drawer-button{display:block;position:absolute;height:48px;width:48px;border:0;-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0;overflow:hidden;text-align:center;cursor:pointer;font-size:26px;line-height:50px;font-family:Helvetica,Arial,sans-serif;margin:10px 12px;top:0;left:0;color:#fff;z-index:4}.mdl-layout__header .mdl-layout__drawer-button{position:absolute;color:#fff;background-color:inherit}@media screen and (max-width:1024px){.mdl-layout__header .mdl-layout__drawer-button{margin:4px}}@media screen and (max-width:1024px){.mdl-layout__drawer-button{margin:4px;color:rgba(0,0,0,.5)}}@media screen and (min-width:1025px){.mdl-layout--fixed-drawer>.mdl-layout__drawer-button{display:none}}.mdl-layout__header{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;-webkit-flex-wrap:nowrap;-ms-flex-wrap:nowrap;flex-wrap:nowrap;-webkit-box-pack:start;-webkit-justify-content:flex-start;-ms-flex-pack:start;justify-content:flex-start;box-sizing:border-box;-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0;width:100%;margin:0;padding:0;border:none;min-height:64px;max-height:1000px;z-index:3;background-color:#3f51b5;color:#fff;box-shadow:0 2px 2px 0 rgba(0,0,0,.14),0 3px 1px -2px rgba(0,0,0,.2),0 1px 5px 0 rgba(0,0,0,.12);-webkit-transition-duration:.2s;transition-duration:.2s;-webkit-transition-timing-function:cubic-bezier(.4,0,.2,1);transition-timing-function:cubic-bezier(.4,0,.2,1);-webkit-transition-property:max-height,box-shadow;transition-property:max-height,box-shadow}@media screen and (max-width:1024px){.mdl-layout__header{min-height:56px}}.mdl-layout--fixed-drawer.is-upgraded:not(.is-small-screen)>.mdl-layout__header{margin-left:240px;width:calc(100% - 240px)}@media screen and (min-width:1025px){.mdl-layout--fixed-drawer>.mdl-layout__header .mdl-layout__header-row{padding-left:40px}}.mdl-layout__header>.mdl-layout-icon{position:absolute;left:40px;top:16px;height:32px;width:32px;overflow:hidden;z-index:3;display:block}@media screen and (max-width:1024px){.mdl-layout__header>.mdl-layout-icon{left:16px;top:12px}}.mdl-layout.has-drawer .mdl-layout__header>.mdl-layout-icon{display:none}.mdl-layout__header.is-compact{max-height:64px}@media screen and (max-width:1024px){.mdl-layout__header.is-compact{max-height:56px}}.mdl-layout__header.is-compact.has-tabs{height:112px}@media screen and (max-width:1024px){.mdl-layout__header.is-compact.has-tabs{min-height:104px}}@media screen and (max-width:1024px){.mdl-layout__header{display:none}.mdl-layout--fixed-header>.mdl-layout__header{display:-webkit-flex;display:-ms-flexbox;display:flex}}.mdl-layout__header--transparent.mdl-layout__header--transparent{background-color:transparent;box-shadow:none}.mdl-layout__header--seamed,.mdl-layout__header--scroll{box-shadow:none}.mdl-layout__header--waterfall{box-shadow:none;overflow:hidden}.mdl-layout__header--waterfall.is-casting-shadow{box-shadow:0 2px 2px 0 rgba(0,0,0,.14),0 3px 1px -2px rgba(0,0,0,.2),0 1px 5px 0 rgba(0,0,0,.12)}.mdl-layout__header-row{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row;-webkit-flex-wrap:nowrap;-ms-flex-wrap:nowrap;flex-wrap:nowrap;-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0;box-sizing:border-box;-webkit-align-self:stretch;-ms-flex-item-align:stretch;align-self:stretch;-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;height:64px;margin:0;padding:0 40px 0 80px}@media screen and (max-width:1024px){.mdl-layout__header-row{height:56px;padding:0 16px 0 72px}}.mdl-layout__header-row>*{-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0}.mdl-layout__header--scroll .mdl-layout__header-row{width:100%}.mdl-layout__header-row .mdl-navigation{margin:0;padding:0;height:64px;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row;-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center}@media screen and (max-width:1024px){.mdl-layout__header-row .mdl-navigation{height:56px}}.mdl-layout__header-row .mdl-navigation__link{display:block;color:#fff;line-height:64px;padding:0 24px}@media screen and (max-width:1024px){.mdl-layout__header-row .mdl-navigation__link{line-height:56px;padding:0 16px}}.mdl-layout__obfuscator{background-color:transparent;position:absolute;top:0;left:0;height:100%;width:100%;z-index:4;visibility:hidden;-webkit-transition-property:background-color;transition-property:background-color;-webkit-transition-duration:.2s;transition-duration:.2s;-webkit-transition-timing-function:cubic-bezier(.4,0,.2,1);transition-timing-function:cubic-bezier(.4,0,.2,1)}.mdl-layout__obfuscator.is-visible{background-color:rgba(0,0,0,.5);visibility:visible}.mdl-layout__content{-ms-flex:0 1 auto;display:inline-block;overflow-y:auto;overflow-x:hidden;-webkit-box-flex:1;-webkit-flex-grow:1;-ms-flex-positive:1;flex-grow:1;z-index:1;-webkit-overflow-scrolling:touch}.mdl-layout--fixed-drawer>.mdl-layout__content{margin-left:240px}.mdl-layout__container.has-scrolling-header .mdl-layout__content{overflow:visible}@media screen and (max-width:1024px){.mdl-layout--fixed-drawer>.mdl-layout__content{margin-left:0}.mdl-layout__container.has-scrolling-header .mdl-layout__content{overflow-y:auto;overflow-x:hidden}}.mdl-layout__tab-bar{height:96px;margin:0;width:calc(100% - 112px);padding:0 0 0 56px;display:-webkit-flex;display:-ms-flexbox;display:flex;background-color:#3f51b5;overflow-y:hidden;overflow-x:scroll}.mdl-layout__tab-bar::-webkit-scrollbar{display:none}@media screen and (max-width:1024px){.mdl-layout__tab-bar{width:calc(100% - 60px);padding:0 0 0 60px}}.mdl-layout--fixed-tabs .mdl-layout__tab-bar{padding:0;overflow:hidden;width:100%}.mdl-layout__tab-bar-container{position:relative;height:48px;width:100%;border:none;margin:0;z-index:2;-webkit-box-flex:0;-webkit-flex-grow:0;-ms-flex-positive:0;flex-grow:0;-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0;overflow:hidden}.mdl-layout__container>.mdl-layout__tab-bar-container{position:absolute;top:0;left:0}.mdl-layout__tab-bar-button{display:inline-block;position:absolute;top:0;height:48px;width:56px;z-index:4;text-align:center;background-color:#3f51b5;color:transparent;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}@media screen and (max-width:1024px){.mdl-layout__tab-bar-button{display:none;width:60px}}.mdl-layout--fixed-tabs .mdl-layout__tab-bar-button{display:none}.mdl-layout__tab-bar-button .material-icons{line-height:48px}.mdl-layout__tab-bar-button.is-active{color:#fff}.mdl-layout__tab-bar-left-button{left:0}.mdl-layout__tab-bar-right-button{right:0}.mdl-layout__tab{margin:0;border:none;padding:0 24px;float:left;position:relative;display:block;-webkit-box-flex:0;-webkit-flex-grow:0;-ms-flex-positive:0;flex-grow:0;-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0;text-decoration:none;height:48px;line-height:48px;text-align:center;font-weight:500;font-size:14px;text-transform:uppercase;color:rgba(255,255,255,.6);overflow:hidden}@media screen and (max-width:1024px){.mdl-layout__tab{padding:0 12px}}.mdl-layout--fixed-tabs .mdl-layout__tab{float:none;-webkit-box-flex:1;-webkit-flex-grow:1;-ms-flex-positive:1;flex-grow:1;padding:0}.mdl-layout.is-upgraded .mdl-layout__tab.is-active{color:#fff}.mdl-layout.is-upgraded .mdl-layout__tab.is-active::after{height:2px;width:100%;display:block;content:\" \";bottom:0;left:0;position:absolute;background:#ff4081;-webkit-animation:border-expand .2s cubic-bezier(.4,0,.4,1).01s alternate forwards;animation:border-expand .2s cubic-bezier(.4,0,.4,1).01s alternate forwards;-webkit-transition:all 1s cubic-bezier(.4,0,1,1);transition:all 1s cubic-bezier(.4,0,1,1)}.mdl-layout__tab .mdl-layout__tab-ripple-container{display:block;position:absolute;height:100%;width:100%;left:0;top:0;z-index:1;overflow:hidden}.mdl-layout__tab .mdl-layout__tab-ripple-container .mdl-ripple{background-color:#fff}.mdl-layout__tab-panel{display:block}.mdl-layout.is-upgraded .mdl-layout__tab-panel{display:none}.mdl-layout.is-upgraded .mdl-layout__tab-panel.is-active{display:block}.mdl-radio{position:relative;font-size:16px;line-height:24px;display:inline-block;box-sizing:border-box;margin:0;padding-left:0}.mdl-radio.is-upgraded{padding-left:24px}.mdl-radio__button{line-height:24px}.mdl-radio.is-upgraded .mdl-radio__button{position:absolute;width:0;height:0;margin:0;padding:0;opacity:0;-ms-appearance:none;-moz-appearance:none;-webkit-appearance:none;appearance:none;border:none}.mdl-radio__outer-circle{position:absolute;top:4px;left:0;display:inline-block;box-sizing:border-box;width:16px;height:16px;margin:0;cursor:pointer;border:2px solid rgba(0,0,0,.54);border-radius:50%;z-index:2}.mdl-radio.is-checked .mdl-radio__outer-circle{border:2px solid #3f51b5}.mdl-radio.is-disabled .mdl-radio__outer-circle{border:2px solid rgba(0,0,0,.26);cursor:auto}.mdl-radio__inner-circle{position:absolute;z-index:1;margin:0;top:8px;left:4px;box-sizing:border-box;width:8px;height:8px;cursor:pointer;-webkit-transition-duration:.28s;transition-duration:.28s;-webkit-transition-timing-function:cubic-bezier(.4,0,.2,1);transition-timing-function:cubic-bezier(.4,0,.2,1);-webkit-transition-property:-webkit-transform;transition-property:transform;-webkit-transform:scale3d(0,0,0);transform:scale3d(0,0,0);border-radius:50%;background:#3f51b5}.mdl-radio.is-checked .mdl-radio__inner-circle{-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}.mdl-radio.is-disabled .mdl-radio__inner-circle{background:rgba(0,0,0,.26);cursor:auto}.mdl-radio.is-focused .mdl-radio__inner-circle{box-shadow:0 0 0 10px rgba(0,0,0,.1)}.mdl-radio__label{cursor:pointer}.mdl-radio.is-disabled .mdl-radio__label{color:rgba(0,0,0,.26);cursor:auto}.mdl-radio__ripple-container{position:absolute;z-index:2;top:-9px;left:-13px;box-sizing:border-box;width:42px;height:42px;border-radius:50%;cursor:pointer;overflow:hidden;-webkit-mask-image:-webkit-radial-gradient(circle,#fff,#000)}.mdl-radio__ripple-container .mdl-ripple{background:#3f51b5}.mdl-radio.is-disabled .mdl-radio__ripple-container{cursor:auto}.mdl-radio.is-disabled .mdl-radio__ripple-container .mdl-ripple{background:0 0}_:-ms-input-placeholder,:root .mdl-slider.mdl-slider.is-upgraded{-ms-appearance:none;height:32px;margin:0}.mdl-slider{width:calc(100% - 40px);margin:0 20px}.mdl-slider.is-upgraded{-webkit-appearance:none;-moz-appearance:none;appearance:none;height:2px;background:0 0;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;outline:0;padding:0;color:#3f51b5;-webkit-align-self:center;-ms-flex-item-align:center;align-self:center;z-index:1;cursor:pointer}.mdl-slider.is-upgraded::-moz-focus-outer{border:0}.mdl-slider.is-upgraded::-ms-tooltip{display:none}.mdl-slider.is-upgraded::-webkit-slider-runnable-track{background:0 0}.mdl-slider.is-upgraded::-moz-range-track{background:0 0;border:none}.mdl-slider.is-upgraded::-ms-track{background:0 0;color:transparent;height:2px;width:100%;border:none}.mdl-slider.is-upgraded::-ms-fill-lower{padding:0;background:linear-gradient(to right,transparent,transparent 16px,#3f51b5 16px,#3f51b5 0)}.mdl-slider.is-upgraded::-ms-fill-upper{padding:0;background:linear-gradient(to left,transparent,transparent 16px,rgba(0,0,0,.26)16px,rgba(0,0,0,.26)0)}.mdl-slider.is-upgraded::-webkit-slider-thumb{-webkit-appearance:none;width:12px;height:12px;box-sizing:border-box;border-radius:50%;background:#3f51b5;border:none;-webkit-transition:-webkit-transform .18s cubic-bezier(.4,0,.2,1),border .18s cubic-bezier(.4,0,.2,1),box-shadow .18s cubic-bezier(.4,0,.2,1),background .28s cubic-bezier(.4,0,.2,1);transition:transform .18s cubic-bezier(.4,0,.2,1),border .18s cubic-bezier(.4,0,.2,1),box-shadow .18s cubic-bezier(.4,0,.2,1),background .28s cubic-bezier(.4,0,.2,1)}.mdl-slider.is-upgraded::-moz-range-thumb{-moz-appearance:none;width:12px;height:12px;box-sizing:border-box;border-radius:50%;background-image:none;background:#3f51b5;border:none}.mdl-slider.is-upgraded:focus:not(:active)::-webkit-slider-thumb{box-shadow:0 0 0 10px rgba(63,81,181,.26)}.mdl-slider.is-upgraded:focus:not(:active)::-moz-range-thumb{box-shadow:0 0 0 10px rgba(63,81,181,.26)}.mdl-slider.is-upgraded:active::-webkit-slider-thumb{background-image:none;background:#3f51b5;-webkit-transform:scale(1.5);transform:scale(1.5)}.mdl-slider.is-upgraded:active::-moz-range-thumb{background-image:none;background:#3f51b5;transform:scale(1.5)}.mdl-slider.is-upgraded::-ms-thumb{width:32px;height:32px;border:none;border-radius:50%;background:#3f51b5;-ms-transform:scale(.375);transform:scale(.375);transition:transform .18s cubic-bezier(.4,0,.2,1),background .28s cubic-bezier(.4,0,.2,1)}.mdl-slider.is-upgraded:focus:not(:active)::-ms-thumb{background:radial-gradient(circle closest-side,#3f51b5 0%,#3f51b5 37.5%,rgba(63,81,181,.26)37.5%,rgba(63,81,181,.26)100%);-ms-transform:scale(1);transform:scale(1)}.mdl-slider.is-upgraded:active::-ms-thumb{background:#3f51b5;-ms-transform:scale(.5625);transform:scale(.5625)}.mdl-slider.is-upgraded.is-lowest-value::-webkit-slider-thumb{border:2px solid rgba(0,0,0,.26);background:0 0}.mdl-slider.is-upgraded.is-lowest-value::-moz-range-thumb{border:2px solid rgba(0,0,0,.26);background:0 0}.mdl-slider.is-upgraded.is-lowest-value+.mdl-slider__background-flex>.mdl-slider__background-upper{left:6px}.mdl-slider.is-upgraded.is-lowest-value:focus:not(:active)::-webkit-slider-thumb{box-shadow:0 0 0 10px rgba(0,0,0,.12);background:rgba(0,0,0,.12)}.mdl-slider.is-upgraded.is-lowest-value:focus:not(:active)::-moz-range-thumb{box-shadow:0 0 0 10px rgba(0,0,0,.12);background:rgba(0,0,0,.12)}.mdl-slider.is-upgraded.is-lowest-value:active::-webkit-slider-thumb{border:1.6px solid rgba(0,0,0,.26);-webkit-transform:scale(1.5);transform:scale(1.5)}.mdl-slider.is-upgraded.is-lowest-value:active+.mdl-slider__background-flex>.mdl-slider__background-upper{left:9px}.mdl-slider.is-upgraded.is-lowest-value:active::-moz-range-thumb{border:1.5px solid rgba(0,0,0,.26);transform:scale(1.5)}.mdl-slider.is-upgraded.is-lowest-value::-ms-thumb{background:radial-gradient(circle closest-side,transparent 0%,transparent 66.67%,rgba(0,0,0,.26)66.67%,rgba(0,0,0,.26)100%)}.mdl-slider.is-upgraded.is-lowest-value:focus:not(:active)::-ms-thumb{background:radial-gradient(circle closest-side,rgba(0,0,0,.12)0%,rgba(0,0,0,.12)25%,rgba(0,0,0,.26)25%,rgba(0,0,0,.26)37.5%,rgba(0,0,0,.12)37.5%,rgba(0,0,0,.12)100%);-ms-transform:scale(1);transform:scale(1)}.mdl-slider.is-upgraded.is-lowest-value:active::-ms-thumb{-ms-transform:scale(.5625);transform:scale(.5625);background:radial-gradient(circle closest-side,transparent 0%,transparent 77.78%,rgba(0,0,0,.26)77.78%,rgba(0,0,0,.26)100%)}.mdl-slider.is-upgraded.is-lowest-value::-ms-fill-lower{background:0 0}.mdl-slider.is-upgraded.is-lowest-value::-ms-fill-upper{margin-left:6px}.mdl-slider.is-upgraded.is-lowest-value:active::-ms-fill-upper{margin-left:9px}.mdl-slider.is-upgraded:disabled:focus::-webkit-slider-thumb,.mdl-slider.is-upgraded:disabled:active::-webkit-slider-thumb,.mdl-slider.is-upgraded:disabled::-webkit-slider-thumb{-webkit-transform:scale(.667);transform:scale(.667);background:rgba(0,0,0,.26)}.mdl-slider.is-upgraded:disabled:focus::-moz-range-thumb,.mdl-slider.is-upgraded:disabled:active::-moz-range-thumb,.mdl-slider.is-upgraded:disabled::-moz-range-thumb{transform:scale(.667);background:rgba(0,0,0,.26)}.mdl-slider.is-upgraded:disabled+.mdl-slider__background-flex>.mdl-slider__background-lower{background-color:rgba(0,0,0,.26);left:-6px}.mdl-slider.is-upgraded:disabled+.mdl-slider__background-flex>.mdl-slider__background-upper{left:6px}.mdl-slider.is-upgraded.is-lowest-value:disabled:focus::-webkit-slider-thumb,.mdl-slider.is-upgraded.is-lowest-value:disabled:active::-webkit-slider-thumb,.mdl-slider.is-upgraded.is-lowest-value:disabled::-webkit-slider-thumb{border:3px solid rgba(0,0,0,.26);background:0 0;-webkit-transform:scale(.667);transform:scale(.667)}.mdl-slider.is-upgraded.is-lowest-value:disabled:focus::-moz-range-thumb,.mdl-slider.is-upgraded.is-lowest-value:disabled:active::-moz-range-thumb,.mdl-slider.is-upgraded.is-lowest-value:disabled::-moz-range-thumb{border:3px solid rgba(0,0,0,.26);background:0 0;transform:scale(.667)}.mdl-slider.is-upgraded.is-lowest-value:disabled:active+.mdl-slider__background-flex>.mdl-slider__background-upper{left:6px}.mdl-slider.is-upgraded:disabled:focus::-ms-thumb,.mdl-slider.is-upgraded:disabled:active::-ms-thumb,.mdl-slider.is-upgraded:disabled::-ms-thumb{-ms-transform:scale(.25);transform:scale(.25);background:rgba(0,0,0,.26)}.mdl-slider.is-upgraded.is-lowest-value:disabled:focus::-ms-thumb,.mdl-slider.is-upgraded.is-lowest-value:disabled:active::-ms-thumb,.mdl-slider.is-upgraded.is-lowest-value:disabled::-ms-thumb{-ms-transform:scale(.25);transform:scale(.25);background:radial-gradient(circle closest-side,transparent 0%,transparent 50%,rgba(0,0,0,.26)50%,rgba(0,0,0,.26)100%)}.mdl-slider.is-upgraded:disabled::-ms-fill-lower{margin-right:6px;background:linear-gradient(to right,transparent,transparent 25px,rgba(0,0,0,.26)25px,rgba(0,0,0,.26)0)}.mdl-slider.is-upgraded:disabled::-ms-fill-upper{margin-left:6px}.mdl-slider.is-upgraded.is-lowest-value:disabled:active::-ms-fill-upper{margin-left:6px}.mdl-slider__ie-container{height:18px;overflow:visible;border:none;margin:none;padding:none}.mdl-slider__container{height:18px;position:relative;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row}.mdl-slider__container,.mdl-slider__background-flex{background:0 0;display:-webkit-flex;display:-ms-flexbox;display:flex}.mdl-slider__background-flex{position:absolute;height:2px;width:calc(100% - 52px);top:50%;left:0;margin:0 26px;overflow:hidden;border:0;padding:0;-webkit-transform:translate(0,-1px);-ms-transform:translate(0,-1px);transform:translate(0,-1px)}.mdl-slider__background-lower{background:#3f51b5}.mdl-slider__background-lower,.mdl-slider__background-upper{-webkit-box-flex:0;-webkit-flex:0;-ms-flex:0;flex:0;position:relative;border:0;padding:0}.mdl-slider__background-upper{background:rgba(0,0,0,.26);-webkit-transition:left .18s cubic-bezier(.4,0,.2,1);transition:left .18s cubic-bezier(.4,0,.2,1)}.mdl-spinner{display:inline-block;position:relative;width:28px;height:28px}.mdl-spinner:not(.is-upgraded).is-active:after{content:\"Loading...\"}.mdl-spinner.is-upgraded.is-active{-webkit-animation:mdl-spinner__container-rotate 1568.23529412ms linear infinite;animation:mdl-spinner__container-rotate 1568.23529412ms linear infinite}@-webkit-keyframes mdl-spinner__container-rotate{to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes mdl-spinner__container-rotate{to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}.mdl-spinner__layer{position:absolute;width:100%;height:100%;opacity:0}.mdl-spinner__layer-1{border-color:#42a5f5}.mdl-spinner--single-color .mdl-spinner__layer-1{border-color:#3f51b5}.mdl-spinner.is-active .mdl-spinner__layer-1{-webkit-animation:mdl-spinner__fill-unfill-rotate 5332ms cubic-bezier(.4,0,.2,1)infinite both,mdl-spinner__layer-1-fade-in-out 5332ms cubic-bezier(.4,0,.2,1)infinite both;animation:mdl-spinner__fill-unfill-rotate 5332ms cubic-bezier(.4,0,.2,1)infinite both,mdl-spinner__layer-1-fade-in-out 5332ms cubic-bezier(.4,0,.2,1)infinite both}.mdl-spinner__layer-2{border-color:#f44336}.mdl-spinner--single-color .mdl-spinner__layer-2{border-color:#3f51b5}.mdl-spinner.is-active .mdl-spinner__layer-2{-webkit-animation:mdl-spinner__fill-unfill-rotate 5332ms cubic-bezier(.4,0,.2,1)infinite both,mdl-spinner__layer-2-fade-in-out 5332ms cubic-bezier(.4,0,.2,1)infinite both;animation:mdl-spinner__fill-unfill-rotate 5332ms cubic-bezier(.4,0,.2,1)infinite both,mdl-spinner__layer-2-fade-in-out 5332ms cubic-bezier(.4,0,.2,1)infinite both}.mdl-spinner__layer-3{border-color:#fdd835}.mdl-spinner--single-color .mdl-spinner__layer-3{border-color:#3f51b5}.mdl-spinner.is-active .mdl-spinner__layer-3{-webkit-animation:mdl-spinner__fill-unfill-rotate 5332ms cubic-bezier(.4,0,.2,1)infinite both,mdl-spinner__layer-3-fade-in-out 5332ms cubic-bezier(.4,0,.2,1)infinite both;animation:mdl-spinner__fill-unfill-rotate 5332ms cubic-bezier(.4,0,.2,1)infinite both,mdl-spinner__layer-3-fade-in-out 5332ms cubic-bezier(.4,0,.2,1)infinite both}.mdl-spinner__layer-4{border-color:#4caf50}.mdl-spinner--single-color .mdl-spinner__layer-4{border-color:#3f51b5}.mdl-spinner.is-active .mdl-spinner__layer-4{-webkit-animation:mdl-spinner__fill-unfill-rotate 5332ms cubic-bezier(.4,0,.2,1)infinite both,mdl-spinner__layer-4-fade-in-out 5332ms cubic-bezier(.4,0,.2,1)infinite both;animation:mdl-spinner__fill-unfill-rotate 5332ms cubic-bezier(.4,0,.2,1)infinite both,mdl-spinner__layer-4-fade-in-out 5332ms cubic-bezier(.4,0,.2,1)infinite both}@-webkit-keyframes mdl-spinner__fill-unfill-rotate{12.5%{-webkit-transform:rotate(135deg);transform:rotate(135deg)}25%{-webkit-transform:rotate(270deg);transform:rotate(270deg)}37.5%{-webkit-transform:rotate(405deg);transform:rotate(405deg)}50%{-webkit-transform:rotate(540deg);transform:rotate(540deg)}62.5%{-webkit-transform:rotate(675deg);transform:rotate(675deg)}75%{-webkit-transform:rotate(810deg);transform:rotate(810deg)}87.5%{-webkit-transform:rotate(945deg);transform:rotate(945deg)}to{-webkit-transform:rotate(1080deg);transform:rotate(1080deg)}}@keyframes mdl-spinner__fill-unfill-rotate{12.5%{-webkit-transform:rotate(135deg);transform:rotate(135deg)}25%{-webkit-transform:rotate(270deg);transform:rotate(270deg)}37.5%{-webkit-transform:rotate(405deg);transform:rotate(405deg)}50%{-webkit-transform:rotate(540deg);transform:rotate(540deg)}62.5%{-webkit-transform:rotate(675deg);transform:rotate(675deg)}75%{-webkit-transform:rotate(810deg);transform:rotate(810deg)}87.5%{-webkit-transform:rotate(945deg);transform:rotate(945deg)}to{-webkit-transform:rotate(1080deg);transform:rotate(1080deg)}}@-webkit-keyframes mdl-spinner__layer-1-fade-in-out{from,25%{opacity:.99}26%,89%{opacity:0}90%,100%{opacity:.99}}@keyframes mdl-spinner__layer-1-fade-in-out{from,25%{opacity:.99}26%,89%{opacity:0}90%,100%{opacity:.99}}@-webkit-keyframes mdl-spinner__layer-2-fade-in-out{from,15%{opacity:0}25%,50%{opacity:.99}51%{opacity:0}}@keyframes mdl-spinner__layer-2-fade-in-out{from,15%{opacity:0}25%,50%{opacity:.99}51%{opacity:0}}@-webkit-keyframes mdl-spinner__layer-3-fade-in-out{from,40%{opacity:0}50%,75%{opacity:.99}76%{opacity:0}}@keyframes mdl-spinner__layer-3-fade-in-out{from,40%{opacity:0}50%,75%{opacity:.99}76%{opacity:0}}@-webkit-keyframes mdl-spinner__layer-4-fade-in-out{from,65%{opacity:0}75%,90%{opacity:.99}100%{opacity:0}}@keyframes mdl-spinner__layer-4-fade-in-out{from,65%{opacity:0}75%,90%{opacity:.99}100%{opacity:0}}.mdl-spinner__gap-patch{position:absolute;box-sizing:border-box;top:0;left:45%;width:10%;height:100%;overflow:hidden;border-color:inherit}.mdl-spinner__gap-patch .mdl-spinner__circle{width:1000%;left:-450%}.mdl-spinner__circle-clipper{display:inline-block;position:relative;width:50%;height:100%;overflow:hidden;border-color:inherit}.mdl-spinner__circle-clipper .mdl-spinner__circle{width:200%}.mdl-spinner__circle{box-sizing:border-box;height:100%;border-width:3px;border-style:solid;border-color:inherit;border-bottom-color:transparent!important;border-radius:50%;-webkit-animation:none;animation:none;position:absolute;top:0;right:0;bottom:0;left:0}.mdl-spinner__left .mdl-spinner__circle{border-right-color:transparent!important;-webkit-transform:rotate(129deg);-ms-transform:rotate(129deg);transform:rotate(129deg)}.mdl-spinner.is-active .mdl-spinner__left .mdl-spinner__circle{-webkit-animation:mdl-spinner__left-spin 1333ms cubic-bezier(.4,0,.2,1)infinite both;animation:mdl-spinner__left-spin 1333ms cubic-bezier(.4,0,.2,1)infinite both}.mdl-spinner__right .mdl-spinner__circle{left:-100%;border-left-color:transparent!important;-webkit-transform:rotate(-129deg);-ms-transform:rotate(-129deg);transform:rotate(-129deg)}.mdl-spinner.is-active .mdl-spinner__right .mdl-spinner__circle{-webkit-animation:mdl-spinner__right-spin 1333ms cubic-bezier(.4,0,.2,1)infinite both;animation:mdl-spinner__right-spin 1333ms cubic-bezier(.4,0,.2,1)infinite both}@-webkit-keyframes mdl-spinner__left-spin{from{-webkit-transform:rotate(130deg);transform:rotate(130deg)}50%{-webkit-transform:rotate(-5deg);transform:rotate(-5deg)}to{-webkit-transform:rotate(130deg);transform:rotate(130deg)}}@keyframes mdl-spinner__left-spin{from{-webkit-transform:rotate(130deg);transform:rotate(130deg)}50%{-webkit-transform:rotate(-5deg);transform:rotate(-5deg)}to{-webkit-transform:rotate(130deg);transform:rotate(130deg)}}@-webkit-keyframes mdl-spinner__right-spin{from{-webkit-transform:rotate(-130deg);transform:rotate(-130deg)}50%{-webkit-transform:rotate(5deg);transform:rotate(5deg)}to{-webkit-transform:rotate(-130deg);transform:rotate(-130deg)}}@keyframes mdl-spinner__right-spin{from{-webkit-transform:rotate(-130deg);transform:rotate(-130deg)}50%{-webkit-transform:rotate(5deg);transform:rotate(5deg)}to{-webkit-transform:rotate(-130deg);transform:rotate(-130deg)}}.mdl-switch{position:relative;z-index:1;vertical-align:middle;display:inline-block;box-sizing:border-box;width:100%;height:24px;margin:0;padding:0;overflow:visible;-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.mdl-switch.is-upgraded{padding-left:28px}.mdl-switch__input{line-height:24px}.mdl-switch.is-upgraded .mdl-switch__input{position:absolute;width:0;height:0;margin:0;padding:0;opacity:0;-ms-appearance:none;-moz-appearance:none;-webkit-appearance:none;appearance:none;border:none}.mdl-switch__track{background:rgba(0,0,0,.26);position:absolute;left:0;top:5px;height:14px;width:36px;border-radius:14px;cursor:pointer}.mdl-switch.is-checked .mdl-switch__track{background:rgba(63,81,181,.5)}.mdl-switch.is-disabled .mdl-switch__track{background:rgba(0,0,0,.12);cursor:auto}.mdl-switch__thumb{background:#fafafa;position:absolute;left:0;top:2px;height:20px;width:20px;border-radius:50%;cursor:pointer;box-shadow:0 2px 2px 0 rgba(0,0,0,.14),0 3px 1px -2px rgba(0,0,0,.2),0 1px 5px 0 rgba(0,0,0,.12);-webkit-transition-duration:.28s;transition-duration:.28s;-webkit-transition-timing-function:cubic-bezier(.4,0,.2,1);transition-timing-function:cubic-bezier(.4,0,.2,1);-webkit-transition-property:left;transition-property:left}.mdl-switch.is-checked .mdl-switch__thumb{background:#3f51b5;left:16px;box-shadow:0 3px 4px 0 rgba(0,0,0,.14),0 3px 3px -2px rgba(0,0,0,.2),0 1px 8px 0 rgba(0,0,0,.12)}.mdl-switch.is-disabled .mdl-switch__thumb{background:#bdbdbd;cursor:auto}.mdl-switch__focus-helper{position:absolute;top:50%;left:50%;-webkit-transform:translate(-4px,-4px);-ms-transform:translate(-4px,-4px);transform:translate(-4px,-4px);display:inline-block;box-sizing:border-box;width:8px;height:8px;border-radius:50%;background-color:transparent}.mdl-switch.is-focused .mdl-switch__focus-helper{box-shadow:0 0 0 20px rgba(0,0,0,.1);background-color:rgba(0,0,0,.1)}.mdl-switch.is-focused.is-checked .mdl-switch__focus-helper{box-shadow:0 0 0 20px rgba(63,81,181,.26);background-color:rgba(63,81,181,.26)}.mdl-switch__label{position:relative;cursor:pointer;font-size:16px;line-height:24px;margin:0;left:24px}.mdl-switch.is-disabled .mdl-switch__label{color:#bdbdbd;cursor:auto}.mdl-switch__ripple-container{position:absolute;z-index:2;top:-12px;left:-14px;box-sizing:border-box;width:48px;height:48px;border-radius:50%;cursor:pointer;overflow:hidden;-webkit-mask-image:-webkit-radial-gradient(circle,#fff,#000);-webkit-transition-duration:.4s;transition-duration:.4s;-webkit-transition-timing-function:step-end;transition-timing-function:step-end;-webkit-transition-property:left;transition-property:left}.mdl-switch__ripple-container .mdl-ripple{background:#3f51b5}.mdl-switch.is-disabled .mdl-switch__ripple-container{cursor:auto}.mdl-switch.is-disabled .mdl-switch__ripple-container .mdl-ripple{background:0 0}.mdl-switch.is-checked .mdl-switch__ripple-container{cursor:auto;left:2px}.mdl-tabs{display:block;width:100%}.mdl-tabs__tab-bar{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;-webkit-align-content:space-between;-ms-flex-line-pack:justify;align-content:space-between;-webkit-box-align:start;-webkit-align-items:flex-start;-ms-flex-align:start;align-items:flex-start;height:48px;padding:0;margin:0;border-bottom:1px solid #e0e0e0}.mdl-tabs__tab{margin:0;border:none;padding:0 24px;float:left;position:relative;display:block;color:red;text-decoration:none;height:48px;line-height:48px;text-align:center;font-weight:500;font-size:14px;text-transform:uppercase;color:rgba(0,0,0,.54);overflow:hidden}.mdl-tabs.is-upgraded .mdl-tabs__tab.is-active{color:rgba(0,0,0,.87)}.mdl-tabs.is-upgraded .mdl-tabs__tab.is-active:after{height:2px;width:100%;display:block;content:\" \";bottom:0;left:0;position:absolute;background:#3f51b5;-webkit-animation:border-expand .2s cubic-bezier(.4,0,.4,1).01s alternate forwards;animation:border-expand .2s cubic-bezier(.4,0,.4,1).01s alternate forwards;-webkit-transition:all 1s cubic-bezier(.4,0,1,1);transition:all 1s cubic-bezier(.4,0,1,1)}.mdl-tabs__tab .mdl-tabs__ripple-container{display:block;position:absolute;height:100%;width:100%;left:0;top:0;z-index:1;overflow:hidden}.mdl-tabs__tab .mdl-tabs__ripple-container .mdl-ripple{background:#3f51b5}.mdl-tabs__panel{display:block}.mdl-tabs.is-upgraded .mdl-tabs__panel{display:none}.mdl-tabs.is-upgraded .mdl-tabs__panel.is-active{display:block}@-webkit-keyframes border-expand{0%{opacity:0;width:0}100%{opacity:1;width:100%}}@keyframes border-expand{0%{opacity:0;width:0}100%{opacity:1;width:100%}}.mdl-textfield{position:relative;font-size:16px;display:inline-block;box-sizing:border-box;width:300px;max-width:100%;margin:0;padding:20px 0}.mdl-textfield .mdl-button{position:absolute;bottom:20px}.mdl-textfield--align-right{text-align:right}.mdl-textfield--full-width{width:100%}.mdl-textfield--expandable{min-width:32px;width:auto;min-height:32px}.mdl-textfield__input{border:none;border-bottom:1px solid rgba(0,0,0,.12);display:block;font-size:16px;margin:0;padding:4px 0;width:100%;background:0 0;text-align:left;color:inherit}.mdl-textfield.is-focused .mdl-textfield__input{outline:none}.mdl-textfield.is-invalid .mdl-textfield__input{border-color:#de3226;box-shadow:none}.mdl-textfield.is-disabled .mdl-textfield__input{background-color:transparent;border-bottom:1px dotted rgba(0,0,0,.12);color:rgba(0,0,0,.26)}.mdl-textfield textarea.mdl-textfield__input{display:block}.mdl-textfield__label{bottom:0;color:rgba(0,0,0,.26);font-size:16px;left:0;right:0;pointer-events:none;position:absolute;display:block;top:24px;width:100%;overflow:hidden;white-space:nowrap;text-align:left}.mdl-textfield.is-dirty .mdl-textfield__label{visibility:hidden}.mdl-textfield--floating-label .mdl-textfield__label{-webkit-transition-duration:.2s;transition-duration:.2s;-webkit-transition-timing-function:cubic-bezier(.4,0,.2,1);transition-timing-function:cubic-bezier(.4,0,.2,1)}.mdl-textfield.is-disabled.is-disabled .mdl-textfield__label{color:rgba(0,0,0,.26)}.mdl-textfield--floating-label.is-focused .mdl-textfield__label,.mdl-textfield--floating-label.is-dirty .mdl-textfield__label{color:#3f51b5;font-size:12px;top:4px;visibility:visible}.mdl-textfield--floating-label.is-focused .mdl-textfield__expandable-holder .mdl-textfield__label,.mdl-textfield--floating-label.is-dirty .mdl-textfield__expandable-holder .mdl-textfield__label{top:-16px}.mdl-textfield--floating-label.is-invalid .mdl-textfield__label{color:#de3226;font-size:12px}.mdl-textfield__label:after{background-color:#3f51b5;bottom:20px;content:'';height:2px;left:45%;position:absolute;-webkit-transition-duration:.2s;transition-duration:.2s;-webkit-transition-timing-function:cubic-bezier(.4,0,.2,1);transition-timing-function:cubic-bezier(.4,0,.2,1);visibility:hidden;width:10px}.mdl-textfield.is-focused .mdl-textfield__label:after{left:0;visibility:visible;width:100%}.mdl-textfield.is-invalid .mdl-textfield__label:after{background-color:#de3226}.mdl-textfield__error{color:#de3226;position:absolute;font-size:12px;margin-top:3px;visibility:hidden;display:block}.mdl-textfield.is-invalid .mdl-textfield__error{visibility:visible}.mdl-textfield__expandable-holder{position:relative;margin-left:32px;-webkit-transition-duration:.2s;transition-duration:.2s;-webkit-transition-timing-function:cubic-bezier(.4,0,.2,1);transition-timing-function:cubic-bezier(.4,0,.2,1);display:inline-block;max-width:.1px}.mdl-textfield.is-focused .mdl-textfield__expandable-holder,.mdl-textfield.is-dirty .mdl-textfield__expandable-holder{max-width:600px}.mdl-textfield__expandable-holder .mdl-textfield__label:after{bottom:0}.mdl-tooltip{-webkit-transform:scale(0);-ms-transform:scale(0);transform:scale(0);-webkit-transform-origin:top center;-ms-transform-origin:top center;transform-origin:top center;will-change:transform;z-index:999;background:rgba(97,97,97,.9);border-radius:2px;color:#fff;display:inline-block;font-size:10px;font-weight:500;line-height:14px;max-width:170px;position:fixed;top:-500px;left:-500px;padding:8px;text-align:center}.mdl-tooltip.is-active{-webkit-animation:pulse 200ms cubic-bezier(0,0,.2,1)forwards;animation:pulse 200ms cubic-bezier(0,0,.2,1)forwards}.mdl-tooltip--large{line-height:14px;font-size:14px;padding:16px}@-webkit-keyframes pulse{0%{-webkit-transform:scale(0);transform:scale(0);opacity:0}50%{-webkit-transform:scale(.99);transform:scale(.99)}100%{-webkit-transform:scale(1);transform:scale(1);opacity:1;visibility:visible}}@keyframes pulse{0%{-webkit-transform:scale(0);transform:scale(0);opacity:0}50%{-webkit-transform:scale(.99);transform:scale(.99)}100%{-webkit-transform:scale(1);transform:scale(1);opacity:1;visibility:visible}}.mdl-shadow--2dp{box-shadow:0 2px 2px 0 rgba(0,0,0,.14),0 3px 1px -2px rgba(0,0,0,.2),0 1px 5px 0 rgba(0,0,0,.12)}.mdl-shadow--3dp{box-shadow:0 3px 4px 0 rgba(0,0,0,.14),0 3px 3px -2px rgba(0,0,0,.2),0 1px 8px 0 rgba(0,0,0,.12)}.mdl-shadow--4dp{box-shadow:0 4px 5px 0 rgba(0,0,0,.14),0 1px 10px 0 rgba(0,0,0,.12),0 2px 4px -1px rgba(0,0,0,.2)}.mdl-shadow--6dp{box-shadow:0 6px 10px 0 rgba(0,0,0,.14),0 1px 18px 0 rgba(0,0,0,.12),0 3px 5px -1px rgba(0,0,0,.2)}.mdl-shadow--8dp{box-shadow:0 8px 10px 1px rgba(0,0,0,.14),0 3px 14px 2px rgba(0,0,0,.12),0 5px 5px -3px rgba(0,0,0,.2)}.mdl-shadow--16dp{box-shadow:0 16px 24px 2px rgba(0,0,0,.14),0 6px 30px 5px rgba(0,0,0,.12),0 8px 10px -5px rgba(0,0,0,.2)}.mdl-grid{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-flow:row wrap;-ms-flex-flow:row wrap;flex-flow:row wrap;margin:0 auto;-webkit-box-align:stretch;-webkit-align-items:stretch;-ms-flex-align:stretch;align-items:stretch}.mdl-grid.mdl-grid--no-spacing{padding:0}.mdl-cell{box-sizing:border-box}.mdl-cell--top{-webkit-align-self:flex-start;-ms-flex-item-align:start;align-self:flex-start}.mdl-cell--middle{-webkit-align-self:center;-ms-flex-item-align:center;align-self:center}.mdl-cell--bottom{-webkit-align-self:flex-end;-ms-flex-item-align:end;align-self:flex-end}.mdl-cell--stretch{-webkit-align-self:stretch;-ms-flex-item-align:stretch;align-self:stretch}.mdl-grid.mdl-grid--no-spacing>.mdl-cell{margin:0}@media (max-width:479px){.mdl-grid{padding:8px}.mdl-cell{margin:8px;width:calc(100% - 16px)}.mdl-grid--no-spacing>.mdl-cell{width:100%}.mdl-cell--hide-phone{display:none!important}.mdl-cell--1-col,.mdl-cell--1-col-phone.mdl-cell--1-col-phone{width:calc(25% - 16px)}.mdl-grid--no-spacing>.mdl-cell--1-col,.mdl-grid--no-spacing>.mdl-cell--1-col-phone.mdl-cell--1-col-phone{width:25%}.mdl-cell--2-col,.mdl-cell--2-col-phone.mdl-cell--2-col-phone{width:calc(50% - 16px)}.mdl-grid--no-spacing>.mdl-cell--2-col,.mdl-grid--no-spacing>.mdl-cell--2-col-phone.mdl-cell--2-col-phone{width:50%}.mdl-cell--3-col,.mdl-cell--3-col-phone.mdl-cell--3-col-phone{width:calc(75% - 16px)}.mdl-grid--no-spacing>.mdl-cell--3-col,.mdl-grid--no-spacing>.mdl-cell--3-col-phone.mdl-cell--3-col-phone{width:75%}.mdl-cell--4-col,.mdl-cell--4-col-phone.mdl-cell--4-col-phone{width:calc(100% - 16px)}.mdl-grid--no-spacing>.mdl-cell--4-col,.mdl-grid--no-spacing>.mdl-cell--4-col-phone.mdl-cell--4-col-phone{width:100%}.mdl-cell--5-col,.mdl-cell--5-col-phone.mdl-cell--5-col-phone{width:calc(100% - 16px)}.mdl-grid--no-spacing>.mdl-cell--5-col,.mdl-grid--no-spacing>.mdl-cell--5-col-phone.mdl-cell--5-col-phone{width:100%}.mdl-cell--6-col,.mdl-cell--6-col-phone.mdl-cell--6-col-phone{width:calc(100% - 16px)}.mdl-grid--no-spacing>.mdl-cell--6-col,.mdl-grid--no-spacing>.mdl-cell--6-col-phone.mdl-cell--6-col-phone{width:100%}.mdl-cell--7-col,.mdl-cell--7-col-phone.mdl-cell--7-col-phone{width:calc(100% - 16px)}.mdl-grid--no-spacing>.mdl-cell--7-col,.mdl-grid--no-spacing>.mdl-cell--7-col-phone.mdl-cell--7-col-phone{width:100%}.mdl-cell--8-col,.mdl-cell--8-col-phone.mdl-cell--8-col-phone{width:calc(100% - 16px)}.mdl-grid--no-spacing>.mdl-cell--8-col,.mdl-grid--no-spacing>.mdl-cell--8-col-phone.mdl-cell--8-col-phone{width:100%}.mdl-cell--9-col,.mdl-cell--9-col-phone.mdl-cell--9-col-phone{width:calc(100% - 16px)}.mdl-grid--no-spacing>.mdl-cell--9-col,.mdl-grid--no-spacing>.mdl-cell--9-col-phone.mdl-cell--9-col-phone{width:100%}.mdl-cell--10-col,.mdl-cell--10-col-phone.mdl-cell--10-col-phone{width:calc(100% - 16px)}.mdl-grid--no-spacing>.mdl-cell--10-col,.mdl-grid--no-spacing>.mdl-cell--10-col-phone.mdl-cell--10-col-phone{width:100%}.mdl-cell--11-col,.mdl-cell--11-col-phone.mdl-cell--11-col-phone{width:calc(100% - 16px)}.mdl-grid--no-spacing>.mdl-cell--11-col,.mdl-grid--no-spacing>.mdl-cell--11-col-phone.mdl-cell--11-col-phone{width:100%}.mdl-cell--12-col,.mdl-cell--12-col-phone.mdl-cell--12-col-phone{width:calc(100% - 16px)}.mdl-grid--no-spacing>.mdl-cell--12-col,.mdl-grid--no-spacing>.mdl-cell--12-col-phone.mdl-cell--12-col-phone{width:100%}}@media (min-width:480px) and (max-width:839px){.mdl-grid{padding:8px}.mdl-cell{margin:8px;width:calc(50% - 16px)}.mdl-grid--no-spacing>.mdl-cell{width:50%}.mdl-cell--hide-tablet{display:none!important}.mdl-cell--1-col,.mdl-cell--1-col-tablet.mdl-cell--1-col-tablet{width:calc(12.5% - 16px)}.mdl-grid--no-spacing>.mdl-cell--1-col,.mdl-grid--no-spacing>.mdl-cell--1-col-tablet.mdl-cell--1-col-tablet{width:12.5%}.mdl-cell--2-col,.mdl-cell--2-col-tablet.mdl-cell--2-col-tablet{width:calc(25% - 16px)}.mdl-grid--no-spacing>.mdl-cell--2-col,.mdl-grid--no-spacing>.mdl-cell--2-col-tablet.mdl-cell--2-col-tablet{width:25%}.mdl-cell--3-col,.mdl-cell--3-col-tablet.mdl-cell--3-col-tablet{width:calc(37.5% - 16px)}.mdl-grid--no-spacing>.mdl-cell--3-col,.mdl-grid--no-spacing>.mdl-cell--3-col-tablet.mdl-cell--3-col-tablet{width:37.5%}.mdl-cell--4-col,.mdl-cell--4-col-tablet.mdl-cell--4-col-tablet{width:calc(50% - 16px)}.mdl-grid--no-spacing>.mdl-cell--4-col,.mdl-grid--no-spacing>.mdl-cell--4-col-tablet.mdl-cell--4-col-tablet{width:50%}.mdl-cell--5-col,.mdl-cell--5-col-tablet.mdl-cell--5-col-tablet{width:calc(62.5% - 16px)}.mdl-grid--no-spacing>.mdl-cell--5-col,.mdl-grid--no-spacing>.mdl-cell--5-col-tablet.mdl-cell--5-col-tablet{width:62.5%}.mdl-cell--6-col,.mdl-cell--6-col-tablet.mdl-cell--6-col-tablet{width:calc(75% - 16px)}.mdl-grid--no-spacing>.mdl-cell--6-col,.mdl-grid--no-spacing>.mdl-cell--6-col-tablet.mdl-cell--6-col-tablet{width:75%}.mdl-cell--7-col,.mdl-cell--7-col-tablet.mdl-cell--7-col-tablet{width:calc(87.5% - 16px)}.mdl-grid--no-spacing>.mdl-cell--7-col,.mdl-grid--no-spacing>.mdl-cell--7-col-tablet.mdl-cell--7-col-tablet{width:87.5%}.mdl-cell--8-col,.mdl-cell--8-col-tablet.mdl-cell--8-col-tablet{width:calc(100% - 16px)}.mdl-grid--no-spacing>.mdl-cell--8-col,.mdl-grid--no-spacing>.mdl-cell--8-col-tablet.mdl-cell--8-col-tablet{width:100%}.mdl-cell--9-col,.mdl-cell--9-col-tablet.mdl-cell--9-col-tablet{width:calc(100% - 16px)}.mdl-grid--no-spacing>.mdl-cell--9-col,.mdl-grid--no-spacing>.mdl-cell--9-col-tablet.mdl-cell--9-col-tablet{width:100%}.mdl-cell--10-col,.mdl-cell--10-col-tablet.mdl-cell--10-col-tablet{width:calc(100% - 16px)}.mdl-grid--no-spacing>.mdl-cell--10-col,.mdl-grid--no-spacing>.mdl-cell--10-col-tablet.mdl-cell--10-col-tablet{width:100%}.mdl-cell--11-col,.mdl-cell--11-col-tablet.mdl-cell--11-col-tablet{width:calc(100% - 16px)}.mdl-grid--no-spacing>.mdl-cell--11-col,.mdl-grid--no-spacing>.mdl-cell--11-col-tablet.mdl-cell--11-col-tablet{width:100%}.mdl-cell--12-col,.mdl-cell--12-col-tablet.mdl-cell--12-col-tablet{width:calc(100% - 16px)}.mdl-grid--no-spacing>.mdl-cell--12-col,.mdl-grid--no-spacing>.mdl-cell--12-col-tablet.mdl-cell--12-col-tablet{width:100%}}@media (min-width:840px){.mdl-grid{padding:8px}.mdl-cell{margin:8px;width:calc(33.3333333333% - 16px)}.mdl-grid--no-spacing>.mdl-cell{width:33.3333333333%}.mdl-cell--hide-desktop{display:none!important}.mdl-cell--1-col,.mdl-cell--1-col-desktop.mdl-cell--1-col-desktop{width:calc(8.3333333333% - 16px)}.mdl-grid--no-spacing>.mdl-cell--1-col,.mdl-grid--no-spacing>.mdl-cell--1-col-desktop.mdl-cell--1-col-desktop{width:8.3333333333%}.mdl-cell--2-col,.mdl-cell--2-col-desktop.mdl-cell--2-col-desktop{width:calc(16.6666666667% - 16px)}.mdl-grid--no-spacing>.mdl-cell--2-col,.mdl-grid--no-spacing>.mdl-cell--2-col-desktop.mdl-cell--2-col-desktop{width:16.6666666667%}.mdl-cell--3-col,.mdl-cell--3-col-desktop.mdl-cell--3-col-desktop{width:calc(25% - 16px)}.mdl-grid--no-spacing>.mdl-cell--3-col,.mdl-grid--no-spacing>.mdl-cell--3-col-desktop.mdl-cell--3-col-desktop{width:25%}.mdl-cell--4-col,.mdl-cell--4-col-desktop.mdl-cell--4-col-desktop{width:calc(33.3333333333% - 16px)}.mdl-grid--no-spacing>.mdl-cell--4-col,.mdl-grid--no-spacing>.mdl-cell--4-col-desktop.mdl-cell--4-col-desktop{width:33.3333333333%}.mdl-cell--5-col,.mdl-cell--5-col-desktop.mdl-cell--5-col-desktop{width:calc(41.6666666667% - 16px)}.mdl-grid--no-spacing>.mdl-cell--5-col,.mdl-grid--no-spacing>.mdl-cell--5-col-desktop.mdl-cell--5-col-desktop{width:41.6666666667%}.mdl-cell--6-col,.mdl-cell--6-col-desktop.mdl-cell--6-col-desktop{width:calc(50% - 16px)}.mdl-grid--no-spacing>.mdl-cell--6-col,.mdl-grid--no-spacing>.mdl-cell--6-col-desktop.mdl-cell--6-col-desktop{width:50%}.mdl-cell--7-col,.mdl-cell--7-col-desktop.mdl-cell--7-col-desktop{width:calc(58.3333333333% - 16px)}.mdl-grid--no-spacing>.mdl-cell--7-col,.mdl-grid--no-spacing>.mdl-cell--7-col-desktop.mdl-cell--7-col-desktop{width:58.3333333333%}.mdl-cell--8-col,.mdl-cell--8-col-desktop.mdl-cell--8-col-desktop{width:calc(66.6666666667% - 16px)}.mdl-grid--no-spacing>.mdl-cell--8-col,.mdl-grid--no-spacing>.mdl-cell--8-col-desktop.mdl-cell--8-col-desktop{width:66.6666666667%}.mdl-cell--9-col,.mdl-cell--9-col-desktop.mdl-cell--9-col-desktop{width:calc(75% - 16px)}.mdl-grid--no-spacing>.mdl-cell--9-col,.mdl-grid--no-spacing>.mdl-cell--9-col-desktop.mdl-cell--9-col-desktop{width:75%}.mdl-cell--10-col,.mdl-cell--10-col-desktop.mdl-cell--10-col-desktop{width:calc(83.3333333333% - 16px)}.mdl-grid--no-spacing>.mdl-cell--10-col,.mdl-grid--no-spacing>.mdl-cell--10-col-desktop.mdl-cell--10-col-desktop{width:83.3333333333%}.mdl-cell--11-col,.mdl-cell--11-col-desktop.mdl-cell--11-col-desktop{width:calc(91.6666666667% - 16px)}.mdl-grid--no-spacing>.mdl-cell--11-col,.mdl-grid--no-spacing>.mdl-cell--11-col-desktop.mdl-cell--11-col-desktop{width:91.6666666667%}.mdl-cell--12-col,.mdl-cell--12-col-desktop.mdl-cell--12-col-desktop{width:calc(100% - 16px)}.mdl-grid--no-spacing>.mdl-cell--12-col,.mdl-grid--no-spacing>.mdl-cell--12-col-desktop.mdl-cell--12-col-desktop{width:100%}}\n/*# sourceMappingURL=material.min.css.map */\n","/**\n * material-design-lite - Material Design Components in CSS, JS and HTML\n * @version v1.0.6\n * @license Apache-2.0\n * @copyright 2015 Google, Inc.\n * @link https://github.com/google/material-design-lite\n */\n@charset \"UTF-8\";\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* Material Design Lite */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* Typography */\n/* Shadows */\n/* Animations */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n/*\n * What follows is the result of much research on cross-browser styling.\n * Credit left inline and big thanks to Nicolas Gallagher, Jonathan Neal,\n * Kroc Camen, and the H5BP dev community and team.\n */\n/* ==========================================================================\n Base styles: opinionated defaults\n ========================================================================== */\nhtml {\n color: rgba(0,0,0, 0.87);\n font-size: 1em;\n line-height: 1.4; }\n\n/*\n * Remove text-shadow in selection highlight:\n * https://twitter.com/miketaylr/status/12228805301\n *\n * These selection rule sets have to be separate.\n * Customize the background color to match your design.\n */\n::-moz-selection {\n background: #b3d4fc;\n text-shadow: none; }\n::selection {\n background: #b3d4fc;\n text-shadow: none; }\n\n/*\n * A better looking default horizontal rule\n */\nhr {\n display: block;\n height: 1px;\n border: 0;\n border-top: 1px solid #ccc;\n margin: 1em 0;\n padding: 0; }\n\n/*\n * Remove the gap between audio, canvas, iframes,\n * images, videos and the bottom of their containers:\n * https://github.com/h5bp/html5-boilerplate/issues/440\n */\naudio,\ncanvas,\niframe,\nimg,\nsvg,\nvideo {\n vertical-align: middle; }\n\n/*\n * Remove default fieldset styles.\n */\nfieldset {\n border: 0;\n margin: 0;\n padding: 0; }\n\n/*\n * Allow only vertical resizing of textareas.\n */\ntextarea {\n resize: vertical; }\n\n/* ==========================================================================\n Browser Upgrade Prompt\n ========================================================================== */\n.browserupgrade {\n margin: 0.2em 0;\n background: #ccc;\n color: #000;\n padding: 0.2em 0; }\n\n/* ==========================================================================\n Author's custom styles\n ========================================================================== */\n/* ==========================================================================\n Helper classes\n ========================================================================== */\n/*\n * Hide visually and from screen readers:\n */\n.hidden {\n display: none !important; }\n\n/*\n * Hide only visually, but have it available for screen readers:\n * http://snook.ca/archives/html_and_css/hiding-content-for-accessibility\n */\n.visuallyhidden {\n border: 0;\n clip: rect(0 0 0 0);\n height: 1px;\n margin: -1px;\n overflow: hidden;\n padding: 0;\n position: absolute;\n width: 1px; }\n\n/*\n * Extends the .visuallyhidden class to allow the element\n * to be focusable when navigated to via the keyboard:\n * https://www.drupal.org/node/897638\n */\n.visuallyhidden.focusable:active,\n.visuallyhidden.focusable:focus {\n clip: auto;\n height: auto;\n margin: 0;\n overflow: visible;\n position: static;\n width: auto; }\n\n/*\n * Hide visually and from screen readers, but maintain layout\n */\n.invisible {\n visibility: hidden; }\n\n/*\n * Clearfix: contain floats\n *\n * For modern browsers\n * 1. The space content is one way to avoid an Opera bug when the\n * `contenteditable` attribute is included anywhere else in the document.\n * Otherwise it causes space to appear at the top and bottom of elements\n * that receive the `clearfix` class.\n * 2. The use of `table` rather than `block` is only necessary if using\n * `:before` to contain the top-margins of child elements.\n */\n.clearfix:before,\n.clearfix:after {\n content: \" \";\n /* 1 */\n display: table;\n /* 2 */ }\n\n.clearfix:after {\n clear: both; }\n\n/* ==========================================================================\n EXAMPLE Media Queries for Responsive Design.\n These examples override the primary ('mobile first') styles.\n Modify as content requires.\n ========================================================================== */\n/* ==========================================================================\n Print styles.\n Inlined to avoid the additional HTTP request:\n http://www.phpied.com/delay-loading-your-print-css/\n ========================================================================== */\n@media print {\n *,\n *:before,\n *:after,\n *:first-letter,\n *:first-line {\n background: transparent !important;\n color: #000 !important;\n /* Black prints faster: http://www.sanbeiji.com/archives/953 */\n box-shadow: none !important;\n text-shadow: none !important; }\n a,\n a:visited {\n text-decoration: underline; }\n a[href]:after {\n content: \" (\" attr(href) \")\"; }\n abbr[title]:after {\n content: \" (\" attr(title) \")\"; }\n /*\n * Don't show links that are fragment identifiers,\n * or use the `javascript:` pseudo protocol\n */\n a[href^=\"#\"]:after,\n a[href^=\"javascript:\"]:after {\n content: \"\"; }\n pre,\n blockquote {\n border: 1px solid #999;\n page-break-inside: avoid; }\n /*\n * Printing Tables:\n * http://css-discuss.incutio.com/wiki/Printing_Tables\n */\n thead {\n display: table-header-group; }\n tr,\n img {\n page-break-inside: avoid; }\n img {\n max-width: 100% !important; }\n p,\n h2,\n h3 {\n orphans: 3;\n widows: 3; }\n h2,\n h3 {\n page-break-after: avoid; } }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* Remove the unwanted box around FAB buttons */\n/* More info: http://goo.gl/IPwKi */\na, .mdl-accordion, .mdl-button, .mdl-card, .mdl-checkbox, .mdl-dropdown-menu,\n.mdl-icon-toggle, .mdl-item, .mdl-radio, .mdl-slider, .mdl-switch, .mdl-tabs__tab {\n -webkit-tap-highlight-color: transparent;\n -webkit-tap-highlight-color: rgba(255, 255, 255, 0); }\n\n/*\n * Make html take up the entire screen\n * Then set touch-action to avoid touch delay on mobile IE\n */\nhtml {\n width: 100%;\n height: 100%;\n -ms-touch-action: manipulation;\n touch-action: manipulation; }\n\n/*\n* Make body take up the entire screen\n* Remove body margin so layout containers don't cause extra overflow.\n*/\nbody {\n width: 100%;\n min-height: 100%;\n margin: 0; }\n\n/*\n * Main display reset for IE support.\n * Source: http://weblog.west-wind.com/posts/2015/Jan/12/main-HTML5-Tag-not-working-in-Internet-Explorer-91011\n */\nmain {\n display: block; }\n\n/*\n* Apply no display to elements with the hidden attribute.\n* IE 9 and 10 support.\n*/\n*[hidden] {\n display: none !important; }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* Typography */\n/* Shadows */\n/* Animations */\nhtml, body {\n font-family: \"Helvetica\", \"Arial\", sans-serif;\n font-size: 14px;\n font-weight: 400;\n line-height: 20px; }\n\nh1, h2, h3, h4, h5, h6, p {\n margin: 0;\n padding: 0; }\n\n/**\n * Styles for HTML elements\n */\nh1 small, h2 small, h3 small, h4 small, h5 small, h6 small {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 56px;\n font-weight: 400;\n line-height: 1.35;\n letter-spacing: -0.02em;\n opacity: 0.54;\n font-size: 0.6em; }\n\nh1 {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 56px;\n font-weight: 400;\n line-height: 1.35;\n letter-spacing: -0.02em;\n margin-top: 24px;\n margin-bottom: 24px; }\n\nh2 {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 45px;\n font-weight: 400;\n line-height: 48px;\n margin-top: 24px;\n margin-bottom: 24px; }\n\nh3 {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 34px;\n font-weight: 400;\n line-height: 40px;\n margin-top: 24px;\n margin-bottom: 24px; }\n\nh4 {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 24px;\n font-weight: 400;\n line-height: 32px;\n -moz-osx-font-smoothing: grayscale;\n margin-top: 24px;\n margin-bottom: 16px; }\n\nh5 {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 20px;\n font-weight: 500;\n line-height: 1;\n letter-spacing: 0.02em;\n margin-top: 24px;\n margin-bottom: 16px; }\n\nh6 {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 16px;\n font-weight: 400;\n line-height: 24px;\n letter-spacing: 0.04em;\n margin-top: 24px;\n margin-bottom: 16px; }\n\np {\n font-size: 14px;\n font-weight: 400;\n line-height: 24px;\n letter-spacing: 0;\n margin-bottom: 16px; }\n\na {\n color: rgb(255,64,129);\n font-weight: 500; }\n\nblockquote {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n position: relative;\n font-size: 24px;\n font-weight: 300;\n font-style: italic;\n line-height: 1.35;\n letter-spacing: 0.08em; }\n blockquote:before {\n position: absolute;\n left: -0.5em;\n content: '“'; }\n blockquote:after {\n content: '”';\n margin-left: -0.05em; }\n\nmark {\n background-color: #f4ff81; }\n\ndt {\n font-weight: 700; }\n\naddress {\n font-size: 12px;\n font-weight: 400;\n line-height: 1;\n letter-spacing: 0;\n font-style: normal; }\n\nul, ol {\n font-size: 14px;\n font-weight: 400;\n line-height: 24px;\n letter-spacing: 0; }\n\n/**\n * Class Name Styles\n */\n.mdl-typography--display-4 {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 112px;\n font-weight: 300;\n line-height: 1;\n letter-spacing: -0.04em; }\n\n.mdl-typography--display-4-color-contrast {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 112px;\n font-weight: 300;\n line-height: 1;\n letter-spacing: -0.04em;\n opacity: 0.54; }\n\n.mdl-typography--display-3 {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 56px;\n font-weight: 400;\n line-height: 1.35;\n letter-spacing: -0.02em; }\n\n.mdl-typography--display-3-color-contrast {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 56px;\n font-weight: 400;\n line-height: 1.35;\n letter-spacing: -0.02em;\n opacity: 0.54; }\n\n.mdl-typography--display-2 {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 45px;\n font-weight: 400;\n line-height: 48px; }\n\n.mdl-typography--display-2-color-contrast {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 45px;\n font-weight: 400;\n line-height: 48px;\n opacity: 0.54; }\n\n.mdl-typography--display-1 {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 34px;\n font-weight: 400;\n line-height: 40px; }\n\n.mdl-typography--display-1-color-contrast {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 34px;\n font-weight: 400;\n line-height: 40px;\n opacity: 0.54; }\n\n.mdl-typography--headline {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 24px;\n font-weight: 400;\n line-height: 32px;\n -moz-osx-font-smoothing: grayscale; }\n\n.mdl-typography--headline-color-contrast {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 24px;\n font-weight: 400;\n line-height: 32px;\n -moz-osx-font-smoothing: grayscale;\n opacity: 0.87; }\n\n.mdl-typography--title {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 20px;\n font-weight: 500;\n line-height: 1;\n letter-spacing: 0.02em; }\n\n.mdl-typography--title-color-contrast {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 20px;\n font-weight: 500;\n line-height: 1;\n letter-spacing: 0.02em;\n opacity: 0.87; }\n\n.mdl-typography--subhead {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 16px;\n font-weight: 400;\n line-height: 24px;\n letter-spacing: 0.04em; }\n\n.mdl-typography--subhead-color-contrast {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 16px;\n font-weight: 400;\n line-height: 24px;\n letter-spacing: 0.04em;\n opacity: 0.87; }\n\n.mdl-typography--body-2 {\n font-size: 14px;\n font-weight: bold;\n line-height: 24px;\n letter-spacing: 0; }\n\n.mdl-typography--body-2-color-contrast {\n font-size: 14px;\n font-weight: bold;\n line-height: 24px;\n letter-spacing: 0;\n opacity: 0.87; }\n\n.mdl-typography--body-1 {\n font-size: 14px;\n font-weight: 400;\n line-height: 24px;\n letter-spacing: 0; }\n\n.mdl-typography--body-1-color-contrast {\n font-size: 14px;\n font-weight: 400;\n line-height: 24px;\n letter-spacing: 0;\n opacity: 0.87; }\n\n.mdl-typography--body-2-force-preferred-font {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 14px;\n font-weight: 500;\n line-height: 24px;\n letter-spacing: 0; }\n\n.mdl-typography--body-2-force-preferred-font-color-contrast {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 14px;\n font-weight: 500;\n line-height: 24px;\n letter-spacing: 0;\n opacity: 0.87; }\n\n.mdl-typography--body-1-force-preferred-font {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 14px;\n font-weight: 400;\n line-height: 24px;\n letter-spacing: 0; }\n\n.mdl-typography--body-1-force-preferred-font-color-contrast {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 14px;\n font-weight: 400;\n line-height: 24px;\n letter-spacing: 0;\n opacity: 0.87; }\n\n.mdl-typography--caption {\n font-size: 12px;\n font-weight: 400;\n line-height: 1;\n letter-spacing: 0; }\n\n.mdl-typography--caption-force-preferred-font {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 12px;\n font-weight: 400;\n line-height: 1;\n letter-spacing: 0; }\n\n.mdl-typography--caption-color-contrast {\n font-size: 12px;\n font-weight: 400;\n line-height: 1;\n letter-spacing: 0;\n opacity: 0.54; }\n\n.mdl-typography--caption-force-preferred-font-color-contrast {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 12px;\n font-weight: 400;\n line-height: 1;\n letter-spacing: 0;\n opacity: 0.54; }\n\n.mdl-typography--menu {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 14px;\n font-weight: 500;\n line-height: 1;\n letter-spacing: 0; }\n\n.mdl-typography--menu-color-contrast {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 14px;\n font-weight: 500;\n line-height: 1;\n letter-spacing: 0;\n opacity: 0.87; }\n\n.mdl-typography--button {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 14px;\n font-weight: 500;\n text-transform: uppercase;\n line-height: 1;\n letter-spacing: 0; }\n\n.mdl-typography--button-color-contrast {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 14px;\n font-weight: 500;\n text-transform: uppercase;\n line-height: 1;\n letter-spacing: 0;\n opacity: 0.87; }\n\n.mdl-typography--text-left {\n text-align: left; }\n\n.mdl-typography--text-right {\n text-align: right; }\n\n.mdl-typography--text-center {\n text-align: center; }\n\n.mdl-typography--text-justify {\n text-align: justify; }\n\n.mdl-typography--text-nowrap {\n white-space: nowrap; }\n\n.mdl-typography--text-lowercase {\n text-transform: lowercase; }\n\n.mdl-typography--text-uppercase {\n text-transform: uppercase; }\n\n.mdl-typography--text-capitalize {\n text-transform: capitalize; }\n\n.mdl-typography--font-thin {\n font-weight: 200 !important; }\n\n.mdl-typography--font-light {\n font-weight: 300 !important; }\n\n.mdl-typography--font-regular {\n font-weight: 400 !important; }\n\n.mdl-typography--font-medium {\n font-weight: 500 !important; }\n\n.mdl-typography--font-bold {\n font-weight: 700 !important; }\n\n.mdl-typography--font-black {\n font-weight: 900 !important; }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n.mdl-color-text--red {\n color: rgb(244,67,54) !important; }\n\n.mdl-color--red {\n background-color: rgb(244,67,54) !important; }\n\n.mdl-color-text--red-50 {\n color: rgb(255,235,238) !important; }\n\n.mdl-color--red-50 {\n background-color: rgb(255,235,238) !important; }\n\n.mdl-color-text--red-100 {\n color: rgb(255,205,210) !important; }\n\n.mdl-color--red-100 {\n background-color: rgb(255,205,210) !important; }\n\n.mdl-color-text--red-200 {\n color: rgb(239,154,154) !important; }\n\n.mdl-color--red-200 {\n background-color: rgb(239,154,154) !important; }\n\n.mdl-color-text--red-300 {\n color: rgb(229,115,115) !important; }\n\n.mdl-color--red-300 {\n background-color: rgb(229,115,115) !important; }\n\n.mdl-color-text--red-400 {\n color: rgb(239,83,80) !important; }\n\n.mdl-color--red-400 {\n background-color: rgb(239,83,80) !important; }\n\n.mdl-color-text--red-500 {\n color: rgb(244,67,54) !important; }\n\n.mdl-color--red-500 {\n background-color: rgb(244,67,54) !important; }\n\n.mdl-color-text--red-600 {\n color: rgb(229,57,53) !important; }\n\n.mdl-color--red-600 {\n background-color: rgb(229,57,53) !important; }\n\n.mdl-color-text--red-700 {\n color: rgb(211,47,47) !important; }\n\n.mdl-color--red-700 {\n background-color: rgb(211,47,47) !important; }\n\n.mdl-color-text--red-800 {\n color: rgb(198,40,40) !important; }\n\n.mdl-color--red-800 {\n background-color: rgb(198,40,40) !important; }\n\n.mdl-color-text--red-900 {\n color: rgb(183,28,28) !important; }\n\n.mdl-color--red-900 {\n background-color: rgb(183,28,28) !important; }\n\n.mdl-color-text--red-A100 {\n color: rgb(255,138,128) !important; }\n\n.mdl-color--red-A100 {\n background-color: rgb(255,138,128) !important; }\n\n.mdl-color-text--red-A200 {\n color: rgb(255,82,82) !important; }\n\n.mdl-color--red-A200 {\n background-color: rgb(255,82,82) !important; }\n\n.mdl-color-text--red-A400 {\n color: rgb(255,23,68) !important; }\n\n.mdl-color--red-A400 {\n background-color: rgb(255,23,68) !important; }\n\n.mdl-color-text--red-A700 {\n color: rgb(213,0,0) !important; }\n\n.mdl-color--red-A700 {\n background-color: rgb(213,0,0) !important; }\n\n.mdl-color-text--pink {\n color: rgb(233,30,99) !important; }\n\n.mdl-color--pink {\n background-color: rgb(233,30,99) !important; }\n\n.mdl-color-text--pink-50 {\n color: rgb(252,228,236) !important; }\n\n.mdl-color--pink-50 {\n background-color: rgb(252,228,236) !important; }\n\n.mdl-color-text--pink-100 {\n color: rgb(248,187,208) !important; }\n\n.mdl-color--pink-100 {\n background-color: rgb(248,187,208) !important; }\n\n.mdl-color-text--pink-200 {\n color: rgb(244,143,177) !important; }\n\n.mdl-color--pink-200 {\n background-color: rgb(244,143,177) !important; }\n\n.mdl-color-text--pink-300 {\n color: rgb(240,98,146) !important; }\n\n.mdl-color--pink-300 {\n background-color: rgb(240,98,146) !important; }\n\n.mdl-color-text--pink-400 {\n color: rgb(236,64,122) !important; }\n\n.mdl-color--pink-400 {\n background-color: rgb(236,64,122) !important; }\n\n.mdl-color-text--pink-500 {\n color: rgb(233,30,99) !important; }\n\n.mdl-color--pink-500 {\n background-color: rgb(233,30,99) !important; }\n\n.mdl-color-text--pink-600 {\n color: rgb(216,27,96) !important; }\n\n.mdl-color--pink-600 {\n background-color: rgb(216,27,96) !important; }\n\n.mdl-color-text--pink-700 {\n color: rgb(194,24,91) !important; }\n\n.mdl-color--pink-700 {\n background-color: rgb(194,24,91) !important; }\n\n.mdl-color-text--pink-800 {\n color: rgb(173,20,87) !important; }\n\n.mdl-color--pink-800 {\n background-color: rgb(173,20,87) !important; }\n\n.mdl-color-text--pink-900 {\n color: rgb(136,14,79) !important; }\n\n.mdl-color--pink-900 {\n background-color: rgb(136,14,79) !important; }\n\n.mdl-color-text--pink-A100 {\n color: rgb(255,128,171) !important; }\n\n.mdl-color--pink-A100 {\n background-color: rgb(255,128,171) !important; }\n\n.mdl-color-text--pink-A200 {\n color: rgb(255,64,129) !important; }\n\n.mdl-color--pink-A200 {\n background-color: rgb(255,64,129) !important; }\n\n.mdl-color-text--pink-A400 {\n color: rgb(245,0,87) !important; }\n\n.mdl-color--pink-A400 {\n background-color: rgb(245,0,87) !important; }\n\n.mdl-color-text--pink-A700 {\n color: rgb(197,17,98) !important; }\n\n.mdl-color--pink-A700 {\n background-color: rgb(197,17,98) !important; }\n\n.mdl-color-text--purple {\n color: rgb(156,39,176) !important; }\n\n.mdl-color--purple {\n background-color: rgb(156,39,176) !important; }\n\n.mdl-color-text--purple-50 {\n color: rgb(243,229,245) !important; }\n\n.mdl-color--purple-50 {\n background-color: rgb(243,229,245) !important; }\n\n.mdl-color-text--purple-100 {\n color: rgb(225,190,231) !important; }\n\n.mdl-color--purple-100 {\n background-color: rgb(225,190,231) !important; }\n\n.mdl-color-text--purple-200 {\n color: rgb(206,147,216) !important; }\n\n.mdl-color--purple-200 {\n background-color: rgb(206,147,216) !important; }\n\n.mdl-color-text--purple-300 {\n color: rgb(186,104,200) !important; }\n\n.mdl-color--purple-300 {\n background-color: rgb(186,104,200) !important; }\n\n.mdl-color-text--purple-400 {\n color: rgb(171,71,188) !important; }\n\n.mdl-color--purple-400 {\n background-color: rgb(171,71,188) !important; }\n\n.mdl-color-text--purple-500 {\n color: rgb(156,39,176) !important; }\n\n.mdl-color--purple-500 {\n background-color: rgb(156,39,176) !important; }\n\n.mdl-color-text--purple-600 {\n color: rgb(142,36,170) !important; }\n\n.mdl-color--purple-600 {\n background-color: rgb(142,36,170) !important; }\n\n.mdl-color-text--purple-700 {\n color: rgb(123,31,162) !important; }\n\n.mdl-color--purple-700 {\n background-color: rgb(123,31,162) !important; }\n\n.mdl-color-text--purple-800 {\n color: rgb(106,27,154) !important; }\n\n.mdl-color--purple-800 {\n background-color: rgb(106,27,154) !important; }\n\n.mdl-color-text--purple-900 {\n color: rgb(74,20,140) !important; }\n\n.mdl-color--purple-900 {\n background-color: rgb(74,20,140) !important; }\n\n.mdl-color-text--purple-A100 {\n color: rgb(234,128,252) !important; }\n\n.mdl-color--purple-A100 {\n background-color: rgb(234,128,252) !important; }\n\n.mdl-color-text--purple-A200 {\n color: rgb(224,64,251) !important; }\n\n.mdl-color--purple-A200 {\n background-color: rgb(224,64,251) !important; }\n\n.mdl-color-text--purple-A400 {\n color: rgb(213,0,249) !important; }\n\n.mdl-color--purple-A400 {\n background-color: rgb(213,0,249) !important; }\n\n.mdl-color-text--purple-A700 {\n color: rgb(170,0,255) !important; }\n\n.mdl-color--purple-A700 {\n background-color: rgb(170,0,255) !important; }\n\n.mdl-color-text--deep-purple {\n color: rgb(103,58,183) !important; }\n\n.mdl-color--deep-purple {\n background-color: rgb(103,58,183) !important; }\n\n.mdl-color-text--deep-purple-50 {\n color: rgb(237,231,246) !important; }\n\n.mdl-color--deep-purple-50 {\n background-color: rgb(237,231,246) !important; }\n\n.mdl-color-text--deep-purple-100 {\n color: rgb(209,196,233) !important; }\n\n.mdl-color--deep-purple-100 {\n background-color: rgb(209,196,233) !important; }\n\n.mdl-color-text--deep-purple-200 {\n color: rgb(179,157,219) !important; }\n\n.mdl-color--deep-purple-200 {\n background-color: rgb(179,157,219) !important; }\n\n.mdl-color-text--deep-purple-300 {\n color: rgb(149,117,205) !important; }\n\n.mdl-color--deep-purple-300 {\n background-color: rgb(149,117,205) !important; }\n\n.mdl-color-text--deep-purple-400 {\n color: rgb(126,87,194) !important; }\n\n.mdl-color--deep-purple-400 {\n background-color: rgb(126,87,194) !important; }\n\n.mdl-color-text--deep-purple-500 {\n color: rgb(103,58,183) !important; }\n\n.mdl-color--deep-purple-500 {\n background-color: rgb(103,58,183) !important; }\n\n.mdl-color-text--deep-purple-600 {\n color: rgb(94,53,177) !important; }\n\n.mdl-color--deep-purple-600 {\n background-color: rgb(94,53,177) !important; }\n\n.mdl-color-text--deep-purple-700 {\n color: rgb(81,45,168) !important; }\n\n.mdl-color--deep-purple-700 {\n background-color: rgb(81,45,168) !important; }\n\n.mdl-color-text--deep-purple-800 {\n color: rgb(69,39,160) !important; }\n\n.mdl-color--deep-purple-800 {\n background-color: rgb(69,39,160) !important; }\n\n.mdl-color-text--deep-purple-900 {\n color: rgb(49,27,146) !important; }\n\n.mdl-color--deep-purple-900 {\n background-color: rgb(49,27,146) !important; }\n\n.mdl-color-text--deep-purple-A100 {\n color: rgb(179,136,255) !important; }\n\n.mdl-color--deep-purple-A100 {\n background-color: rgb(179,136,255) !important; }\n\n.mdl-color-text--deep-purple-A200 {\n color: rgb(124,77,255) !important; }\n\n.mdl-color--deep-purple-A200 {\n background-color: rgb(124,77,255) !important; }\n\n.mdl-color-text--deep-purple-A400 {\n color: rgb(101,31,255) !important; }\n\n.mdl-color--deep-purple-A400 {\n background-color: rgb(101,31,255) !important; }\n\n.mdl-color-text--deep-purple-A700 {\n color: rgb(98,0,234) !important; }\n\n.mdl-color--deep-purple-A700 {\n background-color: rgb(98,0,234) !important; }\n\n.mdl-color-text--indigo {\n color: rgb(63,81,181) !important; }\n\n.mdl-color--indigo {\n background-color: rgb(63,81,181) !important; }\n\n.mdl-color-text--indigo-50 {\n color: rgb(232,234,246) !important; }\n\n.mdl-color--indigo-50 {\n background-color: rgb(232,234,246) !important; }\n\n.mdl-color-text--indigo-100 {\n color: rgb(197,202,233) !important; }\n\n.mdl-color--indigo-100 {\n background-color: rgb(197,202,233) !important; }\n\n.mdl-color-text--indigo-200 {\n color: rgb(159,168,218) !important; }\n\n.mdl-color--indigo-200 {\n background-color: rgb(159,168,218) !important; }\n\n.mdl-color-text--indigo-300 {\n color: rgb(121,134,203) !important; }\n\n.mdl-color--indigo-300 {\n background-color: rgb(121,134,203) !important; }\n\n.mdl-color-text--indigo-400 {\n color: rgb(92,107,192) !important; }\n\n.mdl-color--indigo-400 {\n background-color: rgb(92,107,192) !important; }\n\n.mdl-color-text--indigo-500 {\n color: rgb(63,81,181) !important; }\n\n.mdl-color--indigo-500 {\n background-color: rgb(63,81,181) !important; }\n\n.mdl-color-text--indigo-600 {\n color: rgb(57,73,171) !important; }\n\n.mdl-color--indigo-600 {\n background-color: rgb(57,73,171) !important; }\n\n.mdl-color-text--indigo-700 {\n color: rgb(48,63,159) !important; }\n\n.mdl-color--indigo-700 {\n background-color: rgb(48,63,159) !important; }\n\n.mdl-color-text--indigo-800 {\n color: rgb(40,53,147) !important; }\n\n.mdl-color--indigo-800 {\n background-color: rgb(40,53,147) !important; }\n\n.mdl-color-text--indigo-900 {\n color: rgb(26,35,126) !important; }\n\n.mdl-color--indigo-900 {\n background-color: rgb(26,35,126) !important; }\n\n.mdl-color-text--indigo-A100 {\n color: rgb(140,158,255) !important; }\n\n.mdl-color--indigo-A100 {\n background-color: rgb(140,158,255) !important; }\n\n.mdl-color-text--indigo-A200 {\n color: rgb(83,109,254) !important; }\n\n.mdl-color--indigo-A200 {\n background-color: rgb(83,109,254) !important; }\n\n.mdl-color-text--indigo-A400 {\n color: rgb(61,90,254) !important; }\n\n.mdl-color--indigo-A400 {\n background-color: rgb(61,90,254) !important; }\n\n.mdl-color-text--indigo-A700 {\n color: rgb(48,79,254) !important; }\n\n.mdl-color--indigo-A700 {\n background-color: rgb(48,79,254) !important; }\n\n.mdl-color-text--blue {\n color: rgb(33,150,243) !important; }\n\n.mdl-color--blue {\n background-color: rgb(33,150,243) !important; }\n\n.mdl-color-text--blue-50 {\n color: rgb(227,242,253) !important; }\n\n.mdl-color--blue-50 {\n background-color: rgb(227,242,253) !important; }\n\n.mdl-color-text--blue-100 {\n color: rgb(187,222,251) !important; }\n\n.mdl-color--blue-100 {\n background-color: rgb(187,222,251) !important; }\n\n.mdl-color-text--blue-200 {\n color: rgb(144,202,249) !important; }\n\n.mdl-color--blue-200 {\n background-color: rgb(144,202,249) !important; }\n\n.mdl-color-text--blue-300 {\n color: rgb(100,181,246) !important; }\n\n.mdl-color--blue-300 {\n background-color: rgb(100,181,246) !important; }\n\n.mdl-color-text--blue-400 {\n color: rgb(66,165,245) !important; }\n\n.mdl-color--blue-400 {\n background-color: rgb(66,165,245) !important; }\n\n.mdl-color-text--blue-500 {\n color: rgb(33,150,243) !important; }\n\n.mdl-color--blue-500 {\n background-color: rgb(33,150,243) !important; }\n\n.mdl-color-text--blue-600 {\n color: rgb(30,136,229) !important; }\n\n.mdl-color--blue-600 {\n background-color: rgb(30,136,229) !important; }\n\n.mdl-color-text--blue-700 {\n color: rgb(25,118,210) !important; }\n\n.mdl-color--blue-700 {\n background-color: rgb(25,118,210) !important; }\n\n.mdl-color-text--blue-800 {\n color: rgb(21,101,192) !important; }\n\n.mdl-color--blue-800 {\n background-color: rgb(21,101,192) !important; }\n\n.mdl-color-text--blue-900 {\n color: rgb(13,71,161) !important; }\n\n.mdl-color--blue-900 {\n background-color: rgb(13,71,161) !important; }\n\n.mdl-color-text--blue-A100 {\n color: rgb(130,177,255) !important; }\n\n.mdl-color--blue-A100 {\n background-color: rgb(130,177,255) !important; }\n\n.mdl-color-text--blue-A200 {\n color: rgb(68,138,255) !important; }\n\n.mdl-color--blue-A200 {\n background-color: rgb(68,138,255) !important; }\n\n.mdl-color-text--blue-A400 {\n color: rgb(41,121,255) !important; }\n\n.mdl-color--blue-A400 {\n background-color: rgb(41,121,255) !important; }\n\n.mdl-color-text--blue-A700 {\n color: rgb(41,98,255) !important; }\n\n.mdl-color--blue-A700 {\n background-color: rgb(41,98,255) !important; }\n\n.mdl-color-text--light-blue {\n color: rgb(3,169,244) !important; }\n\n.mdl-color--light-blue {\n background-color: rgb(3,169,244) !important; }\n\n.mdl-color-text--light-blue-50 {\n color: rgb(225,245,254) !important; }\n\n.mdl-color--light-blue-50 {\n background-color: rgb(225,245,254) !important; }\n\n.mdl-color-text--light-blue-100 {\n color: rgb(179,229,252) !important; }\n\n.mdl-color--light-blue-100 {\n background-color: rgb(179,229,252) !important; }\n\n.mdl-color-text--light-blue-200 {\n color: rgb(129,212,250) !important; }\n\n.mdl-color--light-blue-200 {\n background-color: rgb(129,212,250) !important; }\n\n.mdl-color-text--light-blue-300 {\n color: rgb(79,195,247) !important; }\n\n.mdl-color--light-blue-300 {\n background-color: rgb(79,195,247) !important; }\n\n.mdl-color-text--light-blue-400 {\n color: rgb(41,182,246) !important; }\n\n.mdl-color--light-blue-400 {\n background-color: rgb(41,182,246) !important; }\n\n.mdl-color-text--light-blue-500 {\n color: rgb(3,169,244) !important; }\n\n.mdl-color--light-blue-500 {\n background-color: rgb(3,169,244) !important; }\n\n.mdl-color-text--light-blue-600 {\n color: rgb(3,155,229) !important; }\n\n.mdl-color--light-blue-600 {\n background-color: rgb(3,155,229) !important; }\n\n.mdl-color-text--light-blue-700 {\n color: rgb(2,136,209) !important; }\n\n.mdl-color--light-blue-700 {\n background-color: rgb(2,136,209) !important; }\n\n.mdl-color-text--light-blue-800 {\n color: rgb(2,119,189) !important; }\n\n.mdl-color--light-blue-800 {\n background-color: rgb(2,119,189) !important; }\n\n.mdl-color-text--light-blue-900 {\n color: rgb(1,87,155) !important; }\n\n.mdl-color--light-blue-900 {\n background-color: rgb(1,87,155) !important; }\n\n.mdl-color-text--light-blue-A100 {\n color: rgb(128,216,255) !important; }\n\n.mdl-color--light-blue-A100 {\n background-color: rgb(128,216,255) !important; }\n\n.mdl-color-text--light-blue-A200 {\n color: rgb(64,196,255) !important; }\n\n.mdl-color--light-blue-A200 {\n background-color: rgb(64,196,255) !important; }\n\n.mdl-color-text--light-blue-A400 {\n color: rgb(0,176,255) !important; }\n\n.mdl-color--light-blue-A400 {\n background-color: rgb(0,176,255) !important; }\n\n.mdl-color-text--light-blue-A700 {\n color: rgb(0,145,234) !important; }\n\n.mdl-color--light-blue-A700 {\n background-color: rgb(0,145,234) !important; }\n\n.mdl-color-text--cyan {\n color: rgb(0,188,212) !important; }\n\n.mdl-color--cyan {\n background-color: rgb(0,188,212) !important; }\n\n.mdl-color-text--cyan-50 {\n color: rgb(224,247,250) !important; }\n\n.mdl-color--cyan-50 {\n background-color: rgb(224,247,250) !important; }\n\n.mdl-color-text--cyan-100 {\n color: rgb(178,235,242) !important; }\n\n.mdl-color--cyan-100 {\n background-color: rgb(178,235,242) !important; }\n\n.mdl-color-text--cyan-200 {\n color: rgb(128,222,234) !important; }\n\n.mdl-color--cyan-200 {\n background-color: rgb(128,222,234) !important; }\n\n.mdl-color-text--cyan-300 {\n color: rgb(77,208,225) !important; }\n\n.mdl-color--cyan-300 {\n background-color: rgb(77,208,225) !important; }\n\n.mdl-color-text--cyan-400 {\n color: rgb(38,198,218) !important; }\n\n.mdl-color--cyan-400 {\n background-color: rgb(38,198,218) !important; }\n\n.mdl-color-text--cyan-500 {\n color: rgb(0,188,212) !important; }\n\n.mdl-color--cyan-500 {\n background-color: rgb(0,188,212) !important; }\n\n.mdl-color-text--cyan-600 {\n color: rgb(0,172,193) !important; }\n\n.mdl-color--cyan-600 {\n background-color: rgb(0,172,193) !important; }\n\n.mdl-color-text--cyan-700 {\n color: rgb(0,151,167) !important; }\n\n.mdl-color--cyan-700 {\n background-color: rgb(0,151,167) !important; }\n\n.mdl-color-text--cyan-800 {\n color: rgb(0,131,143) !important; }\n\n.mdl-color--cyan-800 {\n background-color: rgb(0,131,143) !important; }\n\n.mdl-color-text--cyan-900 {\n color: rgb(0,96,100) !important; }\n\n.mdl-color--cyan-900 {\n background-color: rgb(0,96,100) !important; }\n\n.mdl-color-text--cyan-A100 {\n color: rgb(132,255,255) !important; }\n\n.mdl-color--cyan-A100 {\n background-color: rgb(132,255,255) !important; }\n\n.mdl-color-text--cyan-A200 {\n color: rgb(24,255,255) !important; }\n\n.mdl-color--cyan-A200 {\n background-color: rgb(24,255,255) !important; }\n\n.mdl-color-text--cyan-A400 {\n color: rgb(0,229,255) !important; }\n\n.mdl-color--cyan-A400 {\n background-color: rgb(0,229,255) !important; }\n\n.mdl-color-text--cyan-A700 {\n color: rgb(0,184,212) !important; }\n\n.mdl-color--cyan-A700 {\n background-color: rgb(0,184,212) !important; }\n\n.mdl-color-text--teal {\n color: rgb(0,150,136) !important; }\n\n.mdl-color--teal {\n background-color: rgb(0,150,136) !important; }\n\n.mdl-color-text--teal-50 {\n color: rgb(224,242,241) !important; }\n\n.mdl-color--teal-50 {\n background-color: rgb(224,242,241) !important; }\n\n.mdl-color-text--teal-100 {\n color: rgb(178,223,219) !important; }\n\n.mdl-color--teal-100 {\n background-color: rgb(178,223,219) !important; }\n\n.mdl-color-text--teal-200 {\n color: rgb(128,203,196) !important; }\n\n.mdl-color--teal-200 {\n background-color: rgb(128,203,196) !important; }\n\n.mdl-color-text--teal-300 {\n color: rgb(77,182,172) !important; }\n\n.mdl-color--teal-300 {\n background-color: rgb(77,182,172) !important; }\n\n.mdl-color-text--teal-400 {\n color: rgb(38,166,154) !important; }\n\n.mdl-color--teal-400 {\n background-color: rgb(38,166,154) !important; }\n\n.mdl-color-text--teal-500 {\n color: rgb(0,150,136) !important; }\n\n.mdl-color--teal-500 {\n background-color: rgb(0,150,136) !important; }\n\n.mdl-color-text--teal-600 {\n color: rgb(0,137,123) !important; }\n\n.mdl-color--teal-600 {\n background-color: rgb(0,137,123) !important; }\n\n.mdl-color-text--teal-700 {\n color: rgb(0,121,107) !important; }\n\n.mdl-color--teal-700 {\n background-color: rgb(0,121,107) !important; }\n\n.mdl-color-text--teal-800 {\n color: rgb(0,105,92) !important; }\n\n.mdl-color--teal-800 {\n background-color: rgb(0,105,92) !important; }\n\n.mdl-color-text--teal-900 {\n color: rgb(0,77,64) !important; }\n\n.mdl-color--teal-900 {\n background-color: rgb(0,77,64) !important; }\n\n.mdl-color-text--teal-A100 {\n color: rgb(167,255,235) !important; }\n\n.mdl-color--teal-A100 {\n background-color: rgb(167,255,235) !important; }\n\n.mdl-color-text--teal-A200 {\n color: rgb(100,255,218) !important; }\n\n.mdl-color--teal-A200 {\n background-color: rgb(100,255,218) !important; }\n\n.mdl-color-text--teal-A400 {\n color: rgb(29,233,182) !important; }\n\n.mdl-color--teal-A400 {\n background-color: rgb(29,233,182) !important; }\n\n.mdl-color-text--teal-A700 {\n color: rgb(0,191,165) !important; }\n\n.mdl-color--teal-A700 {\n background-color: rgb(0,191,165) !important; }\n\n.mdl-color-text--green {\n color: rgb(76,175,80) !important; }\n\n.mdl-color--green {\n background-color: rgb(76,175,80) !important; }\n\n.mdl-color-text--green-50 {\n color: rgb(232,245,233) !important; }\n\n.mdl-color--green-50 {\n background-color: rgb(232,245,233) !important; }\n\n.mdl-color-text--green-100 {\n color: rgb(200,230,201) !important; }\n\n.mdl-color--green-100 {\n background-color: rgb(200,230,201) !important; }\n\n.mdl-color-text--green-200 {\n color: rgb(165,214,167) !important; }\n\n.mdl-color--green-200 {\n background-color: rgb(165,214,167) !important; }\n\n.mdl-color-text--green-300 {\n color: rgb(129,199,132) !important; }\n\n.mdl-color--green-300 {\n background-color: rgb(129,199,132) !important; }\n\n.mdl-color-text--green-400 {\n color: rgb(102,187,106) !important; }\n\n.mdl-color--green-400 {\n background-color: rgb(102,187,106) !important; }\n\n.mdl-color-text--green-500 {\n color: rgb(76,175,80) !important; }\n\n.mdl-color--green-500 {\n background-color: rgb(76,175,80) !important; }\n\n.mdl-color-text--green-600 {\n color: rgb(67,160,71) !important; }\n\n.mdl-color--green-600 {\n background-color: rgb(67,160,71) !important; }\n\n.mdl-color-text--green-700 {\n color: rgb(56,142,60) !important; }\n\n.mdl-color--green-700 {\n background-color: rgb(56,142,60) !important; }\n\n.mdl-color-text--green-800 {\n color: rgb(46,125,50) !important; }\n\n.mdl-color--green-800 {\n background-color: rgb(46,125,50) !important; }\n\n.mdl-color-text--green-900 {\n color: rgb(27,94,32) !important; }\n\n.mdl-color--green-900 {\n background-color: rgb(27,94,32) !important; }\n\n.mdl-color-text--green-A100 {\n color: rgb(185,246,202) !important; }\n\n.mdl-color--green-A100 {\n background-color: rgb(185,246,202) !important; }\n\n.mdl-color-text--green-A200 {\n color: rgb(105,240,174) !important; }\n\n.mdl-color--green-A200 {\n background-color: rgb(105,240,174) !important; }\n\n.mdl-color-text--green-A400 {\n color: rgb(0,230,118) !important; }\n\n.mdl-color--green-A400 {\n background-color: rgb(0,230,118) !important; }\n\n.mdl-color-text--green-A700 {\n color: rgb(0,200,83) !important; }\n\n.mdl-color--green-A700 {\n background-color: rgb(0,200,83) !important; }\n\n.mdl-color-text--light-green {\n color: rgb(139,195,74) !important; }\n\n.mdl-color--light-green {\n background-color: rgb(139,195,74) !important; }\n\n.mdl-color-text--light-green-50 {\n color: rgb(241,248,233) !important; }\n\n.mdl-color--light-green-50 {\n background-color: rgb(241,248,233) !important; }\n\n.mdl-color-text--light-green-100 {\n color: rgb(220,237,200) !important; }\n\n.mdl-color--light-green-100 {\n background-color: rgb(220,237,200) !important; }\n\n.mdl-color-text--light-green-200 {\n color: rgb(197,225,165) !important; }\n\n.mdl-color--light-green-200 {\n background-color: rgb(197,225,165) !important; }\n\n.mdl-color-text--light-green-300 {\n color: rgb(174,213,129) !important; }\n\n.mdl-color--light-green-300 {\n background-color: rgb(174,213,129) !important; }\n\n.mdl-color-text--light-green-400 {\n color: rgb(156,204,101) !important; }\n\n.mdl-color--light-green-400 {\n background-color: rgb(156,204,101) !important; }\n\n.mdl-color-text--light-green-500 {\n color: rgb(139,195,74) !important; }\n\n.mdl-color--light-green-500 {\n background-color: rgb(139,195,74) !important; }\n\n.mdl-color-text--light-green-600 {\n color: rgb(124,179,66) !important; }\n\n.mdl-color--light-green-600 {\n background-color: rgb(124,179,66) !important; }\n\n.mdl-color-text--light-green-700 {\n color: rgb(104,159,56) !important; }\n\n.mdl-color--light-green-700 {\n background-color: rgb(104,159,56) !important; }\n\n.mdl-color-text--light-green-800 {\n color: rgb(85,139,47) !important; }\n\n.mdl-color--light-green-800 {\n background-color: rgb(85,139,47) !important; }\n\n.mdl-color-text--light-green-900 {\n color: rgb(51,105,30) !important; }\n\n.mdl-color--light-green-900 {\n background-color: rgb(51,105,30) !important; }\n\n.mdl-color-text--light-green-A100 {\n color: rgb(204,255,144) !important; }\n\n.mdl-color--light-green-A100 {\n background-color: rgb(204,255,144) !important; }\n\n.mdl-color-text--light-green-A200 {\n color: rgb(178,255,89) !important; }\n\n.mdl-color--light-green-A200 {\n background-color: rgb(178,255,89) !important; }\n\n.mdl-color-text--light-green-A400 {\n color: rgb(118,255,3) !important; }\n\n.mdl-color--light-green-A400 {\n background-color: rgb(118,255,3) !important; }\n\n.mdl-color-text--light-green-A700 {\n color: rgb(100,221,23) !important; }\n\n.mdl-color--light-green-A700 {\n background-color: rgb(100,221,23) !important; }\n\n.mdl-color-text--lime {\n color: rgb(205,220,57) !important; }\n\n.mdl-color--lime {\n background-color: rgb(205,220,57) !important; }\n\n.mdl-color-text--lime-50 {\n color: rgb(249,251,231) !important; }\n\n.mdl-color--lime-50 {\n background-color: rgb(249,251,231) !important; }\n\n.mdl-color-text--lime-100 {\n color: rgb(240,244,195) !important; }\n\n.mdl-color--lime-100 {\n background-color: rgb(240,244,195) !important; }\n\n.mdl-color-text--lime-200 {\n color: rgb(230,238,156) !important; }\n\n.mdl-color--lime-200 {\n background-color: rgb(230,238,156) !important; }\n\n.mdl-color-text--lime-300 {\n color: rgb(220,231,117) !important; }\n\n.mdl-color--lime-300 {\n background-color: rgb(220,231,117) !important; }\n\n.mdl-color-text--lime-400 {\n color: rgb(212,225,87) !important; }\n\n.mdl-color--lime-400 {\n background-color: rgb(212,225,87) !important; }\n\n.mdl-color-text--lime-500 {\n color: rgb(205,220,57) !important; }\n\n.mdl-color--lime-500 {\n background-color: rgb(205,220,57) !important; }\n\n.mdl-color-text--lime-600 {\n color: rgb(192,202,51) !important; }\n\n.mdl-color--lime-600 {\n background-color: rgb(192,202,51) !important; }\n\n.mdl-color-text--lime-700 {\n color: rgb(175,180,43) !important; }\n\n.mdl-color--lime-700 {\n background-color: rgb(175,180,43) !important; }\n\n.mdl-color-text--lime-800 {\n color: rgb(158,157,36) !important; }\n\n.mdl-color--lime-800 {\n background-color: rgb(158,157,36) !important; }\n\n.mdl-color-text--lime-900 {\n color: rgb(130,119,23) !important; }\n\n.mdl-color--lime-900 {\n background-color: rgb(130,119,23) !important; }\n\n.mdl-color-text--lime-A100 {\n color: rgb(244,255,129) !important; }\n\n.mdl-color--lime-A100 {\n background-color: rgb(244,255,129) !important; }\n\n.mdl-color-text--lime-A200 {\n color: rgb(238,255,65) !important; }\n\n.mdl-color--lime-A200 {\n background-color: rgb(238,255,65) !important; }\n\n.mdl-color-text--lime-A400 {\n color: rgb(198,255,0) !important; }\n\n.mdl-color--lime-A400 {\n background-color: rgb(198,255,0) !important; }\n\n.mdl-color-text--lime-A700 {\n color: rgb(174,234,0) !important; }\n\n.mdl-color--lime-A700 {\n background-color: rgb(174,234,0) !important; }\n\n.mdl-color-text--yellow {\n color: rgb(255,235,59) !important; }\n\n.mdl-color--yellow {\n background-color: rgb(255,235,59) !important; }\n\n.mdl-color-text--yellow-50 {\n color: rgb(255,253,231) !important; }\n\n.mdl-color--yellow-50 {\n background-color: rgb(255,253,231) !important; }\n\n.mdl-color-text--yellow-100 {\n color: rgb(255,249,196) !important; }\n\n.mdl-color--yellow-100 {\n background-color: rgb(255,249,196) !important; }\n\n.mdl-color-text--yellow-200 {\n color: rgb(255,245,157) !important; }\n\n.mdl-color--yellow-200 {\n background-color: rgb(255,245,157) !important; }\n\n.mdl-color-text--yellow-300 {\n color: rgb(255,241,118) !important; }\n\n.mdl-color--yellow-300 {\n background-color: rgb(255,241,118) !important; }\n\n.mdl-color-text--yellow-400 {\n color: rgb(255,238,88) !important; }\n\n.mdl-color--yellow-400 {\n background-color: rgb(255,238,88) !important; }\n\n.mdl-color-text--yellow-500 {\n color: rgb(255,235,59) !important; }\n\n.mdl-color--yellow-500 {\n background-color: rgb(255,235,59) !important; }\n\n.mdl-color-text--yellow-600 {\n color: rgb(253,216,53) !important; }\n\n.mdl-color--yellow-600 {\n background-color: rgb(253,216,53) !important; }\n\n.mdl-color-text--yellow-700 {\n color: rgb(251,192,45) !important; }\n\n.mdl-color--yellow-700 {\n background-color: rgb(251,192,45) !important; }\n\n.mdl-color-text--yellow-800 {\n color: rgb(249,168,37) !important; }\n\n.mdl-color--yellow-800 {\n background-color: rgb(249,168,37) !important; }\n\n.mdl-color-text--yellow-900 {\n color: rgb(245,127,23) !important; }\n\n.mdl-color--yellow-900 {\n background-color: rgb(245,127,23) !important; }\n\n.mdl-color-text--yellow-A100 {\n color: rgb(255,255,141) !important; }\n\n.mdl-color--yellow-A100 {\n background-color: rgb(255,255,141) !important; }\n\n.mdl-color-text--yellow-A200 {\n color: rgb(255,255,0) !important; }\n\n.mdl-color--yellow-A200 {\n background-color: rgb(255,255,0) !important; }\n\n.mdl-color-text--yellow-A400 {\n color: rgb(255,234,0) !important; }\n\n.mdl-color--yellow-A400 {\n background-color: rgb(255,234,0) !important; }\n\n.mdl-color-text--yellow-A700 {\n color: rgb(255,214,0) !important; }\n\n.mdl-color--yellow-A700 {\n background-color: rgb(255,214,0) !important; }\n\n.mdl-color-text--amber {\n color: rgb(255,193,7) !important; }\n\n.mdl-color--amber {\n background-color: rgb(255,193,7) !important; }\n\n.mdl-color-text--amber-50 {\n color: rgb(255,248,225) !important; }\n\n.mdl-color--amber-50 {\n background-color: rgb(255,248,225) !important; }\n\n.mdl-color-text--amber-100 {\n color: rgb(255,236,179) !important; }\n\n.mdl-color--amber-100 {\n background-color: rgb(255,236,179) !important; }\n\n.mdl-color-text--amber-200 {\n color: rgb(255,224,130) !important; }\n\n.mdl-color--amber-200 {\n background-color: rgb(255,224,130) !important; }\n\n.mdl-color-text--amber-300 {\n color: rgb(255,213,79) !important; }\n\n.mdl-color--amber-300 {\n background-color: rgb(255,213,79) !important; }\n\n.mdl-color-text--amber-400 {\n color: rgb(255,202,40) !important; }\n\n.mdl-color--amber-400 {\n background-color: rgb(255,202,40) !important; }\n\n.mdl-color-text--amber-500 {\n color: rgb(255,193,7) !important; }\n\n.mdl-color--amber-500 {\n background-color: rgb(255,193,7) !important; }\n\n.mdl-color-text--amber-600 {\n color: rgb(255,179,0) !important; }\n\n.mdl-color--amber-600 {\n background-color: rgb(255,179,0) !important; }\n\n.mdl-color-text--amber-700 {\n color: rgb(255,160,0) !important; }\n\n.mdl-color--amber-700 {\n background-color: rgb(255,160,0) !important; }\n\n.mdl-color-text--amber-800 {\n color: rgb(255,143,0) !important; }\n\n.mdl-color--amber-800 {\n background-color: rgb(255,143,0) !important; }\n\n.mdl-color-text--amber-900 {\n color: rgb(255,111,0) !important; }\n\n.mdl-color--amber-900 {\n background-color: rgb(255,111,0) !important; }\n\n.mdl-color-text--amber-A100 {\n color: rgb(255,229,127) !important; }\n\n.mdl-color--amber-A100 {\n background-color: rgb(255,229,127) !important; }\n\n.mdl-color-text--amber-A200 {\n color: rgb(255,215,64) !important; }\n\n.mdl-color--amber-A200 {\n background-color: rgb(255,215,64) !important; }\n\n.mdl-color-text--amber-A400 {\n color: rgb(255,196,0) !important; }\n\n.mdl-color--amber-A400 {\n background-color: rgb(255,196,0) !important; }\n\n.mdl-color-text--amber-A700 {\n color: rgb(255,171,0) !important; }\n\n.mdl-color--amber-A700 {\n background-color: rgb(255,171,0) !important; }\n\n.mdl-color-text--orange {\n color: rgb(255,152,0) !important; }\n\n.mdl-color--orange {\n background-color: rgb(255,152,0) !important; }\n\n.mdl-color-text--orange-50 {\n color: rgb(255,243,224) !important; }\n\n.mdl-color--orange-50 {\n background-color: rgb(255,243,224) !important; }\n\n.mdl-color-text--orange-100 {\n color: rgb(255,224,178) !important; }\n\n.mdl-color--orange-100 {\n background-color: rgb(255,224,178) !important; }\n\n.mdl-color-text--orange-200 {\n color: rgb(255,204,128) !important; }\n\n.mdl-color--orange-200 {\n background-color: rgb(255,204,128) !important; }\n\n.mdl-color-text--orange-300 {\n color: rgb(255,183,77) !important; }\n\n.mdl-color--orange-300 {\n background-color: rgb(255,183,77) !important; }\n\n.mdl-color-text--orange-400 {\n color: rgb(255,167,38) !important; }\n\n.mdl-color--orange-400 {\n background-color: rgb(255,167,38) !important; }\n\n.mdl-color-text--orange-500 {\n color: rgb(255,152,0) !important; }\n\n.mdl-color--orange-500 {\n background-color: rgb(255,152,0) !important; }\n\n.mdl-color-text--orange-600 {\n color: rgb(251,140,0) !important; }\n\n.mdl-color--orange-600 {\n background-color: rgb(251,140,0) !important; }\n\n.mdl-color-text--orange-700 {\n color: rgb(245,124,0) !important; }\n\n.mdl-color--orange-700 {\n background-color: rgb(245,124,0) !important; }\n\n.mdl-color-text--orange-800 {\n color: rgb(239,108,0) !important; }\n\n.mdl-color--orange-800 {\n background-color: rgb(239,108,0) !important; }\n\n.mdl-color-text--orange-900 {\n color: rgb(230,81,0) !important; }\n\n.mdl-color--orange-900 {\n background-color: rgb(230,81,0) !important; }\n\n.mdl-color-text--orange-A100 {\n color: rgb(255,209,128) !important; }\n\n.mdl-color--orange-A100 {\n background-color: rgb(255,209,128) !important; }\n\n.mdl-color-text--orange-A200 {\n color: rgb(255,171,64) !important; }\n\n.mdl-color--orange-A200 {\n background-color: rgb(255,171,64) !important; }\n\n.mdl-color-text--orange-A400 {\n color: rgb(255,145,0) !important; }\n\n.mdl-color--orange-A400 {\n background-color: rgb(255,145,0) !important; }\n\n.mdl-color-text--orange-A700 {\n color: rgb(255,109,0) !important; }\n\n.mdl-color--orange-A700 {\n background-color: rgb(255,109,0) !important; }\n\n.mdl-color-text--deep-orange {\n color: rgb(255,87,34) !important; }\n\n.mdl-color--deep-orange {\n background-color: rgb(255,87,34) !important; }\n\n.mdl-color-text--deep-orange-50 {\n color: rgb(251,233,231) !important; }\n\n.mdl-color--deep-orange-50 {\n background-color: rgb(251,233,231) !important; }\n\n.mdl-color-text--deep-orange-100 {\n color: rgb(255,204,188) !important; }\n\n.mdl-color--deep-orange-100 {\n background-color: rgb(255,204,188) !important; }\n\n.mdl-color-text--deep-orange-200 {\n color: rgb(255,171,145) !important; }\n\n.mdl-color--deep-orange-200 {\n background-color: rgb(255,171,145) !important; }\n\n.mdl-color-text--deep-orange-300 {\n color: rgb(255,138,101) !important; }\n\n.mdl-color--deep-orange-300 {\n background-color: rgb(255,138,101) !important; }\n\n.mdl-color-text--deep-orange-400 {\n color: rgb(255,112,67) !important; }\n\n.mdl-color--deep-orange-400 {\n background-color: rgb(255,112,67) !important; }\n\n.mdl-color-text--deep-orange-500 {\n color: rgb(255,87,34) !important; }\n\n.mdl-color--deep-orange-500 {\n background-color: rgb(255,87,34) !important; }\n\n.mdl-color-text--deep-orange-600 {\n color: rgb(244,81,30) !important; }\n\n.mdl-color--deep-orange-600 {\n background-color: rgb(244,81,30) !important; }\n\n.mdl-color-text--deep-orange-700 {\n color: rgb(230,74,25) !important; }\n\n.mdl-color--deep-orange-700 {\n background-color: rgb(230,74,25) !important; }\n\n.mdl-color-text--deep-orange-800 {\n color: rgb(216,67,21) !important; }\n\n.mdl-color--deep-orange-800 {\n background-color: rgb(216,67,21) !important; }\n\n.mdl-color-text--deep-orange-900 {\n color: rgb(191,54,12) !important; }\n\n.mdl-color--deep-orange-900 {\n background-color: rgb(191,54,12) !important; }\n\n.mdl-color-text--deep-orange-A100 {\n color: rgb(255,158,128) !important; }\n\n.mdl-color--deep-orange-A100 {\n background-color: rgb(255,158,128) !important; }\n\n.mdl-color-text--deep-orange-A200 {\n color: rgb(255,110,64) !important; }\n\n.mdl-color--deep-orange-A200 {\n background-color: rgb(255,110,64) !important; }\n\n.mdl-color-text--deep-orange-A400 {\n color: rgb(255,61,0) !important; }\n\n.mdl-color--deep-orange-A400 {\n background-color: rgb(255,61,0) !important; }\n\n.mdl-color-text--deep-orange-A700 {\n color: rgb(221,44,0) !important; }\n\n.mdl-color--deep-orange-A700 {\n background-color: rgb(221,44,0) !important; }\n\n.mdl-color-text--brown {\n color: rgb(121,85,72) !important; }\n\n.mdl-color--brown {\n background-color: rgb(121,85,72) !important; }\n\n.mdl-color-text--brown-50 {\n color: rgb(239,235,233) !important; }\n\n.mdl-color--brown-50 {\n background-color: rgb(239,235,233) !important; }\n\n.mdl-color-text--brown-100 {\n color: rgb(215,204,200) !important; }\n\n.mdl-color--brown-100 {\n background-color: rgb(215,204,200) !important; }\n\n.mdl-color-text--brown-200 {\n color: rgb(188,170,164) !important; }\n\n.mdl-color--brown-200 {\n background-color: rgb(188,170,164) !important; }\n\n.mdl-color-text--brown-300 {\n color: rgb(161,136,127) !important; }\n\n.mdl-color--brown-300 {\n background-color: rgb(161,136,127) !important; }\n\n.mdl-color-text--brown-400 {\n color: rgb(141,110,99) !important; }\n\n.mdl-color--brown-400 {\n background-color: rgb(141,110,99) !important; }\n\n.mdl-color-text--brown-500 {\n color: rgb(121,85,72) !important; }\n\n.mdl-color--brown-500 {\n background-color: rgb(121,85,72) !important; }\n\n.mdl-color-text--brown-600 {\n color: rgb(109,76,65) !important; }\n\n.mdl-color--brown-600 {\n background-color: rgb(109,76,65) !important; }\n\n.mdl-color-text--brown-700 {\n color: rgb(93,64,55) !important; }\n\n.mdl-color--brown-700 {\n background-color: rgb(93,64,55) !important; }\n\n.mdl-color-text--brown-800 {\n color: rgb(78,52,46) !important; }\n\n.mdl-color--brown-800 {\n background-color: rgb(78,52,46) !important; }\n\n.mdl-color-text--brown-900 {\n color: rgb(62,39,35) !important; }\n\n.mdl-color--brown-900 {\n background-color: rgb(62,39,35) !important; }\n\n.mdl-color-text--grey {\n color: rgb(158,158,158) !important; }\n\n.mdl-color--grey {\n background-color: rgb(158,158,158) !important; }\n\n.mdl-color-text--grey-50 {\n color: rgb(250,250,250) !important; }\n\n.mdl-color--grey-50 {\n background-color: rgb(250,250,250) !important; }\n\n.mdl-color-text--grey-100 {\n color: rgb(245,245,245) !important; }\n\n.mdl-color--grey-100 {\n background-color: rgb(245,245,245) !important; }\n\n.mdl-color-text--grey-200 {\n color: rgb(238,238,238) !important; }\n\n.mdl-color--grey-200 {\n background-color: rgb(238,238,238) !important; }\n\n.mdl-color-text--grey-300 {\n color: rgb(224,224,224) !important; }\n\n.mdl-color--grey-300 {\n background-color: rgb(224,224,224) !important; }\n\n.mdl-color-text--grey-400 {\n color: rgb(189,189,189) !important; }\n\n.mdl-color--grey-400 {\n background-color: rgb(189,189,189) !important; }\n\n.mdl-color-text--grey-500 {\n color: rgb(158,158,158) !important; }\n\n.mdl-color--grey-500 {\n background-color: rgb(158,158,158) !important; }\n\n.mdl-color-text--grey-600 {\n color: rgb(117,117,117) !important; }\n\n.mdl-color--grey-600 {\n background-color: rgb(117,117,117) !important; }\n\n.mdl-color-text--grey-700 {\n color: rgb(97,97,97) !important; }\n\n.mdl-color--grey-700 {\n background-color: rgb(97,97,97) !important; }\n\n.mdl-color-text--grey-800 {\n color: rgb(66,66,66) !important; }\n\n.mdl-color--grey-800 {\n background-color: rgb(66,66,66) !important; }\n\n.mdl-color-text--grey-900 {\n color: rgb(33,33,33) !important; }\n\n.mdl-color--grey-900 {\n background-color: rgb(33,33,33) !important; }\n\n.mdl-color-text--blue-grey {\n color: rgb(96,125,139) !important; }\n\n.mdl-color--blue-grey {\n background-color: rgb(96,125,139) !important; }\n\n.mdl-color-text--blue-grey-50 {\n color: rgb(236,239,241) !important; }\n\n.mdl-color--blue-grey-50 {\n background-color: rgb(236,239,241) !important; }\n\n.mdl-color-text--blue-grey-100 {\n color: rgb(207,216,220) !important; }\n\n.mdl-color--blue-grey-100 {\n background-color: rgb(207,216,220) !important; }\n\n.mdl-color-text--blue-grey-200 {\n color: rgb(176,190,197) !important; }\n\n.mdl-color--blue-grey-200 {\n background-color: rgb(176,190,197) !important; }\n\n.mdl-color-text--blue-grey-300 {\n color: rgb(144,164,174) !important; }\n\n.mdl-color--blue-grey-300 {\n background-color: rgb(144,164,174) !important; }\n\n.mdl-color-text--blue-grey-400 {\n color: rgb(120,144,156) !important; }\n\n.mdl-color--blue-grey-400 {\n background-color: rgb(120,144,156) !important; }\n\n.mdl-color-text--blue-grey-500 {\n color: rgb(96,125,139) !important; }\n\n.mdl-color--blue-grey-500 {\n background-color: rgb(96,125,139) !important; }\n\n.mdl-color-text--blue-grey-600 {\n color: rgb(84,110,122) !important; }\n\n.mdl-color--blue-grey-600 {\n background-color: rgb(84,110,122) !important; }\n\n.mdl-color-text--blue-grey-700 {\n color: rgb(69,90,100) !important; }\n\n.mdl-color--blue-grey-700 {\n background-color: rgb(69,90,100) !important; }\n\n.mdl-color-text--blue-grey-800 {\n color: rgb(55,71,79) !important; }\n\n.mdl-color--blue-grey-800 {\n background-color: rgb(55,71,79) !important; }\n\n.mdl-color-text--blue-grey-900 {\n color: rgb(38,50,56) !important; }\n\n.mdl-color--blue-grey-900 {\n background-color: rgb(38,50,56) !important; }\n\n.mdl-color--black {\n background-color: rgb(0,0,0) !important; }\n\n.mdl-color-text--black {\n color: rgb(0,0,0) !important; }\n\n.mdl-color--white {\n background-color: rgb(255,255,255) !important; }\n\n.mdl-color-text--white {\n color: rgb(255,255,255) !important; }\n\n.mdl-color--primary {\n background-color: rgb(63,81,181) !important; }\n\n.mdl-color--primary-contrast {\n background-color: rgb(255,255,255) !important; }\n\n.mdl-color--primary-dark {\n background-color: rgb(48,63,159) !important; }\n\n.mdl-color--accent {\n background-color: rgb(255,64,129) !important; }\n\n.mdl-color--accent-contrast {\n background-color: rgb(255,255,255) !important; }\n\n.mdl-color-text--primary {\n color: rgb(63,81,181) !important; }\n\n.mdl-color-text--primary-contrast {\n color: rgb(255,255,255) !important; }\n\n.mdl-color-text--primary-dark {\n color: rgb(48,63,159) !important; }\n\n.mdl-color-text--accent {\n color: rgb(255,64,129) !important; }\n\n.mdl-color-text--accent-contrast {\n color: rgb(255,255,255) !important; }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n.mdl-ripple {\n background: rgb(0,0,0);\n border-radius: 50%;\n height: 50px;\n left: 0;\n opacity: 0;\n pointer-events: none;\n position: absolute;\n top: 0;\n -webkit-transform: translate(-50%, -50%);\n -ms-transform: translate(-50%, -50%);\n transform: translate(-50%, -50%);\n width: 50px;\n overflow: hidden; }\n .mdl-ripple.is-animating {\n -webkit-transition: -webkit-transform 0.3s cubic-bezier(0, 0, 0.2, 1), width 0.3s cubic-bezier(0, 0, 0.2, 1), height 0.3s cubic-bezier(0, 0, 0.2, 1), opacity 0.6s cubic-bezier(0, 0, 0.2, 1);\n transition: transform 0.3s cubic-bezier(0, 0, 0.2, 1), width 0.3s cubic-bezier(0, 0, 0.2, 1), height 0.3s cubic-bezier(0, 0, 0.2, 1), opacity 0.6s cubic-bezier(0, 0, 0.2, 1); }\n .mdl-ripple.is-visible {\n opacity: 0.3; }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n.mdl-animation--default {\n -webkit-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); }\n\n.mdl-animation--fast-out-slow-in {\n -webkit-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); }\n\n.mdl-animation--linear-out-slow-in {\n -webkit-transition-timing-function: cubic-bezier(0, 0, 0.2, 1);\n transition-timing-function: cubic-bezier(0, 0, 0.2, 1); }\n\n.mdl-animation--fast-out-linear-in {\n -webkit-transition-timing-function: cubic-bezier(0.4, 0, 1, 1);\n transition-timing-function: cubic-bezier(0.4, 0, 1, 1); }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n.mdl-badge {\n position: relative;\n white-space: nowrap;\n margin-right: 24px; }\n .mdl-badge:not([data-badge]) {\n margin-right: auto; }\n .mdl-badge[data-badge]:after {\n content: attr(data-badge);\n display: -webkit-box;\n display: -webkit-flex;\n display: -ms-flexbox;\n display: flex;\n -webkit-box-orient: horizontal;\n -webkit-box-direction: normal;\n -webkit-flex-direction: row;\n -ms-flex-direction: row;\n flex-direction: row;\n -webkit-flex-wrap: wrap;\n -ms-flex-wrap: wrap;\n flex-wrap: wrap;\n -webkit-box-pack: center;\n -webkit-justify-content: center;\n -ms-flex-pack: center;\n justify-content: center;\n -webkit-align-content: center;\n -ms-flex-line-pack: center;\n align-content: center;\n -webkit-box-align: center;\n -webkit-align-items: center;\n -ms-flex-align: center;\n align-items: center;\n position: absolute;\n top: -11px;\n right: -24px;\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-weight: 600;\n font-size: 12px;\n width: 22px;\n height: 22px;\n border-radius: 50%;\n background: rgb(255,64,129);\n color: rgb(255,255,255); }\n .mdl-button .mdl-badge[data-badge]:after {\n top: -10px;\n right: -5px; }\n .mdl-badge.mdl-badge--no-background[data-badge]:after {\n color: rgb(255,64,129);\n background: rgb(255,255,255);\n box-shadow: 0 0 1px gray; }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* Typography */\n/* Shadows */\n/* Animations */\n.mdl-button {\n background: transparent;\n border: none;\n border-radius: 2px;\n color: rgb(0,0,0);\n position: relative;\n height: 36px;\n min-width: 64px;\n padding: 0 16px;\n display: inline-block;\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 14px;\n font-weight: 500;\n text-transform: uppercase;\n line-height: 1;\n letter-spacing: 0;\n overflow: hidden;\n will-change: box-shadow, transform;\n -webkit-transition: box-shadow 0.2s cubic-bezier(0.4, 0, 1, 1), background-color 0.2s cubic-bezier(0.4, 0, 0.2, 1), color 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n transition: box-shadow 0.2s cubic-bezier(0.4, 0, 1, 1), background-color 0.2s cubic-bezier(0.4, 0, 0.2, 1), color 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n outline: none;\n cursor: pointer;\n text-decoration: none;\n text-align: center;\n line-height: 36px;\n vertical-align: middle; }\n .mdl-button::-moz-focus-inner {\n border: 0; }\n .mdl-button:hover {\n background-color: rgba(158,158,158, 0.20); }\n .mdl-button:focus:not(:active) {\n background-color: rgba(0,0,0, 0.12); }\n .mdl-button:active {\n background-color: rgba(158,158,158, 0.40); }\n .mdl-button.mdl-button--colored {\n color: rgb(63,81,181); }\n .mdl-button.mdl-button--colored:focus:not(:active) {\n background-color: rgba(0,0,0, 0.12); }\n\ninput.mdl-button[type=\"submit\"] {\n -webkit-appearance: none; }\n\n.mdl-button--raised {\n background: rgba(158,158,158, 0.20);\n box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12); }\n .mdl-button--raised:active {\n box-shadow: 0 4px 5px 0 rgba(0, 0, 0, 0.14), 0 1px 10px 0 rgba(0, 0, 0, 0.12), 0 2px 4px -1px rgba(0, 0, 0, 0.2);\n background-color: rgba(158,158,158, 0.40); }\n .mdl-button--raised:focus:not(:active) {\n box-shadow: 0 0 8px rgba(0, 0, 0, 0.18), 0 8px 16px rgba(0, 0, 0, 0.36);\n background-color: rgba(158,158,158, 0.40); }\n .mdl-button--raised.mdl-button--colored {\n background: rgb(63,81,181);\n color: rgb(255,255,255); }\n .mdl-button--raised.mdl-button--colored:hover {\n background-color: rgb(63,81,181); }\n .mdl-button--raised.mdl-button--colored:active {\n background-color: rgb(63,81,181); }\n .mdl-button--raised.mdl-button--colored:focus:not(:active) {\n background-color: rgb(63,81,181); }\n .mdl-button--raised.mdl-button--colored .mdl-ripple {\n background: rgb(255,255,255); }\n\n.mdl-button--fab {\n border-radius: 50%;\n font-size: 24px;\n height: 56px;\n margin: auto;\n min-width: 56px;\n width: 56px;\n padding: 0;\n overflow: hidden;\n background: rgba(158,158,158, 0.20);\n box-shadow: 0 1px 1.5px 0 rgba(0, 0, 0, 0.12), 0 1px 1px 0 rgba(0, 0, 0, 0.24);\n position: relative;\n line-height: normal; }\n .mdl-button--fab .material-icons {\n position: absolute;\n top: 50%;\n left: 50%;\n -webkit-transform: translate(-12px, -12px);\n -ms-transform: translate(-12px, -12px);\n transform: translate(-12px, -12px);\n line-height: 24px;\n width: 24px; }\n .mdl-button--fab.mdl-button--mini-fab {\n height: 40px;\n min-width: 40px;\n width: 40px; }\n .mdl-button--fab .mdl-button__ripple-container {\n border-radius: 50%;\n -webkit-mask-image: -webkit-radial-gradient(circle, white, black); }\n .mdl-button--fab:active {\n box-shadow: 0 4px 5px 0 rgba(0, 0, 0, 0.14), 0 1px 10px 0 rgba(0, 0, 0, 0.12), 0 2px 4px -1px rgba(0, 0, 0, 0.2);\n background-color: rgba(158,158,158, 0.40); }\n .mdl-button--fab:focus:not(:active) {\n box-shadow: 0 0 8px rgba(0, 0, 0, 0.18), 0 8px 16px rgba(0, 0, 0, 0.36);\n background-color: rgba(158,158,158, 0.40); }\n .mdl-button--fab.mdl-button--colored {\n background: rgb(255,64,129);\n color: rgb(255,255,255); }\n .mdl-button--fab.mdl-button--colored:hover {\n background-color: rgb(255,64,129); }\n .mdl-button--fab.mdl-button--colored:focus:not(:active) {\n background-color: rgb(255,64,129); }\n .mdl-button--fab.mdl-button--colored:active {\n background-color: rgb(255,64,129); }\n .mdl-button--fab.mdl-button--colored .mdl-ripple {\n background: rgb(255,255,255); }\n\n.mdl-button--icon {\n border-radius: 50%;\n font-size: 24px;\n height: 32px;\n margin-left: 0;\n margin-right: 0;\n min-width: 32px;\n width: 32px;\n padding: 0;\n overflow: hidden;\n color: inherit;\n line-height: normal; }\n .mdl-button--icon .material-icons {\n position: absolute;\n top: 50%;\n left: 50%;\n -webkit-transform: translate(-12px, -12px);\n -ms-transform: translate(-12px, -12px);\n transform: translate(-12px, -12px);\n line-height: 24px;\n width: 24px; }\n .mdl-button--icon.mdl-button--mini-icon {\n height: 24px;\n min-width: 24px;\n width: 24px; }\n .mdl-button--icon.mdl-button--mini-icon .material-icons {\n top: 0px;\n left: 0px; }\n .mdl-button--icon .mdl-button__ripple-container {\n border-radius: 50%;\n -webkit-mask-image: -webkit-radial-gradient(circle, white, black); }\n\n.mdl-button__ripple-container {\n display: block;\n height: 100%;\n left: 0px;\n position: absolute;\n top: 0px;\n width: 100%;\n z-index: 0;\n overflow: hidden; }\n .mdl-button[disabled] .mdl-button__ripple-container .mdl-ripple,\n .mdl-button.mdl-button--disabled .mdl-button__ripple-container .mdl-ripple {\n background-color: transparent; }\n\n.mdl-button--primary.mdl-button--primary {\n color: rgb(63,81,181); }\n .mdl-button--primary.mdl-button--primary .mdl-ripple {\n background: rgb(255,255,255); }\n .mdl-button--primary.mdl-button--primary.mdl-button--raised, .mdl-button--primary.mdl-button--primary.mdl-button--fab {\n color: rgb(255,255,255);\n background-color: rgb(63,81,181); }\n\n.mdl-button--accent.mdl-button--accent {\n color: rgb(255,64,129); }\n .mdl-button--accent.mdl-button--accent .mdl-ripple {\n background: rgb(255,255,255); }\n .mdl-button--accent.mdl-button--accent.mdl-button--raised, .mdl-button--accent.mdl-button--accent.mdl-button--fab {\n color: rgb(255,255,255);\n background-color: rgb(255,64,129); }\n\n.mdl-button[disabled][disabled], .mdl-button.mdl-button--disabled.mdl-button--disabled {\n color: rgba(0,0,0, 0.26);\n cursor: default;\n background-color: transparent; }\n\n.mdl-button--fab[disabled][disabled], .mdl-button--fab.mdl-button--disabled.mdl-button--disabled {\n background-color: rgba(0,0,0, 0.12);\n color: rgba(0,0,0, 0.26);\n box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12); }\n\n.mdl-button--raised[disabled][disabled], .mdl-button--raised.mdl-button--disabled.mdl-button--disabled {\n background-color: rgba(0,0,0, 0.12);\n color: rgba(0,0,0, 0.26);\n box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12); }\n\n.mdl-button--colored[disabled][disabled], .mdl-button--colored.mdl-button--disabled.mdl-button--disabled {\n color: rgba(0,0,0, 0.26); }\n\n.mdl-button .material-icons {\n vertical-align: middle; }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n.mdl-card {\n display: -webkit-box;\n display: -webkit-flex;\n display: -ms-flexbox;\n display: flex;\n -webkit-box-orient: vertical;\n -webkit-box-direction: normal;\n -webkit-flex-direction: column;\n -ms-flex-direction: column;\n flex-direction: column;\n font-size: 16px;\n font-weight: 400;\n min-height: 200px;\n overflow: hidden;\n width: 330px;\n z-index: 1;\n position: relative;\n background: rgb(255,255,255);\n border-radius: 2px;\n box-sizing: border-box; }\n\n.mdl-card__media {\n background-color: rgb(255,64,129);\n background-repeat: repeat;\n background-position: 50% 50%;\n background-size: cover;\n background-origin: padding-box;\n background-attachment: scroll;\n box-sizing: border-box; }\n\n.mdl-card__title {\n -webkit-box-align: center;\n -webkit-align-items: center;\n -ms-flex-align: center;\n align-items: center;\n color: rgb(0,0,0);\n display: block;\n display: -webkit-box;\n display: -webkit-flex;\n display: -ms-flexbox;\n display: flex;\n -webkit-box-pack: stretch;\n -webkit-justify-content: stretch;\n -ms-flex-pack: stretch;\n justify-content: stretch;\n line-height: normal;\n padding: 16px 16px;\n -webkit-perspective-origin: 165px 56px;\n perspective-origin: 165px 56px;\n -webkit-transform-origin: 165px 56px;\n -ms-transform-origin: 165px 56px;\n transform-origin: 165px 56px;\n box-sizing: border-box; }\n .mdl-card__title.mdl-card--border {\n border-bottom: 1px solid rgba(0, 0, 0, 0.1); }\n\n.mdl-card__title-text {\n -webkit-align-self: flex-end;\n -ms-flex-item-align: end;\n align-self: flex-end;\n color: inherit;\n display: block;\n display: -webkit-box;\n display: -webkit-flex;\n display: -ms-flexbox;\n display: flex;\n font-size: 24px;\n font-weight: 300;\n line-height: normal;\n overflow: hidden;\n -webkit-transform-origin: 149px 48px;\n -ms-transform-origin: 149px 48px;\n transform-origin: 149px 48px;\n margin: 0; }\n\n.mdl-card__subtitle-text {\n font-size: 14px;\n color: rgba(0,0,0, 0.54);\n margin: 0; }\n\n.mdl-card__supporting-text {\n color: rgba(0,0,0, 0.54);\n font-size: 13px;\n line-height: 18px;\n overflow: hidden;\n padding: 16px 16px;\n width: 90%; }\n\n.mdl-card__actions {\n font-size: 16px;\n line-height: normal;\n width: 100%;\n background-color: transparent;\n padding: 8px;\n box-sizing: border-box; }\n .mdl-card__actions.mdl-card--border {\n border-top: 1px solid rgba(0, 0, 0, 0.1); }\n\n.mdl-card--expand {\n -webkit-box-flex: 1;\n -webkit-flex-grow: 1;\n -ms-flex-positive: 1;\n flex-grow: 1; }\n\n.mdl-card__menu {\n position: absolute;\n right: 16px;\n top: 16px; }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* Typography */\n/* Shadows */\n/* Animations */\n.mdl-checkbox {\n position: relative;\n z-index: 1;\n vertical-align: middle;\n display: inline-block;\n box-sizing: border-box;\n width: 100%;\n height: 24px;\n margin: 0;\n padding: 0; }\n .mdl-checkbox.is-upgraded {\n padding-left: 24px; }\n\n.mdl-checkbox__input {\n line-height: 24px; }\n .mdl-checkbox.is-upgraded .mdl-checkbox__input {\n position: absolute;\n width: 0;\n height: 0;\n margin: 0;\n padding: 0;\n opacity: 0;\n -ms-appearance: none;\n -moz-appearance: none;\n -webkit-appearance: none;\n appearance: none;\n border: none; }\n\n.mdl-checkbox__box-outline {\n position: absolute;\n top: 3px;\n left: 0;\n display: inline-block;\n box-sizing: border-box;\n width: 16px;\n height: 16px;\n margin: 0;\n cursor: pointer;\n overflow: hidden;\n border: 2px solid rgba(0,0,0, 0.54);\n border-radius: 2px;\n z-index: 2; }\n .mdl-checkbox.is-checked .mdl-checkbox__box-outline {\n border: 2px solid rgb(63,81,181); }\n .mdl-checkbox.is-disabled .mdl-checkbox__box-outline {\n border: 2px solid rgba(0,0,0, 0.26);\n cursor: auto; }\n\n.mdl-checkbox__focus-helper {\n position: absolute;\n top: 3px;\n left: 0;\n display: inline-block;\n box-sizing: border-box;\n width: 16px;\n height: 16px;\n border-radius: 50%;\n background-color: transparent; }\n .mdl-checkbox.is-focused .mdl-checkbox__focus-helper {\n box-shadow: 0 0 0px 8px rgba(0, 0, 0, 0.1);\n background-color: rgba(0, 0, 0, 0.1); }\n .mdl-checkbox.is-focused.is-checked .mdl-checkbox__focus-helper {\n box-shadow: 0 0 0px 8px rgba(63,81,181, 0.26);\n background-color: rgba(63,81,181, 0.26); }\n\n.mdl-checkbox__tick-outline {\n position: absolute;\n top: 0;\n left: 0;\n height: 100%;\n width: 100%;\n -webkit-mask: url(\"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcKICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICB4bWxuczpjYz0iaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbnMjIgogICB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiCiAgIHhtbG5zOnN2Zz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgdmVyc2lvbj0iMS4xIgogICB2aWV3Qm94PSIwIDAgMSAxIgogICBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWluWU1pbiBtZWV0Ij4KICA8ZGVmcz4KICAgIDxjbGlwUGF0aCBpZD0iY2xpcCI+CiAgICAgIDxwYXRoCiAgICAgICAgIGQ9Ik0gMCwwIDAsMSAxLDEgMSwwIDAsMCB6IE0gMC44NTM0Mzc1LDAuMTY3MTg3NSAwLjk1OTY4NzUsMC4yNzMxMjUgMC40MjkzNzUsMC44MDM0Mzc1IDAuMzIzMTI1LDAuOTA5Njg3NSAwLjIxNzE4NzUsMC44MDM0Mzc1IDAuMDQwMzEyNSwwLjYyNjg3NSAwLjE0NjU2MjUsMC41MjA2MjUgMC4zMjMxMjUsMC42OTc1IDAuODUzNDM3NSwwLjE2NzE4NzUgeiIKICAgICAgICAgc3R5bGU9ImZpbGw6I2ZmZmZmZjtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZSIgLz4KICAgIDwvY2xpcFBhdGg+CiAgICA8bWFzayBpZD0ibWFzayIgbWFza1VuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgbWFza0NvbnRlbnRVbml0cz0ib2JqZWN0Qm91bmRpbmdCb3giPgogICAgICA8cGF0aAogICAgICAgICBkPSJNIDAsMCAwLDEgMSwxIDEsMCAwLDAgeiBNIDAuODUzNDM3NSwwLjE2NzE4NzUgMC45NTk2ODc1LDAuMjczMTI1IDAuNDI5Mzc1LDAuODAzNDM3NSAwLjMyMzEyNSwwLjkwOTY4NzUgMC4yMTcxODc1LDAuODAzNDM3NSAwLjA0MDMxMjUsMC42MjY4NzUgMC4xNDY1NjI1LDAuNTIwNjI1IDAuMzIzMTI1LDAuNjk3NSAwLjg1MzQzNzUsMC4xNjcxODc1IHoiCiAgICAgICAgIHN0eWxlPSJmaWxsOiNmZmZmZmY7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmUiIC8+CiAgICA8L21hc2s+CiAgPC9kZWZzPgogIDxyZWN0CiAgICAgd2lkdGg9IjEiCiAgICAgaGVpZ2h0PSIxIgogICAgIHg9IjAiCiAgICAgeT0iMCIKICAgICBjbGlwLXBhdGg9InVybCgjY2xpcCkiCiAgICAgc3R5bGU9ImZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZSIgLz4KPC9zdmc+Cg==\");\n mask: url(\"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcKICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICB4bWxuczpjYz0iaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbnMjIgogICB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiCiAgIHhtbG5zOnN2Zz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgdmVyc2lvbj0iMS4xIgogICB2aWV3Qm94PSIwIDAgMSAxIgogICBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWluWU1pbiBtZWV0Ij4KICA8ZGVmcz4KICAgIDxjbGlwUGF0aCBpZD0iY2xpcCI+CiAgICAgIDxwYXRoCiAgICAgICAgIGQ9Ik0gMCwwIDAsMSAxLDEgMSwwIDAsMCB6IE0gMC44NTM0Mzc1LDAuMTY3MTg3NSAwLjk1OTY4NzUsMC4yNzMxMjUgMC40MjkzNzUsMC44MDM0Mzc1IDAuMzIzMTI1LDAuOTA5Njg3NSAwLjIxNzE4NzUsMC44MDM0Mzc1IDAuMDQwMzEyNSwwLjYyNjg3NSAwLjE0NjU2MjUsMC41MjA2MjUgMC4zMjMxMjUsMC42OTc1IDAuODUzNDM3NSwwLjE2NzE4NzUgeiIKICAgICAgICAgc3R5bGU9ImZpbGw6I2ZmZmZmZjtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZSIgLz4KICAgIDwvY2xpcFBhdGg+CiAgICA8bWFzayBpZD0ibWFzayIgbWFza1VuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgbWFza0NvbnRlbnRVbml0cz0ib2JqZWN0Qm91bmRpbmdCb3giPgogICAgICA8cGF0aAogICAgICAgICBkPSJNIDAsMCAwLDEgMSwxIDEsMCAwLDAgeiBNIDAuODUzNDM3NSwwLjE2NzE4NzUgMC45NTk2ODc1LDAuMjczMTI1IDAuNDI5Mzc1LDAuODAzNDM3NSAwLjMyMzEyNSwwLjkwOTY4NzUgMC4yMTcxODc1LDAuODAzNDM3NSAwLjA0MDMxMjUsMC42MjY4NzUgMC4xNDY1NjI1LDAuNTIwNjI1IDAuMzIzMTI1LDAuNjk3NSAwLjg1MzQzNzUsMC4xNjcxODc1IHoiCiAgICAgICAgIHN0eWxlPSJmaWxsOiNmZmZmZmY7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmUiIC8+CiAgICA8L21hc2s+CiAgPC9kZWZzPgogIDxyZWN0CiAgICAgd2lkdGg9IjEiCiAgICAgaGVpZ2h0PSIxIgogICAgIHg9IjAiCiAgICAgeT0iMCIKICAgICBjbGlwLXBhdGg9InVybCgjY2xpcCkiCiAgICAgc3R5bGU9ImZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZSIgLz4KPC9zdmc+Cg==\");\n background: transparent;\n -webkit-transition-duration: 0.28s;\n transition-duration: 0.28s;\n -webkit-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n -webkit-transition-property: background;\n transition-property: background; }\n .mdl-checkbox.is-checked .mdl-checkbox__tick-outline {\n background: rgb(63,81,181) url(\"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcKICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICB4bWxuczpjYz0iaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbnMjIgogICB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiCiAgIHhtbG5zOnN2Zz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgdmVyc2lvbj0iMS4xIgogICB2aWV3Qm94PSIwIDAgMSAxIgogICBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWluWU1pbiBtZWV0Ij4KICA8cGF0aAogICAgIGQ9Ik0gMC4wNDAzODA1OSwwLjYyNjc3NjcgMC4xNDY0NDY2MSwwLjUyMDcxMDY4IDAuNDI5Mjg5MzIsMC44MDM1NTMzOSAwLjMyMzIyMzMsMC45MDk2MTk0MSB6IE0gMC4yMTcxNTcyOSwwLjgwMzU1MzM5IDAuODUzNTUzMzksMC4xNjcxNTcyOSAwLjk1OTYxOTQxLDAuMjczMjIzMyAwLjMyMzIyMzMsMC45MDk2MTk0MSB6IgogICAgIGlkPSJyZWN0Mzc4MCIKICAgICBzdHlsZT0iZmlsbDojZmZmZmZmO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lIiAvPgo8L3N2Zz4K\"); }\n .mdl-checkbox.is-checked.is-disabled .mdl-checkbox__tick-outline {\n background: rgba(0,0,0, 0.26) url(\"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcKICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICB4bWxuczpjYz0iaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbnMjIgogICB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiCiAgIHhtbG5zOnN2Zz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgdmVyc2lvbj0iMS4xIgogICB2aWV3Qm94PSIwIDAgMSAxIgogICBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWluWU1pbiBtZWV0Ij4KICA8cGF0aAogICAgIGQ9Ik0gMC4wNDAzODA1OSwwLjYyNjc3NjcgMC4xNDY0NDY2MSwwLjUyMDcxMDY4IDAuNDI5Mjg5MzIsMC44MDM1NTMzOSAwLjMyMzIyMzMsMC45MDk2MTk0MSB6IE0gMC4yMTcxNTcyOSwwLjgwMzU1MzM5IDAuODUzNTUzMzksMC4xNjcxNTcyOSAwLjk1OTYxOTQxLDAuMjczMjIzMyAwLjMyMzIyMzMsMC45MDk2MTk0MSB6IgogICAgIGlkPSJyZWN0Mzc4MCIKICAgICBzdHlsZT0iZmlsbDojZmZmZmZmO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lIiAvPgo8L3N2Zz4K\"); }\n\n.mdl-checkbox__label {\n position: relative;\n cursor: pointer;\n font-size: 16px;\n line-height: 24px;\n margin: 0; }\n .mdl-checkbox.is-disabled .mdl-checkbox__label {\n color: rgba(0,0,0, 0.26);\n cursor: auto; }\n\n.mdl-checkbox__ripple-container {\n position: absolute;\n z-index: 2;\n top: -6px;\n left: -10px;\n box-sizing: border-box;\n width: 36px;\n height: 36px;\n border-radius: 50%;\n cursor: pointer;\n overflow: hidden;\n -webkit-mask-image: -webkit-radial-gradient(circle, white, black); }\n .mdl-checkbox__ripple-container .mdl-ripple {\n background: rgb(63,81,181); }\n .mdl-checkbox.is-disabled .mdl-checkbox__ripple-container {\n cursor: auto; }\n .mdl-checkbox.is-disabled .mdl-checkbox__ripple-container .mdl-ripple {\n background: transparent; }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* Typography */\n/* Shadows */\n/* Animations */\n.mdl-data-table {\n position: relative;\n border: 1px solid rgba(0, 0, 0, 0.12);\n border-collapse: collapse;\n white-space: nowrap;\n font-size: 13px;\n background-color: rgb(255,255,255); }\n .mdl-data-table thead {\n padding-bottom: 3px; }\n .mdl-data-table thead .mdl-data-table__select {\n margin-top: 0; }\n .mdl-data-table tbody tr {\n position: relative;\n height: 48px;\n -webkit-transition-duration: 0.28s;\n transition-duration: 0.28s;\n -webkit-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n -webkit-transition-property: background-color;\n transition-property: background-color; }\n .mdl-data-table tbody tr.is-selected {\n background-color: #e0e0e0; }\n .mdl-data-table tbody tr:hover {\n background-color: #eeeeee; }\n .mdl-data-table td, .mdl-data-table th {\n padding: 0 18px 0 18px;\n text-align: right; }\n .mdl-data-table td:first-of-type, .mdl-data-table th:first-of-type {\n padding-left: 24px; }\n .mdl-data-table td:last-of-type, .mdl-data-table th:last-of-type {\n padding-right: 24px; }\n .mdl-data-table td {\n position: relative;\n vertical-align: top;\n height: 48px;\n border-top: 1px solid rgba(0, 0, 0, 0.12);\n border-bottom: 1px solid rgba(0, 0, 0, 0.12);\n padding-top: 12px;\n box-sizing: border-box; }\n .mdl-data-table td .mdl-data-table__select {\n vertical-align: top;\n position: absolute;\n left: 24px; }\n .mdl-data-table th {\n position: relative;\n vertical-align: bottom;\n text-overflow: ellipsis;\n font-size: 14px;\n font-weight: bold;\n line-height: 24px;\n letter-spacing: 0;\n height: 48px;\n font-size: 12px;\n color: rgba(0, 0, 0, 0.54);\n padding-bottom: 8px;\n box-sizing: border-box; }\n .mdl-data-table th .mdl-data-table__select {\n position: absolute;\n bottom: 8px;\n left: 24px; }\n\n.mdl-data-table__select {\n width: 16px; }\n\n.mdl-data-table__cell--non-numeric.mdl-data-table__cell--non-numeric {\n text-align: left; }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* Typography */\n/* Shadows */\n/* Animations */\n.mdl-mega-footer {\n padding: 16px 40px;\n color: rgb(158,158,158);\n background-color: rgb(66,66,66); }\n\n.mdl-mega-footer--top-section:after,\n.mdl-mega-footer--middle-section:after,\n.mdl-mega-footer--bottom-section:after,\n.mdl-mega-footer__top-section:after,\n.mdl-mega-footer__middle-section:after,\n.mdl-mega-footer__bottom-section:after {\n content: '';\n display: block;\n clear: both; }\n\n.mdl-mega-footer--left-section,\n.mdl-mega-footer__left-section {\n margin-bottom: 16px; }\n\n.mdl-mega-footer--right-section,\n.mdl-mega-footer__right-section {\n margin-bottom: 16px; }\n\n.mdl-mega-footer--right-section a,\n.mdl-mega-footer__right-section a {\n display: block;\n margin-bottom: 16px;\n color: inherit;\n text-decoration: none; }\n\n@media screen and (min-width: 760px) {\n .mdl-mega-footer--left-section,\n .mdl-mega-footer__left-section {\n float: left; }\n .mdl-mega-footer--right-section,\n .mdl-mega-footer__right-section {\n float: right; }\n .mdl-mega-footer--right-section a,\n .mdl-mega-footer__right-section a {\n display: inline-block;\n margin-left: 16px;\n line-height: 36px;\n vertical-align: middle; } }\n\n.mdl-mega-footer--social-btn,\n.mdl-mega-footer__social-btn {\n width: 36px;\n height: 36px;\n padding: 0;\n margin: 0;\n background-color: rgb(158,158,158);\n border: none; }\n\n.mdl-mega-footer--drop-down-section,\n.mdl-mega-footer__drop-down-section {\n display: block;\n position: relative; }\n\n@media screen and (min-width: 760px) {\n .mdl-mega-footer--drop-down-section,\n .mdl-mega-footer__drop-down-section {\n width: 33%; }\n .mdl-mega-footer--drop-down-section:nth-child(1),\n .mdl-mega-footer--drop-down-section:nth-child(2),\n .mdl-mega-footer__drop-down-section:nth-child(1),\n .mdl-mega-footer__drop-down-section:nth-child(2) {\n float: left; }\n .mdl-mega-footer--drop-down-section:nth-child(3),\n .mdl-mega-footer__drop-down-section:nth-child(3) {\n float: right; }\n .mdl-mega-footer--drop-down-section:nth-child(3):after,\n .mdl-mega-footer__drop-down-section:nth-child(3):after {\n clear: right; }\n .mdl-mega-footer--drop-down-section:nth-child(4),\n .mdl-mega-footer__drop-down-section:nth-child(4) {\n clear: right;\n float: right; }\n .mdl-mega-footer--middle-section:after,\n .mdl-mega-footer__middle-section:after {\n content: '';\n display: block;\n clear: both; }\n .mdl-mega-footer--bottom-section,\n .mdl-mega-footer__bottom-section {\n padding-top: 0; } }\n\n@media screen and (min-width: 1024px) {\n .mdl-mega-footer--drop-down-section,\n .mdl-mega-footer--drop-down-section:nth-child(3),\n .mdl-mega-footer--drop-down-section:nth-child(4),\n .mdl-mega-footer__drop-down-section,\n .mdl-mega-footer__drop-down-section:nth-child(3),\n .mdl-mega-footer__drop-down-section:nth-child(4) {\n width: 24%;\n float: left; } }\n\n.mdl-mega-footer--heading-checkbox,\n.mdl-mega-footer__heading-checkbox {\n position: absolute;\n width: 100%;\n height: 55.8px;\n padding: 32px;\n margin: 0;\n margin-top: -16px;\n cursor: pointer;\n z-index: 1;\n opacity: 0; }\n .mdl-mega-footer--heading-checkbox + .mdl-mega-footer--heading:after,\n .mdl-mega-footer--heading-checkbox + .mdl-mega-footer__heading:after,\n .mdl-mega-footer__heading-checkbox + .mdl-mega-footer--heading:after,\n .mdl-mega-footer__heading-checkbox + .mdl-mega-footer__heading:after {\n font-family: 'Material Icons';\n content: '\\E5CE'; }\n\n.mdl-mega-footer--heading-checkbox:checked ~ .mdl-mega-footer--link-list,\n.mdl-mega-footer--heading-checkbox:checked ~ .mdl-mega-footer__link-list,\n.mdl-mega-footer--heading-checkbox:checked + .mdl-mega-footer--heading + .mdl-mega-footer--link-list,\n.mdl-mega-footer--heading-checkbox:checked + .mdl-mega-footer__heading + .mdl-mega-footer__link-list,\n.mdl-mega-footer__heading-checkbox:checked ~ .mdl-mega-footer--link-list,\n.mdl-mega-footer__heading-checkbox:checked ~ .mdl-mega-footer__link-list,\n.mdl-mega-footer__heading-checkbox:checked + .mdl-mega-footer--heading + .mdl-mega-footer--link-list,\n.mdl-mega-footer__heading-checkbox:checked + .mdl-mega-footer__heading + .mdl-mega-footer__link-list {\n display: none; }\n\n.mdl-mega-footer--heading-checkbox:checked + .mdl-mega-footer--heading:after,\n.mdl-mega-footer--heading-checkbox:checked + .mdl-mega-footer__heading:after,\n.mdl-mega-footer__heading-checkbox:checked + .mdl-mega-footer--heading:after,\n.mdl-mega-footer__heading-checkbox:checked + .mdl-mega-footer__heading:after {\n font-family: 'Material Icons';\n content: '\\E5CF'; }\n\n.mdl-mega-footer--heading,\n.mdl-mega-footer__heading {\n position: relative;\n width: 100%;\n padding-right: 39.8px;\n margin-bottom: 16px;\n box-sizing: border-box;\n font-size: 14px;\n line-height: 23.8px;\n font-weight: 500;\n white-space: nowrap;\n text-overflow: ellipsis;\n overflow: hidden;\n color: rgb(224,224,224); }\n\n.mdl-mega-footer--heading:after,\n.mdl-mega-footer__heading:after {\n content: '';\n position: absolute;\n top: 0;\n right: 0;\n display: block;\n width: 23.8px;\n height: 23.8px;\n background-size: cover; }\n\n.mdl-mega-footer--link-list,\n.mdl-mega-footer__link-list {\n list-style: none;\n margin: 0;\n padding: 0;\n margin-bottom: 32px; }\n .mdl-mega-footer--link-list:after,\n .mdl-mega-footer__link-list:after {\n clear: both;\n display: block;\n content: ''; }\n\n.mdl-mega-footer--link-list li,\n.mdl-mega-footer__link-list li {\n font-size: 14px;\n font-weight: 400;\n line-height: 24px;\n letter-spacing: 0;\n line-height: 20px; }\n\n.mdl-mega-footer--link-list a,\n.mdl-mega-footer__link-list a {\n color: inherit;\n text-decoration: none;\n white-space: nowrap; }\n\n@media screen and (min-width: 760px) {\n .mdl-mega-footer--heading-checkbox,\n .mdl-mega-footer__heading-checkbox {\n display: none; }\n .mdl-mega-footer--heading-checkbox + .mdl-mega-footer--heading:after,\n .mdl-mega-footer--heading-checkbox + .mdl-mega-footer__heading:after,\n .mdl-mega-footer__heading-checkbox + .mdl-mega-footer--heading:after,\n .mdl-mega-footer__heading-checkbox + .mdl-mega-footer__heading:after {\n background-image: none; }\n .mdl-mega-footer--heading-checkbox:checked ~ .mdl-mega-footer--link-list,\n .mdl-mega-footer--heading-checkbox:checked ~ .mdl-mega-footer__link-list,\n .mdl-mega-footer--heading-checkbox:checked + .mdl-mega-footer__heading + .mdl-mega-footer__link-list,\n .mdl-mega-footer--heading-checkbox:checked + .mdl-mega-footer--heading + .mdl-mega-footer--link-list,\n .mdl-mega-footer__heading-checkbox:checked ~ .mdl-mega-footer--link-list,\n .mdl-mega-footer__heading-checkbox:checked ~ .mdl-mega-footer__link-list,\n .mdl-mega-footer__heading-checkbox:checked + .mdl-mega-footer__heading + .mdl-mega-footer__link-list,\n .mdl-mega-footer__heading-checkbox:checked + .mdl-mega-footer--heading + .mdl-mega-footer--link-list {\n display: block; }\n .mdl-mega-footer--heading-checkbox:checked + .mdl-mega-footer--heading:after,\n .mdl-mega-footer--heading-checkbox:checked + .mdl-mega-footer__heading:after,\n .mdl-mega-footer__heading-checkbox:checked + .mdl-mega-footer--heading:after,\n .mdl-mega-footer__heading-checkbox:checked + .mdl-mega-footer__heading:after {\n content: ''; } }\n\n.mdl-mega-footer--bottom-section,\n.mdl-mega-footer__bottom-section {\n padding-top: 16px;\n margin-bottom: 16px; }\n\n.mdl-logo {\n margin-bottom: 16px;\n color: white; }\n\n.mdl-mega-footer--bottom-section .mdl-mega-footer--link-list li,\n.mdl-mega-footer__bottom-section .mdl-mega-footer__link-list li {\n float: left;\n margin-bottom: 0;\n margin-right: 16px; }\n\n@media screen and (min-width: 760px) {\n .mdl-logo {\n float: left;\n margin-bottom: 0;\n margin-right: 16px; } }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n.mdl-mini-footer {\n display: -webkit-box;\n display: -webkit-flex;\n display: -ms-flexbox;\n display: flex;\n -webkit-flex-flow: row wrap;\n -ms-flex-flow: row wrap;\n flex-flow: row wrap;\n -webkit-box-pack: justify;\n -webkit-justify-content: space-between;\n -ms-flex-pack: justify;\n justify-content: space-between;\n padding: 32px 16px;\n color: rgb(158,158,158);\n background-color: rgb(66,66,66); }\n .mdl-mini-footer:after {\n content: '';\n display: block; }\n .mdl-mini-footer .mdl-logo {\n line-height: 36px; }\n\n.mdl-mini-footer--link-list,\n.mdl-mini-footer__link-list {\n display: -webkit-box;\n display: -webkit-flex;\n display: -ms-flexbox;\n display: flex;\n -webkit-flex-flow: row nowrap;\n -ms-flex-flow: row nowrap;\n flex-flow: row nowrap;\n list-style: none;\n margin: 0;\n padding: 0; }\n .mdl-mini-footer--link-list li,\n .mdl-mini-footer__link-list li {\n margin-bottom: 0;\n margin-right: 16px; }\n @media screen and (min-width: 760px) {\n .mdl-mini-footer--link-list li,\n .mdl-mini-footer__link-list li {\n line-height: 36px; } }\n .mdl-mini-footer--link-list a,\n .mdl-mini-footer__link-list a {\n color: inherit;\n text-decoration: none;\n white-space: nowrap; }\n\n.mdl-mini-footer--left-section,\n.mdl-mini-footer__left-section {\n display: inline-block;\n -webkit-box-ordinal-group: 1;\n -webkit-order: 0;\n -ms-flex-order: 0;\n order: 0; }\n\n.mdl-mini-footer--right-section,\n.mdl-mini-footer__right-section {\n display: inline-block;\n -webkit-box-ordinal-group: 2;\n -webkit-order: 1;\n -ms-flex-order: 1;\n order: 1; }\n\n.mdl-mini-footer--social-btn,\n.mdl-mini-footer__social-btn {\n width: 36px;\n height: 36px;\n padding: 0;\n margin: 0;\n background-color: rgb(158,158,158);\n border: none; }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n.mdl-icon-toggle {\n position: relative;\n z-index: 1;\n vertical-align: middle;\n display: inline-block;\n height: 32px;\n margin: 0;\n padding: 0; }\n\n.mdl-icon-toggle__input {\n line-height: 32px; }\n .mdl-icon-toggle.is-upgraded .mdl-icon-toggle__input {\n position: absolute;\n width: 0;\n height: 0;\n margin: 0;\n padding: 0;\n opacity: 0;\n -ms-appearance: none;\n -moz-appearance: none;\n -webkit-appearance: none;\n appearance: none;\n border: none; }\n\n.mdl-icon-toggle__label {\n display: inline-block;\n position: relative;\n cursor: pointer;\n height: 32px;\n width: 32px;\n min-width: 32px;\n color: rgb(97,97,97);\n border-radius: 50%;\n padding: 0;\n margin-left: 0;\n margin-right: 0;\n text-align: center;\n background-color: transparent;\n will-change: background-color;\n -webkit-transition: background-color 0.2s cubic-bezier(0.4, 0, 0.2, 1), color 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n transition: background-color 0.2s cubic-bezier(0.4, 0, 0.2, 1), color 0.2s cubic-bezier(0.4, 0, 0.2, 1); }\n .mdl-icon-toggle__label.material-icons {\n line-height: 32px;\n font-size: 24px; }\n .mdl-icon-toggle.is-checked .mdl-icon-toggle__label {\n color: rgb(63,81,181); }\n .mdl-icon-toggle.is-disabled .mdl-icon-toggle__label {\n color: rgba(0,0,0, 0.26);\n cursor: auto;\n -webkit-transition: none;\n transition: none; }\n .mdl-icon-toggle.is-focused .mdl-icon-toggle__label {\n background-color: rgba(0,0,0, 0.12); }\n .mdl-icon-toggle.is-focused.is-checked .mdl-icon-toggle__label {\n background-color: rgba(63,81,181, 0.26); }\n\n.mdl-icon-toggle__ripple-container {\n position: absolute;\n z-index: 2;\n top: -2px;\n left: -2px;\n box-sizing: border-box;\n width: 36px;\n height: 36px;\n border-radius: 50%;\n cursor: pointer;\n overflow: hidden;\n -webkit-mask-image: -webkit-radial-gradient(circle, white, black); }\n .mdl-icon-toggle__ripple-container .mdl-ripple {\n background: rgb(97,97,97); }\n .mdl-icon-toggle.is-disabled .mdl-icon-toggle__ripple-container {\n cursor: auto; }\n .mdl-icon-toggle.is-disabled .mdl-icon-toggle__ripple-container .mdl-ripple {\n background: transparent; }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* Typography */\n/* Shadows */\n/* Animations */\n.mdl-menu__container {\n display: block;\n margin: 0;\n padding: 0;\n border: none;\n position: absolute;\n overflow: visible;\n height: 0;\n width: 0;\n visibility: hidden;\n z-index: -1; }\n .mdl-menu__container.is-visible, .mdl-menu__container.is-animating {\n z-index: 999;\n visibility: visible; }\n\n.mdl-menu__outline {\n display: block;\n background: rgb(255,255,255);\n margin: 0;\n padding: 0;\n border: none;\n border-radius: 2px;\n position: absolute;\n top: 0;\n left: 0;\n overflow: hidden;\n opacity: 0;\n -webkit-transform: scale(0);\n -ms-transform: scale(0);\n transform: scale(0);\n -webkit-transform-origin: 0 0;\n -ms-transform-origin: 0 0;\n transform-origin: 0 0;\n box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12);\n will-change: transform;\n -webkit-transition: -webkit-transform 0.3s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n z-index: -1; }\n .mdl-menu__container.is-visible .mdl-menu__outline {\n opacity: 1;\n -webkit-transform: scale(1);\n -ms-transform: scale(1);\n transform: scale(1);\n z-index: 999; }\n .mdl-menu__outline.mdl-menu--bottom-right {\n -webkit-transform-origin: 100% 0;\n -ms-transform-origin: 100% 0;\n transform-origin: 100% 0; }\n .mdl-menu__outline.mdl-menu--top-left {\n -webkit-transform-origin: 0 100%;\n -ms-transform-origin: 0 100%;\n transform-origin: 0 100%; }\n .mdl-menu__outline.mdl-menu--top-right {\n -webkit-transform-origin: 100% 100%;\n -ms-transform-origin: 100% 100%;\n transform-origin: 100% 100%; }\n\n.mdl-menu {\n position: absolute;\n list-style: none;\n top: 0;\n left: 0;\n height: auto;\n width: auto;\n min-width: 124px;\n padding: 8px 0;\n margin: 0;\n opacity: 0;\n clip: rect(0 0 0 0);\n z-index: -1; }\n .mdl-menu__container.is-visible .mdl-menu {\n opacity: 1;\n z-index: 999; }\n .mdl-menu.is-animating {\n -webkit-transition: opacity 0.2s cubic-bezier(0.4, 0, 0.2, 1), clip 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n transition: opacity 0.2s cubic-bezier(0.4, 0, 0.2, 1), clip 0.3s cubic-bezier(0.4, 0, 0.2, 1); }\n .mdl-menu.mdl-menu--bottom-right {\n left: auto;\n right: 0; }\n .mdl-menu.mdl-menu--top-left {\n top: auto;\n bottom: 0; }\n .mdl-menu.mdl-menu--top-right {\n top: auto;\n left: auto;\n bottom: 0;\n right: 0; }\n .mdl-menu.mdl-menu--unaligned {\n top: auto;\n left: auto; }\n\n.mdl-menu__item {\n display: block;\n border: none;\n color: rgba(0,0,0, 0.87);\n background-color: transparent;\n text-align: left;\n margin: 0;\n padding: 0 16px;\n outline-color: rgb(189,189,189);\n position: relative;\n overflow: hidden;\n font-size: 14px;\n font-weight: 400;\n line-height: 24px;\n letter-spacing: 0;\n text-decoration: none;\n cursor: pointer;\n height: 48px;\n line-height: 48px;\n white-space: nowrap;\n opacity: 0;\n -webkit-transition: opacity 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n transition: opacity 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n -webkit-user-select: none;\n -moz-user-select: none;\n -ms-user-select: none;\n user-select: none; }\n .mdl-menu__container.is-visible .mdl-menu__item {\n opacity: 1; }\n .mdl-menu__item::-moz-focus-inner {\n border: 0; }\n .mdl-menu__item[disabled] {\n color: rgb(189,189,189);\n background-color: transparent;\n cursor: auto; }\n .mdl-menu__item[disabled]:hover {\n background-color: transparent; }\n .mdl-menu__item[disabled]:focus {\n background-color: transparent; }\n .mdl-menu__item[disabled] .mdl-ripple {\n background: transparent; }\n .mdl-menu__item:hover {\n background-color: rgb(238,238,238); }\n .mdl-menu__item:focus {\n outline: none;\n background-color: rgb(238,238,238); }\n .mdl-menu__item:active {\n background-color: rgb(224,224,224); }\n\n.mdl-menu__item--ripple-container {\n display: block;\n height: 100%;\n left: 0px;\n position: absolute;\n top: 0px;\n width: 100%;\n z-index: 0;\n overflow: hidden; }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n.mdl-progress {\n display: block;\n position: relative;\n height: 4px;\n width: 500px; }\n\n.mdl-progress > .bar {\n display: block;\n position: absolute;\n top: 0;\n bottom: 0;\n width: 0%;\n -webkit-transition: width 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n transition: width 0.2s cubic-bezier(0.4, 0, 0.2, 1); }\n\n.mdl-progress > .progressbar {\n background-color: rgb(63,81,181);\n z-index: 1;\n left: 0; }\n\n.mdl-progress > .bufferbar {\n background-image: -webkit-linear-gradient(left, rgba(255,255,255, 0.7), rgba(255,255,255, 0.7)), -webkit-linear-gradient(left, rgb(63,81,181), rgb(63,81,181));\n background-image: linear-gradient(to right, rgba(255,255,255, 0.7), rgba(255,255,255, 0.7)), linear-gradient(to right, rgb(63,81,181), rgb(63,81,181));\n z-index: 0;\n left: 0; }\n\n.mdl-progress > .auxbar {\n right: 0; }\n\n@supports (-webkit-appearance: none) {\n .mdl-progress:not(.mdl-progress__indeterminate):not(.mdl-progress__indeterminate) > .auxbar {\n background-image: -webkit-linear-gradient(left, rgba(255,255,255, 0.7), rgba(255,255,255, 0.7)), -webkit-linear-gradient(left, rgb(63,81,181), rgb(63,81,181));\n background-image: linear-gradient(to right, rgba(255,255,255, 0.7), rgba(255,255,255, 0.7)), linear-gradient(to right, rgb(63,81,181), rgb(63,81,181));\n -webkit-mask: url(\"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIj8+Cjxzdmcgd2lkdGg9IjEyIiBoZWlnaHQ9IjQiIHZpZXdQb3J0PSIwIDAgMTIgNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgogIDxlbGxpcHNlIGN4PSIyIiBjeT0iMiIgcng9IjIiIHJ5PSIyIj4KICAgIDxhbmltYXRlIGF0dHJpYnV0ZU5hbWU9ImN4IiBmcm9tPSIyIiB0bz0iLTEwIiBkdXI9IjAuNnMiIHJlcGVhdENvdW50PSJpbmRlZmluaXRlIiAvPgogIDwvZWxsaXBzZT4KICA8ZWxsaXBzZSBjeD0iMTQiIGN5PSIyIiByeD0iMiIgcnk9IjIiIGNsYXNzPSJsb2FkZXIiPgogICAgPGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iY3giIGZyb209IjE0IiB0bz0iMiIgZHVyPSIwLjZzIiByZXBlYXRDb3VudD0iaW5kZWZpbml0ZSIgLz4KICA8L2VsbGlwc2U+Cjwvc3ZnPgo=\");\n mask: url(\"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIj8+Cjxzdmcgd2lkdGg9IjEyIiBoZWlnaHQ9IjQiIHZpZXdQb3J0PSIwIDAgMTIgNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgogIDxlbGxpcHNlIGN4PSIyIiBjeT0iMiIgcng9IjIiIHJ5PSIyIj4KICAgIDxhbmltYXRlIGF0dHJpYnV0ZU5hbWU9ImN4IiBmcm9tPSIyIiB0bz0iLTEwIiBkdXI9IjAuNnMiIHJlcGVhdENvdW50PSJpbmRlZmluaXRlIiAvPgogIDwvZWxsaXBzZT4KICA8ZWxsaXBzZSBjeD0iMTQiIGN5PSIyIiByeD0iMiIgcnk9IjIiIGNsYXNzPSJsb2FkZXIiPgogICAgPGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iY3giIGZyb209IjE0IiB0bz0iMiIgZHVyPSIwLjZzIiByZXBlYXRDb3VudD0iaW5kZWZpbml0ZSIgLz4KICA8L2VsbGlwc2U+Cjwvc3ZnPgo=\"); } }\n\n.mdl-progress:not(.mdl-progress__indeterminate) > .auxbar {\n background-image: -webkit-linear-gradient(left, rgba(255,255,255, 0.9), rgba(255,255,255, 0.9)), -webkit-linear-gradient(left, rgb(63,81,181), rgb(63,81,181));\n background-image: linear-gradient(to right, rgba(255,255,255, 0.9), rgba(255,255,255, 0.9)), linear-gradient(to right, rgb(63,81,181), rgb(63,81,181)); }\n\n.mdl-progress.mdl-progress__indeterminate > .bar1 {\n background-color: rgb(63,81,181);\n -webkit-animation-name: indeterminate1;\n animation-name: indeterminate1;\n -webkit-animation-duration: 2s;\n animation-duration: 2s;\n -webkit-animation-iteration-count: infinite;\n animation-iteration-count: infinite;\n -webkit-animation-timing-function: linear;\n animation-timing-function: linear; }\n\n.mdl-progress.mdl-progress__indeterminate > .bar3 {\n background-image: none;\n background-color: rgb(63,81,181);\n -webkit-animation-name: indeterminate2;\n animation-name: indeterminate2;\n -webkit-animation-duration: 2s;\n animation-duration: 2s;\n -webkit-animation-iteration-count: infinite;\n animation-iteration-count: infinite;\n -webkit-animation-timing-function: linear;\n animation-timing-function: linear; }\n\n@-webkit-keyframes indeterminate1 {\n 0% {\n left: 0%;\n width: 0%; }\n 50% {\n left: 25%;\n width: 75%; }\n 75% {\n left: 100%;\n width: 0%; } }\n\n@keyframes indeterminate1 {\n 0% {\n left: 0%;\n width: 0%; }\n 50% {\n left: 25%;\n width: 75%; }\n 75% {\n left: 100%;\n width: 0%; } }\n\n@-webkit-keyframes indeterminate2 {\n 0% {\n left: 0%;\n width: 0%; }\n 50% {\n left: 0%;\n width: 0%; }\n 75% {\n left: 0%;\n width: 25%; }\n 100% {\n left: 100%;\n width: 0%; } }\n\n@keyframes indeterminate2 {\n 0% {\n left: 0%;\n width: 0%; }\n 50% {\n left: 0%;\n width: 0%; }\n 75% {\n left: 0%;\n width: 25%; }\n 100% {\n left: 100%;\n width: 0%; } }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* Typography */\n/* Shadows */\n/* Animations */\n.mdl-navigation {\n display: -webkit-box;\n display: -webkit-flex;\n display: -ms-flexbox;\n display: flex;\n -webkit-flex-wrap: nowrap;\n -ms-flex-wrap: nowrap;\n flex-wrap: nowrap;\n box-sizing: border-box; }\n\n.mdl-navigation__link {\n color: rgb(66,66,66);\n text-decoration: none;\n font-weight: 500;\n font-size: 13px;\n margin: 0; }\n\n.mdl-layout {\n width: 100%;\n height: 100%;\n display: -webkit-box;\n display: -webkit-flex;\n display: -ms-flexbox;\n display: flex;\n -webkit-box-orient: vertical;\n -webkit-box-direction: normal;\n -webkit-flex-direction: column;\n -ms-flex-direction: column;\n flex-direction: column;\n overflow-y: auto;\n overflow-x: hidden;\n position: relative;\n -webkit-overflow-scrolling: touch; }\n\n.mdl-layout.is-small-screen .mdl-layout--large-screen-only {\n display: none; }\n\n.mdl-layout:not(.is-small-screen) .mdl-layout--small-screen-only {\n display: none; }\n\n.mdl-layout__container {\n position: absolute;\n width: 100%;\n height: 100%; }\n\n.mdl-layout__title,\n.mdl-layout-title {\n display: block;\n position: relative;\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 20px;\n font-weight: 500;\n line-height: 1;\n letter-spacing: 0.02em;\n font-weight: 400;\n box-sizing: border-box; }\n\n.mdl-layout-spacer {\n -webkit-box-flex: 1;\n -webkit-flex-grow: 1;\n -ms-flex-positive: 1;\n flex-grow: 1; }\n\n.mdl-layout__drawer {\n display: -webkit-box;\n display: -webkit-flex;\n display: -ms-flexbox;\n display: flex;\n -webkit-box-orient: vertical;\n -webkit-box-direction: normal;\n -webkit-flex-direction: column;\n -ms-flex-direction: column;\n flex-direction: column;\n -webkit-flex-wrap: nowrap;\n -ms-flex-wrap: nowrap;\n flex-wrap: nowrap;\n width: 240px;\n height: 100%;\n max-height: 100%;\n position: absolute;\n top: 0;\n left: 0;\n box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12);\n box-sizing: border-box;\n border-right: 1px solid rgb(224,224,224);\n background: rgb(250,250,250);\n -webkit-transform: translateX(-250px);\n -ms-transform: translateX(-250px);\n transform: translateX(-250px);\n -webkit-transform-style: preserve-3d;\n transform-style: preserve-3d;\n will-change: transform;\n -webkit-transition-duration: 0.2s;\n transition-duration: 0.2s;\n -webkit-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n -webkit-transition-property: -webkit-transform;\n transition-property: transform;\n color: rgb(66,66,66);\n overflow: visible;\n overflow-y: auto;\n z-index: 5; }\n .mdl-layout__drawer.is-visible {\n -webkit-transform: translateX(0);\n -ms-transform: translateX(0);\n transform: translateX(0); }\n .mdl-layout__drawer.is-visible ~ .mdl-layout__content.mdl-layout__content {\n overflow: hidden; }\n .mdl-layout__drawer > * {\n -webkit-flex-shrink: 0;\n -ms-flex-negative: 0;\n flex-shrink: 0; }\n .mdl-layout__drawer > .mdl-layout__title,\n .mdl-layout__drawer > .mdl-layout-title {\n line-height: 64px;\n padding-left: 40px; }\n @media screen and (max-width: 1024px) {\n .mdl-layout__drawer > .mdl-layout__title,\n .mdl-layout__drawer > .mdl-layout-title {\n line-height: 56px;\n padding-left: 16px; } }\n .mdl-layout__drawer .mdl-navigation {\n -webkit-box-orient: vertical;\n -webkit-box-direction: normal;\n -webkit-flex-direction: column;\n -ms-flex-direction: column;\n flex-direction: column;\n -webkit-box-align: stretch;\n -webkit-align-items: stretch;\n -ms-flex-align: stretch;\n align-items: stretch;\n padding-top: 16px; }\n .mdl-layout__drawer .mdl-navigation .mdl-navigation__link {\n display: block;\n -webkit-flex-shrink: 0;\n -ms-flex-negative: 0;\n flex-shrink: 0;\n padding: 16px 40px;\n margin: 0;\n color: #757575; }\n @media screen and (max-width: 1024px) {\n .mdl-layout__drawer .mdl-navigation .mdl-navigation__link {\n padding: 16px 16px; } }\n .mdl-layout__drawer .mdl-navigation .mdl-navigation__link:hover {\n background-color: rgb(224,224,224); }\n .mdl-layout__drawer .mdl-navigation .mdl-navigation__link--current {\n background-color: rgb(0,0,0);\n color: rgb(224,224,224); }\n @media screen and (min-width: 1025px) {\n .mdl-layout--fixed-drawer > .mdl-layout__drawer {\n -webkit-transform: translateX(0);\n -ms-transform: translateX(0);\n transform: translateX(0); } }\n\n.mdl-layout__drawer-button {\n display: block;\n position: absolute;\n height: 48px;\n width: 48px;\n border: 0;\n -webkit-flex-shrink: 0;\n -ms-flex-negative: 0;\n flex-shrink: 0;\n overflow: hidden;\n text-align: center;\n cursor: pointer;\n font-size: 26px;\n line-height: 50px;\n font-family: Helvetica, Arial, sans-serif;\n margin: 10px 12px;\n top: 0;\n left: 0;\n color: rgb(255,255,255);\n z-index: 4; }\n .mdl-layout__header .mdl-layout__drawer-button {\n position: absolute;\n color: rgb(255,255,255);\n background-color: inherit; }\n @media screen and (max-width: 1024px) {\n .mdl-layout__header .mdl-layout__drawer-button {\n margin: 4px; } }\n @media screen and (max-width: 1024px) {\n .mdl-layout__drawer-button {\n margin: 4px;\n color: rgba(0, 0, 0, 0.5); } }\n @media screen and (min-width: 1025px) {\n .mdl-layout--fixed-drawer > .mdl-layout__drawer-button {\n display: none; } }\n\n.mdl-layout__header {\n display: -webkit-box;\n display: -webkit-flex;\n display: -ms-flexbox;\n display: flex;\n -webkit-box-orient: vertical;\n -webkit-box-direction: normal;\n -webkit-flex-direction: column;\n -ms-flex-direction: column;\n flex-direction: column;\n -webkit-flex-wrap: nowrap;\n -ms-flex-wrap: nowrap;\n flex-wrap: nowrap;\n -webkit-box-pack: start;\n -webkit-justify-content: flex-start;\n -ms-flex-pack: start;\n justify-content: flex-start;\n box-sizing: border-box;\n -webkit-flex-shrink: 0;\n -ms-flex-negative: 0;\n flex-shrink: 0;\n width: 100%;\n margin: 0;\n padding: 0;\n border: none;\n min-height: 64px;\n max-height: 1000px;\n z-index: 3;\n background-color: rgb(63,81,181);\n color: rgb(255,255,255);\n box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12);\n -webkit-transition-duration: 0.2s;\n transition-duration: 0.2s;\n -webkit-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n -webkit-transition-property: max-height, box-shadow;\n transition-property: max-height, box-shadow; }\n @media screen and (max-width: 1024px) {\n .mdl-layout__header {\n min-height: 56px; } }\n .mdl-layout--fixed-drawer.is-upgraded:not(.is-small-screen) > .mdl-layout__header {\n margin-left: 240px;\n width: calc(100% - 240px); }\n @media screen and (min-width: 1025px) {\n .mdl-layout--fixed-drawer > .mdl-layout__header .mdl-layout__header-row {\n padding-left: 40px; } }\n .mdl-layout__header > .mdl-layout-icon {\n position: absolute;\n left: 40px;\n top: 16px;\n height: 32px;\n width: 32px;\n overflow: hidden;\n z-index: 3;\n display: block; }\n @media screen and (max-width: 1024px) {\n .mdl-layout__header > .mdl-layout-icon {\n left: 16px;\n top: 12px; } }\n .mdl-layout.has-drawer .mdl-layout__header > .mdl-layout-icon {\n display: none; }\n .mdl-layout__header.is-compact {\n max-height: 64px; }\n @media screen and (max-width: 1024px) {\n .mdl-layout__header.is-compact {\n max-height: 56px; } }\n .mdl-layout__header.is-compact.has-tabs {\n height: 112px; }\n @media screen and (max-width: 1024px) {\n .mdl-layout__header.is-compact.has-tabs {\n min-height: 104px; } }\n @media screen and (max-width: 1024px) {\n .mdl-layout__header {\n display: none; }\n .mdl-layout--fixed-header > .mdl-layout__header {\n display: -webkit-box;\n display: -webkit-flex;\n display: -ms-flexbox;\n display: flex; } }\n\n.mdl-layout__header--transparent.mdl-layout__header--transparent {\n background-color: transparent;\n box-shadow: none; }\n\n.mdl-layout__header--seamed {\n box-shadow: none; }\n\n.mdl-layout__header--scroll {\n box-shadow: none; }\n\n.mdl-layout__header--waterfall {\n box-shadow: none;\n overflow: hidden; }\n .mdl-layout__header--waterfall.is-casting-shadow {\n box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12); }\n\n.mdl-layout__header-row {\n display: -webkit-box;\n display: -webkit-flex;\n display: -ms-flexbox;\n display: flex;\n -webkit-box-orient: horizontal;\n -webkit-box-direction: normal;\n -webkit-flex-direction: row;\n -ms-flex-direction: row;\n flex-direction: row;\n -webkit-flex-wrap: nowrap;\n -ms-flex-wrap: nowrap;\n flex-wrap: nowrap;\n -webkit-flex-shrink: 0;\n -ms-flex-negative: 0;\n flex-shrink: 0;\n box-sizing: border-box;\n -webkit-align-self: stretch;\n -ms-flex-item-align: stretch;\n align-self: stretch;\n -webkit-box-align: center;\n -webkit-align-items: center;\n -ms-flex-align: center;\n align-items: center;\n height: 64px;\n margin: 0;\n padding: 0 40px 0 80px; }\n @media screen and (max-width: 1024px) {\n .mdl-layout__header-row {\n height: 56px;\n padding: 0 16px 0 72px; } }\n .mdl-layout__header-row > * {\n -webkit-flex-shrink: 0;\n -ms-flex-negative: 0;\n flex-shrink: 0; }\n .mdl-layout__header--scroll .mdl-layout__header-row {\n width: 100%; }\n .mdl-layout__header-row .mdl-navigation {\n margin: 0;\n padding: 0;\n height: 64px;\n -webkit-box-orient: horizontal;\n -webkit-box-direction: normal;\n -webkit-flex-direction: row;\n -ms-flex-direction: row;\n flex-direction: row;\n -webkit-box-align: center;\n -webkit-align-items: center;\n -ms-flex-align: center;\n align-items: center; }\n @media screen and (max-width: 1024px) {\n .mdl-layout__header-row .mdl-navigation {\n height: 56px; } }\n .mdl-layout__header-row .mdl-navigation__link {\n display: block;\n color: rgb(255,255,255);\n line-height: 64px;\n padding: 0 24px; }\n @media screen and (max-width: 1024px) {\n .mdl-layout__header-row .mdl-navigation__link {\n line-height: 56px;\n padding: 0 16px; } }\n\n.mdl-layout__obfuscator {\n background-color: transparent;\n position: absolute;\n top: 0;\n left: 0;\n height: 100%;\n width: 100%;\n z-index: 4;\n visibility: hidden;\n -webkit-transition-property: background-color;\n transition-property: background-color;\n -webkit-transition-duration: 0.2s;\n transition-duration: 0.2s;\n -webkit-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); }\n .mdl-layout__obfuscator.is-visible {\n background-color: rgba(0, 0, 0, 0.5);\n visibility: visible; }\n\n.mdl-layout__content {\n -ms-flex: 0 1 auto;\n display: inline-block;\n overflow-y: auto;\n overflow-x: hidden;\n -webkit-box-flex: 1;\n -webkit-flex-grow: 1;\n -ms-flex-positive: 1;\n flex-grow: 1;\n z-index: 1;\n -webkit-overflow-scrolling: touch; }\n .mdl-layout--fixed-drawer > .mdl-layout__content {\n margin-left: 240px; }\n .mdl-layout__container.has-scrolling-header .mdl-layout__content {\n overflow: visible; }\n @media screen and (max-width: 1024px) {\n .mdl-layout--fixed-drawer > .mdl-layout__content {\n margin-left: 0; }\n .mdl-layout__container.has-scrolling-header .mdl-layout__content {\n overflow-y: auto;\n overflow-x: hidden; } }\n\n.mdl-layout__tab-bar {\n height: 96px;\n margin: 0;\n width: calc(100% - 112px);\n padding: 0 0 0 56px;\n display: -webkit-box;\n display: -webkit-flex;\n display: -ms-flexbox;\n display: flex;\n background-color: rgb(63,81,181);\n overflow-y: hidden;\n overflow-x: scroll; }\n .mdl-layout__tab-bar::-webkit-scrollbar {\n display: none; }\n @media screen and (max-width: 1024px) {\n .mdl-layout__tab-bar {\n width: calc(100% - 60px);\n padding: 0 0 0 60px; } }\n .mdl-layout--fixed-tabs .mdl-layout__tab-bar {\n padding: 0;\n overflow: hidden;\n width: 100%; }\n\n.mdl-layout__tab-bar-container {\n position: relative;\n height: 48px;\n width: 100%;\n border: none;\n margin: 0;\n z-index: 2;\n -webkit-box-flex: 0;\n -webkit-flex-grow: 0;\n -ms-flex-positive: 0;\n flex-grow: 0;\n -webkit-flex-shrink: 0;\n -ms-flex-negative: 0;\n flex-shrink: 0;\n overflow: hidden; }\n .mdl-layout__container > .mdl-layout__tab-bar-container {\n position: absolute;\n top: 0;\n left: 0; }\n\n.mdl-layout__tab-bar-button {\n display: inline-block;\n position: absolute;\n top: 0;\n height: 48px;\n width: 56px;\n z-index: 4;\n text-align: center;\n background-color: rgb(63,81,181);\n color: transparent;\n cursor: pointer;\n -webkit-user-select: none;\n -moz-user-select: none;\n -ms-user-select: none;\n user-select: none; }\n @media screen and (max-width: 1024px) {\n .mdl-layout__tab-bar-button {\n display: none;\n width: 60px; } }\n .mdl-layout--fixed-tabs .mdl-layout__tab-bar-button {\n display: none; }\n .mdl-layout__tab-bar-button .material-icons {\n line-height: 48px; }\n .mdl-layout__tab-bar-button.is-active {\n color: rgb(255,255,255); }\n\n.mdl-layout__tab-bar-left-button {\n left: 0; }\n\n.mdl-layout__tab-bar-right-button {\n right: 0; }\n\n.mdl-layout__tab {\n margin: 0;\n border: none;\n padding: 0 24px 0 24px;\n float: left;\n position: relative;\n display: block;\n -webkit-box-flex: 0;\n -webkit-flex-grow: 0;\n -ms-flex-positive: 0;\n flex-grow: 0;\n -webkit-flex-shrink: 0;\n -ms-flex-negative: 0;\n flex-shrink: 0;\n text-decoration: none;\n height: 48px;\n line-height: 48px;\n text-align: center;\n font-weight: 500;\n font-size: 14px;\n text-transform: uppercase;\n color: rgba(255,255,255, 0.6);\n overflow: hidden; }\n @media screen and (max-width: 1024px) {\n .mdl-layout__tab {\n padding: 0 12px 0 12px; } }\n .mdl-layout--fixed-tabs .mdl-layout__tab {\n float: none;\n -webkit-box-flex: 1;\n -webkit-flex-grow: 1;\n -ms-flex-positive: 1;\n flex-grow: 1;\n padding: 0; }\n .mdl-layout.is-upgraded .mdl-layout__tab.is-active {\n color: rgb(255,255,255); }\n .mdl-layout.is-upgraded .mdl-layout__tab.is-active::after {\n height: 2px;\n width: 100%;\n display: block;\n content: \" \";\n bottom: 0;\n left: 0;\n position: absolute;\n background: rgb(255,64,129);\n -webkit-animation: border-expand 0.2s cubic-bezier(0.4, 0, 0.4, 1) 0.01s alternate forwards;\n animation: border-expand 0.2s cubic-bezier(0.4, 0, 0.4, 1) 0.01s alternate forwards;\n -webkit-transition: all 1s cubic-bezier(0.4, 0, 1, 1);\n transition: all 1s cubic-bezier(0.4, 0, 1, 1); }\n .mdl-layout__tab .mdl-layout__tab-ripple-container {\n display: block;\n position: absolute;\n height: 100%;\n width: 100%;\n left: 0;\n top: 0;\n z-index: 1;\n overflow: hidden; }\n .mdl-layout__tab .mdl-layout__tab-ripple-container .mdl-ripple {\n background-color: rgb(255,255,255); }\n\n.mdl-layout__tab-panel {\n display: block; }\n .mdl-layout.is-upgraded .mdl-layout__tab-panel {\n display: none; }\n .mdl-layout.is-upgraded .mdl-layout__tab-panel.is-active {\n display: block; }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* Typography */\n/* Shadows */\n/* Animations */\n.mdl-radio {\n position: relative;\n font-size: 16px;\n line-height: 24px;\n display: inline-block;\n box-sizing: border-box;\n margin: 0;\n padding-left: 0; }\n .mdl-radio.is-upgraded {\n padding-left: 24px; }\n\n.mdl-radio__button {\n line-height: 24px; }\n .mdl-radio.is-upgraded .mdl-radio__button {\n position: absolute;\n width: 0;\n height: 0;\n margin: 0;\n padding: 0;\n opacity: 0;\n -ms-appearance: none;\n -moz-appearance: none;\n -webkit-appearance: none;\n appearance: none;\n border: none; }\n\n.mdl-radio__outer-circle {\n position: absolute;\n top: 4px;\n left: 0;\n display: inline-block;\n box-sizing: border-box;\n width: 16px;\n height: 16px;\n margin: 0;\n cursor: pointer;\n border: 2px solid rgba(0,0,0, 0.54);\n border-radius: 50%;\n z-index: 2; }\n .mdl-radio.is-checked .mdl-radio__outer-circle {\n border: 2px solid rgb(63,81,181); }\n .mdl-radio.is-disabled .mdl-radio__outer-circle {\n border: 2px solid rgba(0,0,0, 0.26);\n cursor: auto; }\n\n.mdl-radio__inner-circle {\n position: absolute;\n z-index: 1;\n margin: 0;\n top: 8px;\n left: 4px;\n box-sizing: border-box;\n width: 8px;\n height: 8px;\n cursor: pointer;\n -webkit-transition-duration: 0.28s;\n transition-duration: 0.28s;\n -webkit-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n -webkit-transition-property: -webkit-transform;\n transition-property: transform;\n -webkit-transform: scale3d(0, 0, 0);\n transform: scale3d(0, 0, 0);\n border-radius: 50%;\n background: rgb(63,81,181); }\n .mdl-radio.is-checked .mdl-radio__inner-circle {\n -webkit-transform: scale3d(1, 1, 1);\n transform: scale3d(1, 1, 1); }\n .mdl-radio.is-disabled .mdl-radio__inner-circle {\n background: rgba(0,0,0, 0.26);\n cursor: auto; }\n .mdl-radio.is-focused .mdl-radio__inner-circle {\n box-shadow: 0 0 0px 10px rgba(0, 0, 0, 0.1); }\n\n.mdl-radio__label {\n cursor: pointer; }\n .mdl-radio.is-disabled .mdl-radio__label {\n color: rgba(0,0,0, 0.26);\n cursor: auto; }\n\n.mdl-radio__ripple-container {\n position: absolute;\n z-index: 2;\n top: -9px;\n left: -13px;\n box-sizing: border-box;\n width: 42px;\n height: 42px;\n border-radius: 50%;\n cursor: pointer;\n overflow: hidden;\n -webkit-mask-image: -webkit-radial-gradient(circle, white, black); }\n .mdl-radio__ripple-container .mdl-ripple {\n background: rgb(63,81,181); }\n .mdl-radio.is-disabled .mdl-radio__ripple-container {\n cursor: auto; }\n .mdl-radio.is-disabled .mdl-radio__ripple-container .mdl-ripple {\n background: transparent; }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n_:-ms-input-placeholder, :root .mdl-slider.mdl-slider.is-upgraded {\n -ms-appearance: none;\n height: 32px;\n margin: 0; }\n\n.mdl-slider {\n width: calc(100% - 40px);\n margin: 0 20px; }\n .mdl-slider.is-upgraded {\n -webkit-appearance: none;\n -moz-appearance: none;\n appearance: none;\n height: 2px;\n background: transparent;\n -webkit-user-select: none;\n -moz-user-select: none;\n -ms-user-select: none;\n user-select: none;\n outline: 0;\n padding: 0;\n color: rgb(63,81,181);\n -webkit-align-self: center;\n -ms-flex-item-align: center;\n align-self: center;\n z-index: 1;\n cursor: pointer;\n /**************************** Tracks ****************************/\n /**************************** Thumbs ****************************/\n /**************************** 0-value ****************************/\n /**************************** Disabled ****************************/ }\n .mdl-slider.is-upgraded::-moz-focus-outer {\n border: 0; }\n .mdl-slider.is-upgraded::-ms-tooltip {\n display: none; }\n .mdl-slider.is-upgraded::-webkit-slider-runnable-track {\n background: transparent; }\n .mdl-slider.is-upgraded::-moz-range-track {\n background: transparent;\n border: none; }\n .mdl-slider.is-upgraded::-ms-track {\n background: none;\n color: transparent;\n height: 2px;\n width: 100%;\n border: none; }\n .mdl-slider.is-upgraded::-ms-fill-lower {\n padding: 0;\n background: linear-gradient(to right, transparent, transparent 16px, rgb(63,81,181) 16px, rgb(63,81,181) 0); }\n .mdl-slider.is-upgraded::-ms-fill-upper {\n padding: 0;\n background: linear-gradient(to left, transparent, transparent 16px, rgba(0,0,0, 0.26) 16px, rgba(0,0,0, 0.26) 0); }\n .mdl-slider.is-upgraded::-webkit-slider-thumb {\n -webkit-appearance: none;\n width: 12px;\n height: 12px;\n box-sizing: border-box;\n border-radius: 50%;\n background: rgb(63,81,181);\n border: none;\n -webkit-transition: -webkit-transform 0.18s cubic-bezier(0.4, 0, 0.2, 1), border 0.18s cubic-bezier(0.4, 0, 0.2, 1), box-shadow 0.18s cubic-bezier(0.4, 0, 0.2, 1), background 0.28s cubic-bezier(0.4, 0, 0.2, 1);\n transition: transform 0.18s cubic-bezier(0.4, 0, 0.2, 1), border 0.18s cubic-bezier(0.4, 0, 0.2, 1), box-shadow 0.18s cubic-bezier(0.4, 0, 0.2, 1), background 0.28s cubic-bezier(0.4, 0, 0.2, 1); }\n .mdl-slider.is-upgraded::-moz-range-thumb {\n -moz-appearance: none;\n width: 12px;\n height: 12px;\n box-sizing: border-box;\n border-radius: 50%;\n background-image: none;\n background: rgb(63,81,181);\n border: none; }\n .mdl-slider.is-upgraded:focus:not(:active)::-webkit-slider-thumb {\n box-shadow: 0 0 0 10px rgba(63,81,181, 0.26); }\n .mdl-slider.is-upgraded:focus:not(:active)::-moz-range-thumb {\n box-shadow: 0 0 0 10px rgba(63,81,181, 0.26); }\n .mdl-slider.is-upgraded:active::-webkit-slider-thumb {\n background-image: none;\n background: rgb(63,81,181);\n -webkit-transform: scale(1.5);\n transform: scale(1.5); }\n .mdl-slider.is-upgraded:active::-moz-range-thumb {\n background-image: none;\n background: rgb(63,81,181);\n transform: scale(1.5); }\n .mdl-slider.is-upgraded::-ms-thumb {\n width: 32px;\n height: 32px;\n border: none;\n border-radius: 50%;\n background: rgb(63,81,181);\n -ms-transform: scale(0.375);\n transform: scale(0.375);\n transition: transform 0.18s cubic-bezier(0.4, 0, 0.2, 1), background 0.28s cubic-bezier(0.4, 0, 0.2, 1); }\n .mdl-slider.is-upgraded:focus:not(:active)::-ms-thumb {\n background: radial-gradient(circle closest-side, rgb(63,81,181) 0%, rgb(63,81,181) 37.5%, rgba(63,81,181, 0.26) 37.5%, rgba(63,81,181, 0.26) 100%);\n -ms-transform: scale(1);\n transform: scale(1); }\n .mdl-slider.is-upgraded:active::-ms-thumb {\n background: rgb(63,81,181);\n -ms-transform: scale(0.5625);\n transform: scale(0.5625); }\n .mdl-slider.is-upgraded.is-lowest-value::-webkit-slider-thumb {\n border: 2px solid rgba(0,0,0, 0.26);\n background: transparent; }\n .mdl-slider.is-upgraded.is-lowest-value::-moz-range-thumb {\n border: 2px solid rgba(0,0,0, 0.26);\n background: transparent; }\n .mdl-slider.is-upgraded.is-lowest-value +\n.mdl-slider__background-flex > .mdl-slider__background-upper {\n left: 6px; }\n .mdl-slider.is-upgraded.is-lowest-value:focus:not(:active)::-webkit-slider-thumb {\n box-shadow: 0 0 0 10px rgba(0,0,0, 0.12);\n background: rgba(0,0,0, 0.12); }\n .mdl-slider.is-upgraded.is-lowest-value:focus:not(:active)::-moz-range-thumb {\n box-shadow: 0 0 0 10px rgba(0,0,0, 0.12);\n background: rgba(0,0,0, 0.12); }\n .mdl-slider.is-upgraded.is-lowest-value:active::-webkit-slider-thumb {\n border: 1.6px solid rgba(0,0,0, 0.26);\n -webkit-transform: scale(1.5);\n transform: scale(1.5); }\n .mdl-slider.is-upgraded.is-lowest-value:active +\n.mdl-slider__background-flex > .mdl-slider__background-upper {\n left: 9px; }\n .mdl-slider.is-upgraded.is-lowest-value:active::-moz-range-thumb {\n border: 1.5px solid rgba(0,0,0, 0.26);\n transform: scale(1.5); }\n .mdl-slider.is-upgraded.is-lowest-value::-ms-thumb {\n background: radial-gradient(circle closest-side, transparent 0%, transparent 66.67%, rgba(0,0,0, 0.26) 66.67%, rgba(0,0,0, 0.26) 100%); }\n .mdl-slider.is-upgraded.is-lowest-value:focus:not(:active)::-ms-thumb {\n background: radial-gradient(circle closest-side, rgba(0,0,0, 0.12) 0%, rgba(0,0,0, 0.12) 25%, rgba(0,0,0, 0.26) 25%, rgba(0,0,0, 0.26) 37.5%, rgba(0,0,0, 0.12) 37.5%, rgba(0,0,0, 0.12) 100%);\n -ms-transform: scale(1);\n transform: scale(1); }\n .mdl-slider.is-upgraded.is-lowest-value:active::-ms-thumb {\n -ms-transform: scale(0.5625);\n transform: scale(0.5625);\n background: radial-gradient(circle closest-side, transparent 0%, transparent 77.78%, rgba(0,0,0, 0.26) 77.78%, rgba(0,0,0, 0.26) 100%); }\n .mdl-slider.is-upgraded.is-lowest-value::-ms-fill-lower {\n background: transparent; }\n .mdl-slider.is-upgraded.is-lowest-value::-ms-fill-upper {\n margin-left: 6px; }\n .mdl-slider.is-upgraded.is-lowest-value:active::-ms-fill-upper {\n margin-left: 9px; }\n .mdl-slider.is-upgraded:disabled:focus::-webkit-slider-thumb, .mdl-slider.is-upgraded:disabled:active::-webkit-slider-thumb, .mdl-slider.is-upgraded:disabled::-webkit-slider-thumb {\n -webkit-transform: scale(0.667);\n transform: scale(0.667);\n background: rgba(0,0,0, 0.26); }\n .mdl-slider.is-upgraded:disabled:focus::-moz-range-thumb, .mdl-slider.is-upgraded:disabled:active::-moz-range-thumb, .mdl-slider.is-upgraded:disabled::-moz-range-thumb {\n transform: scale(0.667);\n background: rgba(0,0,0, 0.26); }\n .mdl-slider.is-upgraded:disabled +\n.mdl-slider__background-flex > .mdl-slider__background-lower {\n background-color: rgba(0,0,0, 0.26);\n left: -6px; }\n .mdl-slider.is-upgraded:disabled +\n.mdl-slider__background-flex > .mdl-slider__background-upper {\n left: 6px; }\n .mdl-slider.is-upgraded.is-lowest-value:disabled:focus::-webkit-slider-thumb, .mdl-slider.is-upgraded.is-lowest-value:disabled:active::-webkit-slider-thumb, .mdl-slider.is-upgraded.is-lowest-value:disabled::-webkit-slider-thumb {\n border: 3px solid rgba(0,0,0, 0.26);\n background: transparent;\n -webkit-transform: scale(0.667);\n transform: scale(0.667); }\n .mdl-slider.is-upgraded.is-lowest-value:disabled:focus::-moz-range-thumb, .mdl-slider.is-upgraded.is-lowest-value:disabled:active::-moz-range-thumb, .mdl-slider.is-upgraded.is-lowest-value:disabled::-moz-range-thumb {\n border: 3px solid rgba(0,0,0, 0.26);\n background: transparent;\n transform: scale(0.667); }\n .mdl-slider.is-upgraded.is-lowest-value:disabled:active +\n.mdl-slider__background-flex > .mdl-slider__background-upper {\n left: 6px; }\n .mdl-slider.is-upgraded:disabled:focus::-ms-thumb, .mdl-slider.is-upgraded:disabled:active::-ms-thumb, .mdl-slider.is-upgraded:disabled::-ms-thumb {\n -ms-transform: scale(0.25);\n transform: scale(0.25);\n background: rgba(0,0,0, 0.26); }\n .mdl-slider.is-upgraded.is-lowest-value:disabled:focus::-ms-thumb, .mdl-slider.is-upgraded.is-lowest-value:disabled:active::-ms-thumb, .mdl-slider.is-upgraded.is-lowest-value:disabled::-ms-thumb {\n -ms-transform: scale(0.25);\n transform: scale(0.25);\n background: radial-gradient(circle closest-side, transparent 0%, transparent 50%, rgba(0,0,0, 0.26) 50%, rgba(0,0,0, 0.26) 100%); }\n .mdl-slider.is-upgraded:disabled::-ms-fill-lower {\n margin-right: 6px;\n background: linear-gradient(to right, transparent, transparent 25px, rgba(0,0,0, 0.26) 25px, rgba(0,0,0, 0.26) 0); }\n .mdl-slider.is-upgraded:disabled::-ms-fill-upper {\n margin-left: 6px; }\n .mdl-slider.is-upgraded.is-lowest-value:disabled:active::-ms-fill-upper {\n margin-left: 6px; }\n\n.mdl-slider__ie-container {\n height: 18px;\n overflow: visible;\n border: none;\n margin: none;\n padding: none; }\n\n.mdl-slider__container {\n height: 18px;\n position: relative;\n background: none;\n display: -webkit-box;\n display: -webkit-flex;\n display: -ms-flexbox;\n display: flex;\n -webkit-box-orient: horizontal;\n -webkit-box-direction: normal;\n -webkit-flex-direction: row;\n -ms-flex-direction: row;\n flex-direction: row; }\n\n.mdl-slider__background-flex {\n background: transparent;\n position: absolute;\n height: 2px;\n width: calc(100% - 52px);\n top: 50%;\n left: 0;\n margin: 0 26px;\n display: -webkit-box;\n display: -webkit-flex;\n display: -ms-flexbox;\n display: flex;\n overflow: hidden;\n border: 0;\n padding: 0;\n -webkit-transform: translate(0, -1px);\n -ms-transform: translate(0, -1px);\n transform: translate(0, -1px); }\n\n.mdl-slider__background-lower {\n background: rgb(63,81,181);\n -webkit-box-flex: 0;\n -webkit-flex: 0;\n -ms-flex: 0;\n flex: 0;\n position: relative;\n border: 0;\n padding: 0; }\n\n.mdl-slider__background-upper {\n background: rgba(0,0,0, 0.26);\n -webkit-box-flex: 0;\n -webkit-flex: 0;\n -ms-flex: 0;\n flex: 0;\n position: relative;\n border: 0;\n padding: 0;\n -webkit-transition: left 0.18s cubic-bezier(0.4, 0, 0.2, 1);\n transition: left 0.18s cubic-bezier(0.4, 0, 0.2, 1); }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n.mdl-spinner {\n display: inline-block;\n position: relative;\n width: 28px;\n height: 28px; }\n .mdl-spinner:not(.is-upgraded).is-active:after {\n content: \"Loading...\"; }\n .mdl-spinner.is-upgraded.is-active {\n -webkit-animation: mdl-spinner__container-rotate 1568.23529412ms linear infinite;\n animation: mdl-spinner__container-rotate 1568.23529412ms linear infinite; }\n\n@-webkit-keyframes mdl-spinner__container-rotate {\n to {\n -webkit-transform: rotate(360deg);\n transform: rotate(360deg); } }\n\n@keyframes mdl-spinner__container-rotate {\n to {\n -webkit-transform: rotate(360deg);\n transform: rotate(360deg); } }\n\n.mdl-spinner__layer {\n position: absolute;\n width: 100%;\n height: 100%;\n opacity: 0; }\n\n.mdl-spinner__layer-1 {\n border-color: rgb(66,165,245); }\n .mdl-spinner--single-color .mdl-spinner__layer-1 {\n border-color: rgb(63,81,181); }\n .mdl-spinner.is-active .mdl-spinner__layer-1 {\n -webkit-animation: mdl-spinner__fill-unfill-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both, mdl-spinner__layer-1-fade-in-out 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both;\n animation: mdl-spinner__fill-unfill-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both, mdl-spinner__layer-1-fade-in-out 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both; }\n\n.mdl-spinner__layer-2 {\n border-color: rgb(244,67,54); }\n .mdl-spinner--single-color .mdl-spinner__layer-2 {\n border-color: rgb(63,81,181); }\n .mdl-spinner.is-active .mdl-spinner__layer-2 {\n -webkit-animation: mdl-spinner__fill-unfill-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both, mdl-spinner__layer-2-fade-in-out 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both;\n animation: mdl-spinner__fill-unfill-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both, mdl-spinner__layer-2-fade-in-out 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both; }\n\n.mdl-spinner__layer-3 {\n border-color: rgb(253,216,53); }\n .mdl-spinner--single-color .mdl-spinner__layer-3 {\n border-color: rgb(63,81,181); }\n .mdl-spinner.is-active .mdl-spinner__layer-3 {\n -webkit-animation: mdl-spinner__fill-unfill-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both, mdl-spinner__layer-3-fade-in-out 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both;\n animation: mdl-spinner__fill-unfill-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both, mdl-spinner__layer-3-fade-in-out 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both; }\n\n.mdl-spinner__layer-4 {\n border-color: rgb(76,175,80); }\n .mdl-spinner--single-color .mdl-spinner__layer-4 {\n border-color: rgb(63,81,181); }\n .mdl-spinner.is-active .mdl-spinner__layer-4 {\n -webkit-animation: mdl-spinner__fill-unfill-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both, mdl-spinner__layer-4-fade-in-out 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both;\n animation: mdl-spinner__fill-unfill-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both, mdl-spinner__layer-4-fade-in-out 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both; }\n\n@-webkit-keyframes mdl-spinner__fill-unfill-rotate {\n 12.5% {\n -webkit-transform: rotate(135deg);\n transform: rotate(135deg); }\n 25% {\n -webkit-transform: rotate(270deg);\n transform: rotate(270deg); }\n 37.5% {\n -webkit-transform: rotate(405deg);\n transform: rotate(405deg); }\n 50% {\n -webkit-transform: rotate(540deg);\n transform: rotate(540deg); }\n 62.5% {\n -webkit-transform: rotate(675deg);\n transform: rotate(675deg); }\n 75% {\n -webkit-transform: rotate(810deg);\n transform: rotate(810deg); }\n 87.5% {\n -webkit-transform: rotate(945deg);\n transform: rotate(945deg); }\n to {\n -webkit-transform: rotate(1080deg);\n transform: rotate(1080deg); } }\n\n@keyframes mdl-spinner__fill-unfill-rotate {\n 12.5% {\n -webkit-transform: rotate(135deg);\n transform: rotate(135deg); }\n 25% {\n -webkit-transform: rotate(270deg);\n transform: rotate(270deg); }\n 37.5% {\n -webkit-transform: rotate(405deg);\n transform: rotate(405deg); }\n 50% {\n -webkit-transform: rotate(540deg);\n transform: rotate(540deg); }\n 62.5% {\n -webkit-transform: rotate(675deg);\n transform: rotate(675deg); }\n 75% {\n -webkit-transform: rotate(810deg);\n transform: rotate(810deg); }\n 87.5% {\n -webkit-transform: rotate(945deg);\n transform: rotate(945deg); }\n to {\n -webkit-transform: rotate(1080deg);\n transform: rotate(1080deg); } }\n\n/**\n* HACK: Even though the intention is to have the current .mdl-spinner__layer-N\n* at `opacity: 1`, we set it to `opacity: 0.99` instead since this forces Chrome\n* to do proper subpixel rendering for the elements being animated. This is\n* especially visible in Chrome 39 on Ubuntu 14.04. See:\n*\n* - https://github.com/Polymer/paper-spinner/issues/9\n* - https://code.google.com/p/chromium/issues/detail?id=436255\n*/\n@-webkit-keyframes mdl-spinner__layer-1-fade-in-out {\n from {\n opacity: 0.99; }\n 25% {\n opacity: 0.99; }\n 26% {\n opacity: 0; }\n 89% {\n opacity: 0; }\n 90% {\n opacity: 0.99; }\n 100% {\n opacity: 0.99; } }\n@keyframes mdl-spinner__layer-1-fade-in-out {\n from {\n opacity: 0.99; }\n 25% {\n opacity: 0.99; }\n 26% {\n opacity: 0; }\n 89% {\n opacity: 0; }\n 90% {\n opacity: 0.99; }\n 100% {\n opacity: 0.99; } }\n\n@-webkit-keyframes mdl-spinner__layer-2-fade-in-out {\n from {\n opacity: 0; }\n 15% {\n opacity: 0; }\n 25% {\n opacity: 0.99; }\n 50% {\n opacity: 0.99; }\n 51% {\n opacity: 0; } }\n\n@keyframes mdl-spinner__layer-2-fade-in-out {\n from {\n opacity: 0; }\n 15% {\n opacity: 0; }\n 25% {\n opacity: 0.99; }\n 50% {\n opacity: 0.99; }\n 51% {\n opacity: 0; } }\n\n@-webkit-keyframes mdl-spinner__layer-3-fade-in-out {\n from {\n opacity: 0; }\n 40% {\n opacity: 0; }\n 50% {\n opacity: 0.99; }\n 75% {\n opacity: 0.99; }\n 76% {\n opacity: 0; } }\n\n@keyframes mdl-spinner__layer-3-fade-in-out {\n from {\n opacity: 0; }\n 40% {\n opacity: 0; }\n 50% {\n opacity: 0.99; }\n 75% {\n opacity: 0.99; }\n 76% {\n opacity: 0; } }\n\n@-webkit-keyframes mdl-spinner__layer-4-fade-in-out {\n from {\n opacity: 0; }\n 65% {\n opacity: 0; }\n 75% {\n opacity: 0.99; }\n 90% {\n opacity: 0.99; }\n 100% {\n opacity: 0; } }\n\n@keyframes mdl-spinner__layer-4-fade-in-out {\n from {\n opacity: 0; }\n 65% {\n opacity: 0; }\n 75% {\n opacity: 0.99; }\n 90% {\n opacity: 0.99; }\n 100% {\n opacity: 0; } }\n\n/**\n* Patch the gap that appear between the two adjacent\n* div.mdl-spinner__circle-clipper while the spinner is rotating\n* (appears on Chrome 38, Safari 7.1, and IE 11).\n*\n* Update: the gap no longer appears on Chrome when .mdl-spinner__layer-N's\n* opacity is 0.99, but still does on Safari and IE.\n*/\n.mdl-spinner__gap-patch {\n position: absolute;\n box-sizing: border-box;\n top: 0;\n left: 45%;\n width: 10%;\n height: 100%;\n overflow: hidden;\n border-color: inherit; }\n .mdl-spinner__gap-patch .mdl-spinner__circle {\n width: 1000%;\n left: -450%; }\n\n.mdl-spinner__circle-clipper {\n display: inline-block;\n position: relative;\n width: 50%;\n height: 100%;\n overflow: hidden;\n border-color: inherit; }\n .mdl-spinner__circle-clipper .mdl-spinner__circle {\n width: 200%; }\n\n.mdl-spinner__circle {\n box-sizing: border-box;\n height: 100%;\n border-width: 3px;\n border-style: solid;\n border-color: inherit;\n border-bottom-color: transparent !important;\n border-radius: 50%;\n -webkit-animation: none;\n animation: none;\n position: absolute;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0; }\n .mdl-spinner__left .mdl-spinner__circle {\n border-right-color: transparent !important;\n -webkit-transform: rotate(129deg);\n -ms-transform: rotate(129deg);\n transform: rotate(129deg); }\n .mdl-spinner.is-active .mdl-spinner__left .mdl-spinner__circle {\n -webkit-animation: mdl-spinner__left-spin 1333ms cubic-bezier(0.4, 0, 0.2, 1) infinite both;\n animation: mdl-spinner__left-spin 1333ms cubic-bezier(0.4, 0, 0.2, 1) infinite both; }\n .mdl-spinner__right .mdl-spinner__circle {\n left: -100%;\n border-left-color: transparent !important;\n -webkit-transform: rotate(-129deg);\n -ms-transform: rotate(-129deg);\n transform: rotate(-129deg); }\n .mdl-spinner.is-active .mdl-spinner__right .mdl-spinner__circle {\n -webkit-animation: mdl-spinner__right-spin 1333ms cubic-bezier(0.4, 0, 0.2, 1) infinite both;\n animation: mdl-spinner__right-spin 1333ms cubic-bezier(0.4, 0, 0.2, 1) infinite both; }\n\n@-webkit-keyframes mdl-spinner__left-spin {\n from {\n -webkit-transform: rotate(130deg);\n transform: rotate(130deg); }\n 50% {\n -webkit-transform: rotate(-5deg);\n transform: rotate(-5deg); }\n to {\n -webkit-transform: rotate(130deg);\n transform: rotate(130deg); } }\n\n@keyframes mdl-spinner__left-spin {\n from {\n -webkit-transform: rotate(130deg);\n transform: rotate(130deg); }\n 50% {\n -webkit-transform: rotate(-5deg);\n transform: rotate(-5deg); }\n to {\n -webkit-transform: rotate(130deg);\n transform: rotate(130deg); } }\n\n@-webkit-keyframes mdl-spinner__right-spin {\n from {\n -webkit-transform: rotate(-130deg);\n transform: rotate(-130deg); }\n 50% {\n -webkit-transform: rotate(5deg);\n transform: rotate(5deg); }\n to {\n -webkit-transform: rotate(-130deg);\n transform: rotate(-130deg); } }\n\n@keyframes mdl-spinner__right-spin {\n from {\n -webkit-transform: rotate(-130deg);\n transform: rotate(-130deg); }\n 50% {\n -webkit-transform: rotate(5deg);\n transform: rotate(5deg); }\n to {\n -webkit-transform: rotate(-130deg);\n transform: rotate(-130deg); } }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* Typography */\n/* Shadows */\n/* Animations */\n.mdl-switch {\n position: relative;\n z-index: 1;\n vertical-align: middle;\n display: inline-block;\n box-sizing: border-box;\n width: 100%;\n height: 24px;\n margin: 0;\n padding: 0;\n overflow: visible;\n -webkit-touch-callout: none;\n -webkit-user-select: none;\n -moz-user-select: none;\n -ms-user-select: none;\n user-select: none; }\n .mdl-switch.is-upgraded {\n padding-left: 28px; }\n\n.mdl-switch__input {\n line-height: 24px; }\n .mdl-switch.is-upgraded .mdl-switch__input {\n position: absolute;\n width: 0;\n height: 0;\n margin: 0;\n padding: 0;\n opacity: 0;\n -ms-appearance: none;\n -moz-appearance: none;\n -webkit-appearance: none;\n appearance: none;\n border: none; }\n\n.mdl-switch__track {\n background: rgba(0,0,0, 0.26);\n position: absolute;\n left: 0;\n top: 5px;\n height: 14px;\n width: 36px;\n border-radius: 14px;\n cursor: pointer; }\n .mdl-switch.is-checked .mdl-switch__track {\n background: rgba(63,81,181, 0.5); }\n .mdl-switch.is-disabled .mdl-switch__track {\n background: rgba(0,0,0, 0.12);\n cursor: auto; }\n\n.mdl-switch__thumb {\n background: rgb(250,250,250);\n position: absolute;\n left: 0;\n top: 2px;\n height: 20px;\n width: 20px;\n border-radius: 50%;\n cursor: pointer;\n box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12);\n -webkit-transition-duration: 0.28s;\n transition-duration: 0.28s;\n -webkit-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n -webkit-transition-property: left;\n transition-property: left; }\n .mdl-switch.is-checked .mdl-switch__thumb {\n background: rgb(63,81,181);\n left: 16px;\n box-shadow: 0 3px 4px 0 rgba(0, 0, 0, 0.14), 0 3px 3px -2px rgba(0, 0, 0, 0.2), 0 1px 8px 0 rgba(0, 0, 0, 0.12); }\n .mdl-switch.is-disabled .mdl-switch__thumb {\n background: rgb(189,189,189);\n cursor: auto; }\n\n.mdl-switch__focus-helper {\n position: absolute;\n top: 50%;\n left: 50%;\n -webkit-transform: translate(-4px, -4px);\n -ms-transform: translate(-4px, -4px);\n transform: translate(-4px, -4px);\n display: inline-block;\n box-sizing: border-box;\n width: 8px;\n height: 8px;\n border-radius: 50%;\n background-color: transparent; }\n .mdl-switch.is-focused .mdl-switch__focus-helper {\n box-shadow: 0 0 0px 20px rgba(0, 0, 0, 0.1);\n background-color: rgba(0, 0, 0, 0.1); }\n .mdl-switch.is-focused.is-checked .mdl-switch__focus-helper {\n box-shadow: 0 0 0px 20px rgba(63,81,181, 0.26);\n background-color: rgba(63,81,181, 0.26); }\n\n.mdl-switch__label {\n position: relative;\n cursor: pointer;\n font-size: 16px;\n line-height: 24px;\n margin: 0;\n left: 24px; }\n .mdl-switch.is-disabled .mdl-switch__label {\n color: rgb(189,189,189);\n cursor: auto; }\n\n.mdl-switch__ripple-container {\n position: absolute;\n z-index: 2;\n top: -12px;\n left: -14px;\n box-sizing: border-box;\n width: 48px;\n height: 48px;\n border-radius: 50%;\n cursor: pointer;\n overflow: hidden;\n -webkit-mask-image: -webkit-radial-gradient(circle, white, black);\n -webkit-transition-duration: 0.40s;\n transition-duration: 0.40s;\n -webkit-transition-timing-function: step-end;\n transition-timing-function: step-end;\n -webkit-transition-property: left;\n transition-property: left; }\n .mdl-switch__ripple-container .mdl-ripple {\n background: rgb(63,81,181); }\n .mdl-switch.is-disabled .mdl-switch__ripple-container {\n cursor: auto; }\n .mdl-switch.is-disabled .mdl-switch__ripple-container .mdl-ripple {\n background: transparent; }\n .mdl-switch.is-checked .mdl-switch__ripple-container {\n cursor: auto;\n left: 2px; }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n.mdl-tabs {\n display: block;\n width: 100%; }\n\n.mdl-tabs__tab-bar {\n display: -webkit-box;\n display: -webkit-flex;\n display: -ms-flexbox;\n display: flex;\n -webkit-box-orient: horizontal;\n -webkit-box-direction: normal;\n -webkit-flex-direction: row;\n -ms-flex-direction: row;\n flex-direction: row;\n -webkit-box-pack: center;\n -webkit-justify-content: center;\n -ms-flex-pack: center;\n justify-content: center;\n -webkit-align-content: space-between;\n -ms-flex-line-pack: justify;\n align-content: space-between;\n -webkit-box-align: start;\n -webkit-align-items: flex-start;\n -ms-flex-align: start;\n align-items: flex-start;\n height: 48px;\n padding: 0 0 0 0;\n margin: 0;\n border-bottom: 1px solid rgb(224,224,224); }\n\n.mdl-tabs__tab {\n margin: 0;\n border: none;\n padding: 0 24px 0 24px;\n float: left;\n position: relative;\n display: block;\n color: red;\n text-decoration: none;\n height: 48px;\n line-height: 48px;\n text-align: center;\n font-weight: 500;\n font-size: 14px;\n text-transform: uppercase;\n color: rgba(0,0,0, 0.54);\n overflow: hidden; }\n .mdl-tabs.is-upgraded .mdl-tabs__tab.is-active {\n color: rgba(0,0,0, 0.87); }\n .mdl-tabs.is-upgraded .mdl-tabs__tab.is-active:after {\n height: 2px;\n width: 100%;\n display: block;\n content: \" \";\n bottom: 0px;\n left: 0px;\n position: absolute;\n background: rgb(63,81,181);\n -webkit-animation: border-expand 0.2s cubic-bezier(0.4, 0, 0.4, 1) 0.01s alternate forwards;\n animation: border-expand 0.2s cubic-bezier(0.4, 0, 0.4, 1) 0.01s alternate forwards;\n -webkit-transition: all 1s cubic-bezier(0.4, 0, 1, 1);\n transition: all 1s cubic-bezier(0.4, 0, 1, 1); }\n .mdl-tabs__tab .mdl-tabs__ripple-container {\n display: block;\n position: absolute;\n height: 100%;\n width: 100%;\n left: 0px;\n top: 0px;\n z-index: 1;\n overflow: hidden; }\n .mdl-tabs__tab .mdl-tabs__ripple-container .mdl-ripple {\n background: rgb(63,81,181); }\n\n.mdl-tabs__panel {\n display: block; }\n .mdl-tabs.is-upgraded .mdl-tabs__panel {\n display: none; }\n .mdl-tabs.is-upgraded .mdl-tabs__panel.is-active {\n display: block; }\n\n@-webkit-keyframes border-expand {\n 0% {\n opacity: 0;\n width: 0; }\n 100% {\n opacity: 1;\n width: 100%; } }\n\n@keyframes border-expand {\n 0% {\n opacity: 0;\n width: 0; }\n 100% {\n opacity: 1;\n width: 100%; } }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* Typography */\n/* Shadows */\n/* Animations */\n.mdl-textfield {\n position: relative;\n font-size: 16px;\n display: inline-block;\n box-sizing: border-box;\n width: 300px;\n max-width: 100%;\n margin: 0;\n padding: 20px 0; }\n .mdl-textfield .mdl-button {\n position: absolute;\n bottom: 20px; }\n\n.mdl-textfield--align-right {\n text-align: right; }\n\n.mdl-textfield--full-width {\n width: 100%; }\n\n.mdl-textfield--expandable {\n min-width: 32px;\n width: auto;\n min-height: 32px; }\n\n.mdl-textfield__input {\n border: none;\n border-bottom: 1px solid rgba(0,0,0, 0.12);\n display: block;\n font-size: 16px;\n margin: 0;\n padding: 4px 0;\n width: 100%;\n background: none;\n text-align: left;\n color: inherit; }\n .mdl-textfield.is-focused .mdl-textfield__input {\n outline: none; }\n .mdl-textfield.is-invalid .mdl-textfield__input {\n border-color: rgb(222, 50, 38);\n box-shadow: none; }\n .mdl-textfield.is-disabled .mdl-textfield__input {\n background-color: transparent;\n border-bottom: 1px dotted rgba(0,0,0, 0.12);\n color: rgba(0,0,0, 0.26); }\n\n.mdl-textfield textarea.mdl-textfield__input {\n display: block; }\n\n.mdl-textfield__label {\n bottom: 0;\n color: rgba(0,0,0, 0.26);\n font-size: 16px;\n left: 0;\n right: 0;\n pointer-events: none;\n position: absolute;\n display: block;\n top: 24px;\n width: 100%;\n overflow: hidden;\n white-space: nowrap;\n text-align: left; }\n .mdl-textfield.is-dirty .mdl-textfield__label {\n visibility: hidden; }\n .mdl-textfield--floating-label .mdl-textfield__label {\n -webkit-transition-duration: 0.2s;\n transition-duration: 0.2s;\n -webkit-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); }\n .mdl-textfield.is-disabled.is-disabled .mdl-textfield__label {\n color: rgba(0,0,0, 0.26); }\n .mdl-textfield--floating-label.is-focused .mdl-textfield__label,\n .mdl-textfield--floating-label.is-dirty .mdl-textfield__label {\n color: rgb(63,81,181);\n font-size: 12px;\n top: 4px;\n visibility: visible; }\n .mdl-textfield--floating-label.is-focused .mdl-textfield__expandable-holder .mdl-textfield__label,\n .mdl-textfield--floating-label.is-dirty .mdl-textfield__expandable-holder .mdl-textfield__label {\n top: -16px; }\n .mdl-textfield--floating-label.is-invalid .mdl-textfield__label {\n color: rgb(222, 50, 38);\n font-size: 12px; }\n .mdl-textfield__label:after {\n background-color: rgb(63,81,181);\n bottom: 20px;\n content: '';\n height: 2px;\n left: 45%;\n position: absolute;\n -webkit-transition-duration: 0.2s;\n transition-duration: 0.2s;\n -webkit-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n visibility: hidden;\n width: 10px; }\n .mdl-textfield.is-focused .mdl-textfield__label:after {\n left: 0;\n visibility: visible;\n width: 100%; }\n .mdl-textfield.is-invalid .mdl-textfield__label:after {\n background-color: rgb(222, 50, 38); }\n\n.mdl-textfield__error {\n color: rgb(222, 50, 38);\n position: absolute;\n font-size: 12px;\n margin-top: 3px;\n visibility: hidden;\n display: block; }\n .mdl-textfield.is-invalid .mdl-textfield__error {\n visibility: visible; }\n\n.mdl-textfield__expandable-holder {\n display: inline-block;\n position: relative;\n margin-left: 32px;\n -webkit-transition-duration: 0.2s;\n transition-duration: 0.2s;\n -webkit-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n display: inline-block;\n max-width: 0.1px; }\n .mdl-textfield.is-focused .mdl-textfield__expandable-holder, .mdl-textfield.is-dirty .mdl-textfield__expandable-holder {\n max-width: 600px; }\n .mdl-textfield__expandable-holder .mdl-textfield__label:after {\n bottom: 0; }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n.mdl-tooltip {\n -webkit-transform: scale(0);\n -ms-transform: scale(0);\n transform: scale(0);\n -webkit-transform-origin: top center;\n -ms-transform-origin: top center;\n transform-origin: top center;\n will-change: transform;\n z-index: 999;\n background: rgba(97,97,97, 0.9);\n border-radius: 2px;\n color: rgb(255,255,255);\n display: inline-block;\n font-size: 10px;\n font-weight: 500;\n line-height: 14px;\n max-width: 170px;\n position: fixed;\n top: -500px;\n left: -500px;\n padding: 8px;\n text-align: center; }\n\n.mdl-tooltip.is-active {\n -webkit-animation: pulse 200ms cubic-bezier(0, 0, 0.2, 1) forwards;\n animation: pulse 200ms cubic-bezier(0, 0, 0.2, 1) forwards; }\n\n.mdl-tooltip--large {\n line-height: 14px;\n font-size: 14px;\n padding: 16px; }\n\n@-webkit-keyframes pulse {\n 0% {\n -webkit-transform: scale(0);\n transform: scale(0);\n opacity: 0; }\n 50% {\n -webkit-transform: scale(0.99);\n transform: scale(0.99); }\n 100% {\n -webkit-transform: scale(1);\n transform: scale(1);\n opacity: 1;\n visibility: visible; } }\n\n@keyframes pulse {\n 0% {\n -webkit-transform: scale(0);\n transform: scale(0);\n opacity: 0; }\n 50% {\n -webkit-transform: scale(0.99);\n transform: scale(0.99); }\n 100% {\n -webkit-transform: scale(1);\n transform: scale(1);\n opacity: 1;\n visibility: visible; } }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* Typography */\n/* Shadows */\n/* Animations */\n.mdl-shadow--2dp {\n box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12); }\n\n.mdl-shadow--3dp {\n box-shadow: 0 3px 4px 0 rgba(0, 0, 0, 0.14), 0 3px 3px -2px rgba(0, 0, 0, 0.2), 0 1px 8px 0 rgba(0, 0, 0, 0.12); }\n\n.mdl-shadow--4dp {\n box-shadow: 0 4px 5px 0 rgba(0, 0, 0, 0.14), 0 1px 10px 0 rgba(0, 0, 0, 0.12), 0 2px 4px -1px rgba(0, 0, 0, 0.2); }\n\n.mdl-shadow--6dp {\n box-shadow: 0 6px 10px 0 rgba(0, 0, 0, 0.14), 0 1px 18px 0 rgba(0, 0, 0, 0.12), 0 3px 5px -1px rgba(0, 0, 0, 0.2); }\n\n.mdl-shadow--8dp {\n box-shadow: 0 8px 10px 1px rgba(0, 0, 0, 0.14), 0 3px 14px 2px rgba(0, 0, 0, 0.12), 0 5px 5px -3px rgba(0, 0, 0, 0.2); }\n\n.mdl-shadow--16dp {\n box-shadow: 0 16px 24px 2px rgba(0, 0, 0, 0.14), 0 6px 30px 5px rgba(0, 0, 0, 0.12), 0 8px 10px -5px rgba(0, 0, 0, 0.2); }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*\n* NOTE: Some rules here are applied using duplicate selectors.\n* This is on purpose to increase their specificity when applied.\n* For example: `.mdl-cell--1-col-phone.mdl-cell--1-col-phone`\n*/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n.mdl-grid {\n display: -webkit-box;\n display: -webkit-flex;\n display: -ms-flexbox;\n display: flex;\n -webkit-flex-flow: row wrap;\n -ms-flex-flow: row wrap;\n flex-flow: row wrap;\n margin: 0 auto 0 auto;\n -webkit-box-align: stretch;\n -webkit-align-items: stretch;\n -ms-flex-align: stretch;\n align-items: stretch; }\n .mdl-grid.mdl-grid--no-spacing {\n padding: 0; }\n\n.mdl-cell {\n box-sizing: border-box; }\n\n.mdl-cell--top {\n -webkit-align-self: flex-start;\n -ms-flex-item-align: start;\n align-self: flex-start; }\n\n.mdl-cell--middle {\n -webkit-align-self: center;\n -ms-flex-item-align: center;\n align-self: center; }\n\n.mdl-cell--bottom {\n -webkit-align-self: flex-end;\n -ms-flex-item-align: end;\n align-self: flex-end; }\n\n.mdl-cell--stretch {\n -webkit-align-self: stretch;\n -ms-flex-item-align: stretch;\n align-self: stretch; }\n\n.mdl-grid.mdl-grid--no-spacing > .mdl-cell {\n margin: 0; }\n\n@media (max-width: 479px) {\n .mdl-grid {\n padding: 8px; }\n .mdl-cell {\n margin: 8px;\n width: calc(100% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell {\n width: 100%; }\n .mdl-cell--hide-phone {\n display: none !important; }\n .mdl-cell--1-col,\n .mdl-cell--1-col-phone.mdl-cell--1-col-phone {\n width: calc(25% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--1-col, .mdl-grid--no-spacing >\n .mdl-cell--1-col-phone.mdl-cell--1-col-phone {\n width: 25%; }\n .mdl-cell--2-col,\n .mdl-cell--2-col-phone.mdl-cell--2-col-phone {\n width: calc(50% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--2-col, .mdl-grid--no-spacing >\n .mdl-cell--2-col-phone.mdl-cell--2-col-phone {\n width: 50%; }\n .mdl-cell--3-col,\n .mdl-cell--3-col-phone.mdl-cell--3-col-phone {\n width: calc(75% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--3-col, .mdl-grid--no-spacing >\n .mdl-cell--3-col-phone.mdl-cell--3-col-phone {\n width: 75%; }\n .mdl-cell--4-col,\n .mdl-cell--4-col-phone.mdl-cell--4-col-phone {\n width: calc(100% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--4-col, .mdl-grid--no-spacing >\n .mdl-cell--4-col-phone.mdl-cell--4-col-phone {\n width: 100%; }\n .mdl-cell--5-col,\n .mdl-cell--5-col-phone.mdl-cell--5-col-phone {\n width: calc(100% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--5-col, .mdl-grid--no-spacing >\n .mdl-cell--5-col-phone.mdl-cell--5-col-phone {\n width: 100%; }\n .mdl-cell--6-col,\n .mdl-cell--6-col-phone.mdl-cell--6-col-phone {\n width: calc(100% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--6-col, .mdl-grid--no-spacing >\n .mdl-cell--6-col-phone.mdl-cell--6-col-phone {\n width: 100%; }\n .mdl-cell--7-col,\n .mdl-cell--7-col-phone.mdl-cell--7-col-phone {\n width: calc(100% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--7-col, .mdl-grid--no-spacing >\n .mdl-cell--7-col-phone.mdl-cell--7-col-phone {\n width: 100%; }\n .mdl-cell--8-col,\n .mdl-cell--8-col-phone.mdl-cell--8-col-phone {\n width: calc(100% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--8-col, .mdl-grid--no-spacing >\n .mdl-cell--8-col-phone.mdl-cell--8-col-phone {\n width: 100%; }\n .mdl-cell--9-col,\n .mdl-cell--9-col-phone.mdl-cell--9-col-phone {\n width: calc(100% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--9-col, .mdl-grid--no-spacing >\n .mdl-cell--9-col-phone.mdl-cell--9-col-phone {\n width: 100%; }\n .mdl-cell--10-col,\n .mdl-cell--10-col-phone.mdl-cell--10-col-phone {\n width: calc(100% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--10-col, .mdl-grid--no-spacing >\n .mdl-cell--10-col-phone.mdl-cell--10-col-phone {\n width: 100%; }\n .mdl-cell--11-col,\n .mdl-cell--11-col-phone.mdl-cell--11-col-phone {\n width: calc(100% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--11-col, .mdl-grid--no-spacing >\n .mdl-cell--11-col-phone.mdl-cell--11-col-phone {\n width: 100%; }\n .mdl-cell--12-col,\n .mdl-cell--12-col-phone.mdl-cell--12-col-phone {\n width: calc(100% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--12-col, .mdl-grid--no-spacing >\n .mdl-cell--12-col-phone.mdl-cell--12-col-phone {\n width: 100%; } }\n\n@media (min-width: 480px) and (max-width: 839px) {\n .mdl-grid {\n padding: 8px; }\n .mdl-cell {\n margin: 8px;\n width: calc(50% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell {\n width: 50%; }\n .mdl-cell--hide-tablet {\n display: none !important; }\n .mdl-cell--1-col,\n .mdl-cell--1-col-tablet.mdl-cell--1-col-tablet {\n width: calc(12.5% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--1-col, .mdl-grid--no-spacing >\n .mdl-cell--1-col-tablet.mdl-cell--1-col-tablet {\n width: 12.5%; }\n .mdl-cell--2-col,\n .mdl-cell--2-col-tablet.mdl-cell--2-col-tablet {\n width: calc(25% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--2-col, .mdl-grid--no-spacing >\n .mdl-cell--2-col-tablet.mdl-cell--2-col-tablet {\n width: 25%; }\n .mdl-cell--3-col,\n .mdl-cell--3-col-tablet.mdl-cell--3-col-tablet {\n width: calc(37.5% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--3-col, .mdl-grid--no-spacing >\n .mdl-cell--3-col-tablet.mdl-cell--3-col-tablet {\n width: 37.5%; }\n .mdl-cell--4-col,\n .mdl-cell--4-col-tablet.mdl-cell--4-col-tablet {\n width: calc(50% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--4-col, .mdl-grid--no-spacing >\n .mdl-cell--4-col-tablet.mdl-cell--4-col-tablet {\n width: 50%; }\n .mdl-cell--5-col,\n .mdl-cell--5-col-tablet.mdl-cell--5-col-tablet {\n width: calc(62.5% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--5-col, .mdl-grid--no-spacing >\n .mdl-cell--5-col-tablet.mdl-cell--5-col-tablet {\n width: 62.5%; }\n .mdl-cell--6-col,\n .mdl-cell--6-col-tablet.mdl-cell--6-col-tablet {\n width: calc(75% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--6-col, .mdl-grid--no-spacing >\n .mdl-cell--6-col-tablet.mdl-cell--6-col-tablet {\n width: 75%; }\n .mdl-cell--7-col,\n .mdl-cell--7-col-tablet.mdl-cell--7-col-tablet {\n width: calc(87.5% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--7-col, .mdl-grid--no-spacing >\n .mdl-cell--7-col-tablet.mdl-cell--7-col-tablet {\n width: 87.5%; }\n .mdl-cell--8-col,\n .mdl-cell--8-col-tablet.mdl-cell--8-col-tablet {\n width: calc(100% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--8-col, .mdl-grid--no-spacing >\n .mdl-cell--8-col-tablet.mdl-cell--8-col-tablet {\n width: 100%; }\n .mdl-cell--9-col,\n .mdl-cell--9-col-tablet.mdl-cell--9-col-tablet {\n width: calc(100% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--9-col, .mdl-grid--no-spacing >\n .mdl-cell--9-col-tablet.mdl-cell--9-col-tablet {\n width: 100%; }\n .mdl-cell--10-col,\n .mdl-cell--10-col-tablet.mdl-cell--10-col-tablet {\n width: calc(100% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--10-col, .mdl-grid--no-spacing >\n .mdl-cell--10-col-tablet.mdl-cell--10-col-tablet {\n width: 100%; }\n .mdl-cell--11-col,\n .mdl-cell--11-col-tablet.mdl-cell--11-col-tablet {\n width: calc(100% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--11-col, .mdl-grid--no-spacing >\n .mdl-cell--11-col-tablet.mdl-cell--11-col-tablet {\n width: 100%; }\n .mdl-cell--12-col,\n .mdl-cell--12-col-tablet.mdl-cell--12-col-tablet {\n width: calc(100% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--12-col, .mdl-grid--no-spacing >\n .mdl-cell--12-col-tablet.mdl-cell--12-col-tablet {\n width: 100%; } }\n\n@media (min-width: 840px) {\n .mdl-grid {\n padding: 8px; }\n .mdl-cell {\n margin: 8px;\n width: calc(33.3333333333% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell {\n width: 33.3333333333%; }\n .mdl-cell--hide-desktop {\n display: none !important; }\n .mdl-cell--1-col,\n .mdl-cell--1-col-desktop.mdl-cell--1-col-desktop {\n width: calc(8.3333333333% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--1-col, .mdl-grid--no-spacing >\n .mdl-cell--1-col-desktop.mdl-cell--1-col-desktop {\n width: 8.3333333333%; }\n .mdl-cell--2-col,\n .mdl-cell--2-col-desktop.mdl-cell--2-col-desktop {\n width: calc(16.6666666667% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--2-col, .mdl-grid--no-spacing >\n .mdl-cell--2-col-desktop.mdl-cell--2-col-desktop {\n width: 16.6666666667%; }\n .mdl-cell--3-col,\n .mdl-cell--3-col-desktop.mdl-cell--3-col-desktop {\n width: calc(25% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--3-col, .mdl-grid--no-spacing >\n .mdl-cell--3-col-desktop.mdl-cell--3-col-desktop {\n width: 25%; }\n .mdl-cell--4-col,\n .mdl-cell--4-col-desktop.mdl-cell--4-col-desktop {\n width: calc(33.3333333333% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--4-col, .mdl-grid--no-spacing >\n .mdl-cell--4-col-desktop.mdl-cell--4-col-desktop {\n width: 33.3333333333%; }\n .mdl-cell--5-col,\n .mdl-cell--5-col-desktop.mdl-cell--5-col-desktop {\n width: calc(41.6666666667% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--5-col, .mdl-grid--no-spacing >\n .mdl-cell--5-col-desktop.mdl-cell--5-col-desktop {\n width: 41.6666666667%; }\n .mdl-cell--6-col,\n .mdl-cell--6-col-desktop.mdl-cell--6-col-desktop {\n width: calc(50% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--6-col, .mdl-grid--no-spacing >\n .mdl-cell--6-col-desktop.mdl-cell--6-col-desktop {\n width: 50%; }\n .mdl-cell--7-col,\n .mdl-cell--7-col-desktop.mdl-cell--7-col-desktop {\n width: calc(58.3333333333% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--7-col, .mdl-grid--no-spacing >\n .mdl-cell--7-col-desktop.mdl-cell--7-col-desktop {\n width: 58.3333333333%; }\n .mdl-cell--8-col,\n .mdl-cell--8-col-desktop.mdl-cell--8-col-desktop {\n width: calc(66.6666666667% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--8-col, .mdl-grid--no-spacing >\n .mdl-cell--8-col-desktop.mdl-cell--8-col-desktop {\n width: 66.6666666667%; }\n .mdl-cell--9-col,\n .mdl-cell--9-col-desktop.mdl-cell--9-col-desktop {\n width: calc(75% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--9-col, .mdl-grid--no-spacing >\n .mdl-cell--9-col-desktop.mdl-cell--9-col-desktop {\n width: 75%; }\n .mdl-cell--10-col,\n .mdl-cell--10-col-desktop.mdl-cell--10-col-desktop {\n width: calc(83.3333333333% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--10-col, .mdl-grid--no-spacing >\n .mdl-cell--10-col-desktop.mdl-cell--10-col-desktop {\n width: 83.3333333333%; }\n .mdl-cell--11-col,\n .mdl-cell--11-col-desktop.mdl-cell--11-col-desktop {\n width: calc(91.6666666667% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--11-col, .mdl-grid--no-spacing >\n .mdl-cell--11-col-desktop.mdl-cell--11-col-desktop {\n width: 91.6666666667%; }\n .mdl-cell--12-col,\n .mdl-cell--12-col-desktop.mdl-cell--12-col-desktop {\n width: calc(100% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--12-col, .mdl-grid--no-spacing >\n .mdl-cell--12-col-desktop.mdl-cell--12-col-desktop {\n width: 100%; } }\n","@charset \"UTF-8\";\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* Material Design Lite */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* Typography */\n/* Shadows */\n/* Animations */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n/*\n * What follows is the result of much research on cross-browser styling.\n * Credit left inline and big thanks to Nicolas Gallagher, Jonathan Neal,\n * Kroc Camen, and the H5BP dev community and team.\n */\n/* ==========================================================================\n Base styles: opinionated defaults\n ========================================================================== */\nhtml {\n color: rgba(0,0,0, 0.87);\n font-size: 1em;\n line-height: 1.4; }\n\n/*\n * Remove text-shadow in selection highlight:\n * https://twitter.com/miketaylr/status/12228805301\n *\n * These selection rule sets have to be separate.\n * Customize the background color to match your design.\n */\n::selection {\n background: #b3d4fc;\n text-shadow: none; }\n\n/*\n * A better looking default horizontal rule\n */\nhr {\n display: block;\n height: 1px;\n border: 0;\n border-top: 1px solid #ccc;\n margin: 1em 0;\n padding: 0; }\n\n/*\n * Remove the gap between audio, canvas, iframes,\n * images, videos and the bottom of their containers:\n * https://github.com/h5bp/html5-boilerplate/issues/440\n */\naudio,\ncanvas,\niframe,\nimg,\nsvg,\nvideo {\n vertical-align: middle; }\n\n/*\n * Remove default fieldset styles.\n */\nfieldset {\n border: 0;\n margin: 0;\n padding: 0; }\n\n/*\n * Allow only vertical resizing of textareas.\n */\ntextarea {\n resize: vertical; }\n\n/* ==========================================================================\n Browser Upgrade Prompt\n ========================================================================== */\n.browserupgrade {\n margin: 0.2em 0;\n background: #ccc;\n color: #000;\n padding: 0.2em 0; }\n\n/* ==========================================================================\n Author's custom styles\n ========================================================================== */\n/* ==========================================================================\n Helper classes\n ========================================================================== */\n/*\n * Hide visually and from screen readers:\n */\n.hidden {\n display: none !important; }\n\n/*\n * Hide only visually, but have it available for screen readers:\n * http://snook.ca/archives/html_and_css/hiding-content-for-accessibility\n */\n.visuallyhidden {\n border: 0;\n clip: rect(0 0 0 0);\n height: 1px;\n margin: -1px;\n overflow: hidden;\n padding: 0;\n position: absolute;\n width: 1px; }\n\n/*\n * Extends the .visuallyhidden class to allow the element\n * to be focusable when navigated to via the keyboard:\n * https://www.drupal.org/node/897638\n */\n.visuallyhidden.focusable:active,\n.visuallyhidden.focusable:focus {\n clip: auto;\n height: auto;\n margin: 0;\n overflow: visible;\n position: static;\n width: auto; }\n\n/*\n * Hide visually and from screen readers, but maintain layout\n */\n.invisible {\n visibility: hidden; }\n\n/*\n * Clearfix: contain floats\n *\n * For modern browsers\n * 1. The space content is one way to avoid an Opera bug when the\n * `contenteditable` attribute is included anywhere else in the document.\n * Otherwise it causes space to appear at the top and bottom of elements\n * that receive the `clearfix` class.\n * 2. The use of `table` rather than `block` is only necessary if using\n * `:before` to contain the top-margins of child elements.\n */\n.clearfix:before,\n.clearfix:after {\n content: \" \";\n /* 1 */\n display: table;\n /* 2 */ }\n\n.clearfix:after {\n clear: both; }\n\n/* ==========================================================================\n EXAMPLE Media Queries for Responsive Design.\n These examples override the primary ('mobile first') styles.\n Modify as content requires.\n ========================================================================== */\n/* ==========================================================================\n Print styles.\n Inlined to avoid the additional HTTP request:\n http://www.phpied.com/delay-loading-your-print-css/\n ========================================================================== */\n@media print {\n *,\n *:before,\n *:after,\n *:first-letter,\n *:first-line {\n background: transparent !important;\n color: #000 !important;\n /* Black prints faster: http://www.sanbeiji.com/archives/953 */\n box-shadow: none !important;\n text-shadow: none !important; }\n a,\n a:visited {\n text-decoration: underline; }\n a[href]:after {\n content: \" (\" attr(href) \")\"; }\n abbr[title]:after {\n content: \" (\" attr(title) \")\"; }\n /*\n * Don't show links that are fragment identifiers,\n * or use the `javascript:` pseudo protocol\n */\n a[href^=\"#\"]:after,\n a[href^=\"javascript:\"]:after {\n content: \"\"; }\n pre,\n blockquote {\n border: 1px solid #999;\n page-break-inside: avoid; }\n /*\n * Printing Tables:\n * http://css-discuss.incutio.com/wiki/Printing_Tables\n */\n thead {\n display: table-header-group; }\n tr,\n img {\n page-break-inside: avoid; }\n img {\n max-width: 100% !important; }\n p,\n h2,\n h3 {\n orphans: 3;\n widows: 3; }\n h2,\n h3 {\n page-break-after: avoid; } }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* Remove the unwanted box around FAB buttons */\n/* More info: http://goo.gl/IPwKi */\na, .mdl-accordion, .mdl-button, .mdl-card, .mdl-checkbox, .mdl-dropdown-menu,\n.mdl-icon-toggle, .mdl-item, .mdl-radio, .mdl-slider, .mdl-switch, .mdl-tabs__tab {\n -webkit-tap-highlight-color: transparent;\n -webkit-tap-highlight-color: rgba(255, 255, 255, 0); }\n\n/*\n * Make html take up the entire screen\n * Then set touch-action to avoid touch delay on mobile IE\n */\nhtml {\n width: 100%;\n height: 100%;\n -ms-touch-action: manipulation;\n touch-action: manipulation; }\n\n/*\n* Make body take up the entire screen\n* Remove body margin so layout containers don't cause extra overflow.\n*/\nbody {\n width: 100%;\n min-height: 100%;\n margin: 0; }\n\n/*\n * Main display reset for IE support.\n * Source: http://weblog.west-wind.com/posts/2015/Jan/12/main-HTML5-Tag-not-working-in-Internet-Explorer-91011\n */\nmain {\n display: block; }\n\n/*\n* Apply no display to elements with the hidden attribute.\n* IE 9 and 10 support.\n*/\n*[hidden] {\n display: none !important; }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* Typography */\n/* Shadows */\n/* Animations */\nhtml, body {\n font-family: \"Helvetica\", \"Arial\", sans-serif;\n font-size: 14px;\n font-weight: 400;\n line-height: 20px; }\n\nh1, h2, h3, h4, h5, h6, p {\n margin: 0;\n padding: 0; }\n\n/**\n * Styles for HTML elements\n */\nh1 small, h2 small, h3 small, h4 small, h5 small, h6 small {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 56px;\n font-weight: 400;\n line-height: 1.35;\n letter-spacing: -0.02em;\n opacity: 0.54;\n font-size: 0.6em; }\n\nh1 {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 56px;\n font-weight: 400;\n line-height: 1.35;\n letter-spacing: -0.02em;\n margin-top: 24px;\n margin-bottom: 24px; }\n\nh2 {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 45px;\n font-weight: 400;\n line-height: 48px;\n margin-top: 24px;\n margin-bottom: 24px; }\n\nh3 {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 34px;\n font-weight: 400;\n line-height: 40px;\n margin-top: 24px;\n margin-bottom: 24px; }\n\nh4 {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 24px;\n font-weight: 400;\n line-height: 32px;\n -moz-osx-font-smoothing: grayscale;\n margin-top: 24px;\n margin-bottom: 16px; }\n\nh5 {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 20px;\n font-weight: 500;\n line-height: 1;\n letter-spacing: 0.02em;\n margin-top: 24px;\n margin-bottom: 16px; }\n\nh6 {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 16px;\n font-weight: 400;\n line-height: 24px;\n letter-spacing: 0.04em;\n margin-top: 24px;\n margin-bottom: 16px; }\n\np {\n font-size: 14px;\n font-weight: 400;\n line-height: 24px;\n letter-spacing: 0;\n margin-bottom: 16px; }\n\na {\n color: rgb(255,64,129);\n font-weight: 500; }\n\nblockquote {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n position: relative;\n font-size: 24px;\n font-weight: 300;\n font-style: italic;\n line-height: 1.35;\n letter-spacing: 0.08em; }\n blockquote:before {\n position: absolute;\n left: -0.5em;\n content: '“'; }\n blockquote:after {\n content: '”';\n margin-left: -0.05em; }\n\nmark {\n background-color: #f4ff81; }\n\ndt {\n font-weight: 700; }\n\naddress {\n font-size: 12px;\n font-weight: 400;\n line-height: 1;\n letter-spacing: 0;\n font-style: normal; }\n\nul, ol {\n font-size: 14px;\n font-weight: 400;\n line-height: 24px;\n letter-spacing: 0; }\n\n/**\n * Class Name Styles\n */\n.mdl-typography--display-4 {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 112px;\n font-weight: 300;\n line-height: 1;\n letter-spacing: -0.04em; }\n\n.mdl-typography--display-4-color-contrast {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 112px;\n font-weight: 300;\n line-height: 1;\n letter-spacing: -0.04em;\n opacity: 0.54; }\n\n.mdl-typography--display-3 {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 56px;\n font-weight: 400;\n line-height: 1.35;\n letter-spacing: -0.02em; }\n\n.mdl-typography--display-3-color-contrast {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 56px;\n font-weight: 400;\n line-height: 1.35;\n letter-spacing: -0.02em;\n opacity: 0.54; }\n\n.mdl-typography--display-2 {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 45px;\n font-weight: 400;\n line-height: 48px; }\n\n.mdl-typography--display-2-color-contrast {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 45px;\n font-weight: 400;\n line-height: 48px;\n opacity: 0.54; }\n\n.mdl-typography--display-1 {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 34px;\n font-weight: 400;\n line-height: 40px; }\n\n.mdl-typography--display-1-color-contrast {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 34px;\n font-weight: 400;\n line-height: 40px;\n opacity: 0.54; }\n\n.mdl-typography--headline {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 24px;\n font-weight: 400;\n line-height: 32px;\n -moz-osx-font-smoothing: grayscale; }\n\n.mdl-typography--headline-color-contrast {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 24px;\n font-weight: 400;\n line-height: 32px;\n -moz-osx-font-smoothing: grayscale;\n opacity: 0.87; }\n\n.mdl-typography--title {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 20px;\n font-weight: 500;\n line-height: 1;\n letter-spacing: 0.02em; }\n\n.mdl-typography--title-color-contrast {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 20px;\n font-weight: 500;\n line-height: 1;\n letter-spacing: 0.02em;\n opacity: 0.87; }\n\n.mdl-typography--subhead {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 16px;\n font-weight: 400;\n line-height: 24px;\n letter-spacing: 0.04em; }\n\n.mdl-typography--subhead-color-contrast {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 16px;\n font-weight: 400;\n line-height: 24px;\n letter-spacing: 0.04em;\n opacity: 0.87; }\n\n.mdl-typography--body-2 {\n font-size: 14px;\n font-weight: bold;\n line-height: 24px;\n letter-spacing: 0; }\n\n.mdl-typography--body-2-color-contrast {\n font-size: 14px;\n font-weight: bold;\n line-height: 24px;\n letter-spacing: 0;\n opacity: 0.87; }\n\n.mdl-typography--body-1 {\n font-size: 14px;\n font-weight: 400;\n line-height: 24px;\n letter-spacing: 0; }\n\n.mdl-typography--body-1-color-contrast {\n font-size: 14px;\n font-weight: 400;\n line-height: 24px;\n letter-spacing: 0;\n opacity: 0.87; }\n\n.mdl-typography--body-2-force-preferred-font {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 14px;\n font-weight: 500;\n line-height: 24px;\n letter-spacing: 0; }\n\n.mdl-typography--body-2-force-preferred-font-color-contrast {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 14px;\n font-weight: 500;\n line-height: 24px;\n letter-spacing: 0;\n opacity: 0.87; }\n\n.mdl-typography--body-1-force-preferred-font {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 14px;\n font-weight: 400;\n line-height: 24px;\n letter-spacing: 0; }\n\n.mdl-typography--body-1-force-preferred-font-color-contrast {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 14px;\n font-weight: 400;\n line-height: 24px;\n letter-spacing: 0;\n opacity: 0.87; }\n\n.mdl-typography--caption {\n font-size: 12px;\n font-weight: 400;\n line-height: 1;\n letter-spacing: 0; }\n\n.mdl-typography--caption-force-preferred-font {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 12px;\n font-weight: 400;\n line-height: 1;\n letter-spacing: 0; }\n\n.mdl-typography--caption-color-contrast {\n font-size: 12px;\n font-weight: 400;\n line-height: 1;\n letter-spacing: 0;\n opacity: 0.54; }\n\n.mdl-typography--caption-force-preferred-font-color-contrast {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 12px;\n font-weight: 400;\n line-height: 1;\n letter-spacing: 0;\n opacity: 0.54; }\n\n.mdl-typography--menu {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 14px;\n font-weight: 500;\n line-height: 1;\n letter-spacing: 0; }\n\n.mdl-typography--menu-color-contrast {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 14px;\n font-weight: 500;\n line-height: 1;\n letter-spacing: 0;\n opacity: 0.87; }\n\n.mdl-typography--button {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 14px;\n font-weight: 500;\n text-transform: uppercase;\n line-height: 1;\n letter-spacing: 0; }\n\n.mdl-typography--button-color-contrast {\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 14px;\n font-weight: 500;\n text-transform: uppercase;\n line-height: 1;\n letter-spacing: 0;\n opacity: 0.87; }\n\n.mdl-typography--text-left {\n text-align: left; }\n\n.mdl-typography--text-right {\n text-align: right; }\n\n.mdl-typography--text-center {\n text-align: center; }\n\n.mdl-typography--text-justify {\n text-align: justify; }\n\n.mdl-typography--text-nowrap {\n white-space: nowrap; }\n\n.mdl-typography--text-lowercase {\n text-transform: lowercase; }\n\n.mdl-typography--text-uppercase {\n text-transform: uppercase; }\n\n.mdl-typography--text-capitalize {\n text-transform: capitalize; }\n\n.mdl-typography--font-thin {\n font-weight: 200 !important; }\n\n.mdl-typography--font-light {\n font-weight: 300 !important; }\n\n.mdl-typography--font-regular {\n font-weight: 400 !important; }\n\n.mdl-typography--font-medium {\n font-weight: 500 !important; }\n\n.mdl-typography--font-bold {\n font-weight: 700 !important; }\n\n.mdl-typography--font-black {\n font-weight: 900 !important; }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n.mdl-color-text--red {\n color: rgb(244,67,54) !important; }\n\n.mdl-color--red {\n background-color: rgb(244,67,54) !important; }\n\n.mdl-color-text--red-50 {\n color: rgb(255,235,238) !important; }\n\n.mdl-color--red-50 {\n background-color: rgb(255,235,238) !important; }\n\n.mdl-color-text--red-100 {\n color: rgb(255,205,210) !important; }\n\n.mdl-color--red-100 {\n background-color: rgb(255,205,210) !important; }\n\n.mdl-color-text--red-200 {\n color: rgb(239,154,154) !important; }\n\n.mdl-color--red-200 {\n background-color: rgb(239,154,154) !important; }\n\n.mdl-color-text--red-300 {\n color: rgb(229,115,115) !important; }\n\n.mdl-color--red-300 {\n background-color: rgb(229,115,115) !important; }\n\n.mdl-color-text--red-400 {\n color: rgb(239,83,80) !important; }\n\n.mdl-color--red-400 {\n background-color: rgb(239,83,80) !important; }\n\n.mdl-color-text--red-500 {\n color: rgb(244,67,54) !important; }\n\n.mdl-color--red-500 {\n background-color: rgb(244,67,54) !important; }\n\n.mdl-color-text--red-600 {\n color: rgb(229,57,53) !important; }\n\n.mdl-color--red-600 {\n background-color: rgb(229,57,53) !important; }\n\n.mdl-color-text--red-700 {\n color: rgb(211,47,47) !important; }\n\n.mdl-color--red-700 {\n background-color: rgb(211,47,47) !important; }\n\n.mdl-color-text--red-800 {\n color: rgb(198,40,40) !important; }\n\n.mdl-color--red-800 {\n background-color: rgb(198,40,40) !important; }\n\n.mdl-color-text--red-900 {\n color: rgb(183,28,28) !important; }\n\n.mdl-color--red-900 {\n background-color: rgb(183,28,28) !important; }\n\n.mdl-color-text--red-A100 {\n color: rgb(255,138,128) !important; }\n\n.mdl-color--red-A100 {\n background-color: rgb(255,138,128) !important; }\n\n.mdl-color-text--red-A200 {\n color: rgb(255,82,82) !important; }\n\n.mdl-color--red-A200 {\n background-color: rgb(255,82,82) !important; }\n\n.mdl-color-text--red-A400 {\n color: rgb(255,23,68) !important; }\n\n.mdl-color--red-A400 {\n background-color: rgb(255,23,68) !important; }\n\n.mdl-color-text--red-A700 {\n color: rgb(213,0,0) !important; }\n\n.mdl-color--red-A700 {\n background-color: rgb(213,0,0) !important; }\n\n.mdl-color-text--pink {\n color: rgb(233,30,99) !important; }\n\n.mdl-color--pink {\n background-color: rgb(233,30,99) !important; }\n\n.mdl-color-text--pink-50 {\n color: rgb(252,228,236) !important; }\n\n.mdl-color--pink-50 {\n background-color: rgb(252,228,236) !important; }\n\n.mdl-color-text--pink-100 {\n color: rgb(248,187,208) !important; }\n\n.mdl-color--pink-100 {\n background-color: rgb(248,187,208) !important; }\n\n.mdl-color-text--pink-200 {\n color: rgb(244,143,177) !important; }\n\n.mdl-color--pink-200 {\n background-color: rgb(244,143,177) !important; }\n\n.mdl-color-text--pink-300 {\n color: rgb(240,98,146) !important; }\n\n.mdl-color--pink-300 {\n background-color: rgb(240,98,146) !important; }\n\n.mdl-color-text--pink-400 {\n color: rgb(236,64,122) !important; }\n\n.mdl-color--pink-400 {\n background-color: rgb(236,64,122) !important; }\n\n.mdl-color-text--pink-500 {\n color: rgb(233,30,99) !important; }\n\n.mdl-color--pink-500 {\n background-color: rgb(233,30,99) !important; }\n\n.mdl-color-text--pink-600 {\n color: rgb(216,27,96) !important; }\n\n.mdl-color--pink-600 {\n background-color: rgb(216,27,96) !important; }\n\n.mdl-color-text--pink-700 {\n color: rgb(194,24,91) !important; }\n\n.mdl-color--pink-700 {\n background-color: rgb(194,24,91) !important; }\n\n.mdl-color-text--pink-800 {\n color: rgb(173,20,87) !important; }\n\n.mdl-color--pink-800 {\n background-color: rgb(173,20,87) !important; }\n\n.mdl-color-text--pink-900 {\n color: rgb(136,14,79) !important; }\n\n.mdl-color--pink-900 {\n background-color: rgb(136,14,79) !important; }\n\n.mdl-color-text--pink-A100 {\n color: rgb(255,128,171) !important; }\n\n.mdl-color--pink-A100 {\n background-color: rgb(255,128,171) !important; }\n\n.mdl-color-text--pink-A200 {\n color: rgb(255,64,129) !important; }\n\n.mdl-color--pink-A200 {\n background-color: rgb(255,64,129) !important; }\n\n.mdl-color-text--pink-A400 {\n color: rgb(245,0,87) !important; }\n\n.mdl-color--pink-A400 {\n background-color: rgb(245,0,87) !important; }\n\n.mdl-color-text--pink-A700 {\n color: rgb(197,17,98) !important; }\n\n.mdl-color--pink-A700 {\n background-color: rgb(197,17,98) !important; }\n\n.mdl-color-text--purple {\n color: rgb(156,39,176) !important; }\n\n.mdl-color--purple {\n background-color: rgb(156,39,176) !important; }\n\n.mdl-color-text--purple-50 {\n color: rgb(243,229,245) !important; }\n\n.mdl-color--purple-50 {\n background-color: rgb(243,229,245) !important; }\n\n.mdl-color-text--purple-100 {\n color: rgb(225,190,231) !important; }\n\n.mdl-color--purple-100 {\n background-color: rgb(225,190,231) !important; }\n\n.mdl-color-text--purple-200 {\n color: rgb(206,147,216) !important; }\n\n.mdl-color--purple-200 {\n background-color: rgb(206,147,216) !important; }\n\n.mdl-color-text--purple-300 {\n color: rgb(186,104,200) !important; }\n\n.mdl-color--purple-300 {\n background-color: rgb(186,104,200) !important; }\n\n.mdl-color-text--purple-400 {\n color: rgb(171,71,188) !important; }\n\n.mdl-color--purple-400 {\n background-color: rgb(171,71,188) !important; }\n\n.mdl-color-text--purple-500 {\n color: rgb(156,39,176) !important; }\n\n.mdl-color--purple-500 {\n background-color: rgb(156,39,176) !important; }\n\n.mdl-color-text--purple-600 {\n color: rgb(142,36,170) !important; }\n\n.mdl-color--purple-600 {\n background-color: rgb(142,36,170) !important; }\n\n.mdl-color-text--purple-700 {\n color: rgb(123,31,162) !important; }\n\n.mdl-color--purple-700 {\n background-color: rgb(123,31,162) !important; }\n\n.mdl-color-text--purple-800 {\n color: rgb(106,27,154) !important; }\n\n.mdl-color--purple-800 {\n background-color: rgb(106,27,154) !important; }\n\n.mdl-color-text--purple-900 {\n color: rgb(74,20,140) !important; }\n\n.mdl-color--purple-900 {\n background-color: rgb(74,20,140) !important; }\n\n.mdl-color-text--purple-A100 {\n color: rgb(234,128,252) !important; }\n\n.mdl-color--purple-A100 {\n background-color: rgb(234,128,252) !important; }\n\n.mdl-color-text--purple-A200 {\n color: rgb(224,64,251) !important; }\n\n.mdl-color--purple-A200 {\n background-color: rgb(224,64,251) !important; }\n\n.mdl-color-text--purple-A400 {\n color: rgb(213,0,249) !important; }\n\n.mdl-color--purple-A400 {\n background-color: rgb(213,0,249) !important; }\n\n.mdl-color-text--purple-A700 {\n color: rgb(170,0,255) !important; }\n\n.mdl-color--purple-A700 {\n background-color: rgb(170,0,255) !important; }\n\n.mdl-color-text--deep-purple {\n color: rgb(103,58,183) !important; }\n\n.mdl-color--deep-purple {\n background-color: rgb(103,58,183) !important; }\n\n.mdl-color-text--deep-purple-50 {\n color: rgb(237,231,246) !important; }\n\n.mdl-color--deep-purple-50 {\n background-color: rgb(237,231,246) !important; }\n\n.mdl-color-text--deep-purple-100 {\n color: rgb(209,196,233) !important; }\n\n.mdl-color--deep-purple-100 {\n background-color: rgb(209,196,233) !important; }\n\n.mdl-color-text--deep-purple-200 {\n color: rgb(179,157,219) !important; }\n\n.mdl-color--deep-purple-200 {\n background-color: rgb(179,157,219) !important; }\n\n.mdl-color-text--deep-purple-300 {\n color: rgb(149,117,205) !important; }\n\n.mdl-color--deep-purple-300 {\n background-color: rgb(149,117,205) !important; }\n\n.mdl-color-text--deep-purple-400 {\n color: rgb(126,87,194) !important; }\n\n.mdl-color--deep-purple-400 {\n background-color: rgb(126,87,194) !important; }\n\n.mdl-color-text--deep-purple-500 {\n color: rgb(103,58,183) !important; }\n\n.mdl-color--deep-purple-500 {\n background-color: rgb(103,58,183) !important; }\n\n.mdl-color-text--deep-purple-600 {\n color: rgb(94,53,177) !important; }\n\n.mdl-color--deep-purple-600 {\n background-color: rgb(94,53,177) !important; }\n\n.mdl-color-text--deep-purple-700 {\n color: rgb(81,45,168) !important; }\n\n.mdl-color--deep-purple-700 {\n background-color: rgb(81,45,168) !important; }\n\n.mdl-color-text--deep-purple-800 {\n color: rgb(69,39,160) !important; }\n\n.mdl-color--deep-purple-800 {\n background-color: rgb(69,39,160) !important; }\n\n.mdl-color-text--deep-purple-900 {\n color: rgb(49,27,146) !important; }\n\n.mdl-color--deep-purple-900 {\n background-color: rgb(49,27,146) !important; }\n\n.mdl-color-text--deep-purple-A100 {\n color: rgb(179,136,255) !important; }\n\n.mdl-color--deep-purple-A100 {\n background-color: rgb(179,136,255) !important; }\n\n.mdl-color-text--deep-purple-A200 {\n color: rgb(124,77,255) !important; }\n\n.mdl-color--deep-purple-A200 {\n background-color: rgb(124,77,255) !important; }\n\n.mdl-color-text--deep-purple-A400 {\n color: rgb(101,31,255) !important; }\n\n.mdl-color--deep-purple-A400 {\n background-color: rgb(101,31,255) !important; }\n\n.mdl-color-text--deep-purple-A700 {\n color: rgb(98,0,234) !important; }\n\n.mdl-color--deep-purple-A700 {\n background-color: rgb(98,0,234) !important; }\n\n.mdl-color-text--indigo {\n color: rgb(63,81,181) !important; }\n\n.mdl-color--indigo {\n background-color: rgb(63,81,181) !important; }\n\n.mdl-color-text--indigo-50 {\n color: rgb(232,234,246) !important; }\n\n.mdl-color--indigo-50 {\n background-color: rgb(232,234,246) !important; }\n\n.mdl-color-text--indigo-100 {\n color: rgb(197,202,233) !important; }\n\n.mdl-color--indigo-100 {\n background-color: rgb(197,202,233) !important; }\n\n.mdl-color-text--indigo-200 {\n color: rgb(159,168,218) !important; }\n\n.mdl-color--indigo-200 {\n background-color: rgb(159,168,218) !important; }\n\n.mdl-color-text--indigo-300 {\n color: rgb(121,134,203) !important; }\n\n.mdl-color--indigo-300 {\n background-color: rgb(121,134,203) !important; }\n\n.mdl-color-text--indigo-400 {\n color: rgb(92,107,192) !important; }\n\n.mdl-color--indigo-400 {\n background-color: rgb(92,107,192) !important; }\n\n.mdl-color-text--indigo-500 {\n color: rgb(63,81,181) !important; }\n\n.mdl-color--indigo-500 {\n background-color: rgb(63,81,181) !important; }\n\n.mdl-color-text--indigo-600 {\n color: rgb(57,73,171) !important; }\n\n.mdl-color--indigo-600 {\n background-color: rgb(57,73,171) !important; }\n\n.mdl-color-text--indigo-700 {\n color: rgb(48,63,159) !important; }\n\n.mdl-color--indigo-700 {\n background-color: rgb(48,63,159) !important; }\n\n.mdl-color-text--indigo-800 {\n color: rgb(40,53,147) !important; }\n\n.mdl-color--indigo-800 {\n background-color: rgb(40,53,147) !important; }\n\n.mdl-color-text--indigo-900 {\n color: rgb(26,35,126) !important; }\n\n.mdl-color--indigo-900 {\n background-color: rgb(26,35,126) !important; }\n\n.mdl-color-text--indigo-A100 {\n color: rgb(140,158,255) !important; }\n\n.mdl-color--indigo-A100 {\n background-color: rgb(140,158,255) !important; }\n\n.mdl-color-text--indigo-A200 {\n color: rgb(83,109,254) !important; }\n\n.mdl-color--indigo-A200 {\n background-color: rgb(83,109,254) !important; }\n\n.mdl-color-text--indigo-A400 {\n color: rgb(61,90,254) !important; }\n\n.mdl-color--indigo-A400 {\n background-color: rgb(61,90,254) !important; }\n\n.mdl-color-text--indigo-A700 {\n color: rgb(48,79,254) !important; }\n\n.mdl-color--indigo-A700 {\n background-color: rgb(48,79,254) !important; }\n\n.mdl-color-text--blue {\n color: rgb(33,150,243) !important; }\n\n.mdl-color--blue {\n background-color: rgb(33,150,243) !important; }\n\n.mdl-color-text--blue-50 {\n color: rgb(227,242,253) !important; }\n\n.mdl-color--blue-50 {\n background-color: rgb(227,242,253) !important; }\n\n.mdl-color-text--blue-100 {\n color: rgb(187,222,251) !important; }\n\n.mdl-color--blue-100 {\n background-color: rgb(187,222,251) !important; }\n\n.mdl-color-text--blue-200 {\n color: rgb(144,202,249) !important; }\n\n.mdl-color--blue-200 {\n background-color: rgb(144,202,249) !important; }\n\n.mdl-color-text--blue-300 {\n color: rgb(100,181,246) !important; }\n\n.mdl-color--blue-300 {\n background-color: rgb(100,181,246) !important; }\n\n.mdl-color-text--blue-400 {\n color: rgb(66,165,245) !important; }\n\n.mdl-color--blue-400 {\n background-color: rgb(66,165,245) !important; }\n\n.mdl-color-text--blue-500 {\n color: rgb(33,150,243) !important; }\n\n.mdl-color--blue-500 {\n background-color: rgb(33,150,243) !important; }\n\n.mdl-color-text--blue-600 {\n color: rgb(30,136,229) !important; }\n\n.mdl-color--blue-600 {\n background-color: rgb(30,136,229) !important; }\n\n.mdl-color-text--blue-700 {\n color: rgb(25,118,210) !important; }\n\n.mdl-color--blue-700 {\n background-color: rgb(25,118,210) !important; }\n\n.mdl-color-text--blue-800 {\n color: rgb(21,101,192) !important; }\n\n.mdl-color--blue-800 {\n background-color: rgb(21,101,192) !important; }\n\n.mdl-color-text--blue-900 {\n color: rgb(13,71,161) !important; }\n\n.mdl-color--blue-900 {\n background-color: rgb(13,71,161) !important; }\n\n.mdl-color-text--blue-A100 {\n color: rgb(130,177,255) !important; }\n\n.mdl-color--blue-A100 {\n background-color: rgb(130,177,255) !important; }\n\n.mdl-color-text--blue-A200 {\n color: rgb(68,138,255) !important; }\n\n.mdl-color--blue-A200 {\n background-color: rgb(68,138,255) !important; }\n\n.mdl-color-text--blue-A400 {\n color: rgb(41,121,255) !important; }\n\n.mdl-color--blue-A400 {\n background-color: rgb(41,121,255) !important; }\n\n.mdl-color-text--blue-A700 {\n color: rgb(41,98,255) !important; }\n\n.mdl-color--blue-A700 {\n background-color: rgb(41,98,255) !important; }\n\n.mdl-color-text--light-blue {\n color: rgb(3,169,244) !important; }\n\n.mdl-color--light-blue {\n background-color: rgb(3,169,244) !important; }\n\n.mdl-color-text--light-blue-50 {\n color: rgb(225,245,254) !important; }\n\n.mdl-color--light-blue-50 {\n background-color: rgb(225,245,254) !important; }\n\n.mdl-color-text--light-blue-100 {\n color: rgb(179,229,252) !important; }\n\n.mdl-color--light-blue-100 {\n background-color: rgb(179,229,252) !important; }\n\n.mdl-color-text--light-blue-200 {\n color: rgb(129,212,250) !important; }\n\n.mdl-color--light-blue-200 {\n background-color: rgb(129,212,250) !important; }\n\n.mdl-color-text--light-blue-300 {\n color: rgb(79,195,247) !important; }\n\n.mdl-color--light-blue-300 {\n background-color: rgb(79,195,247) !important; }\n\n.mdl-color-text--light-blue-400 {\n color: rgb(41,182,246) !important; }\n\n.mdl-color--light-blue-400 {\n background-color: rgb(41,182,246) !important; }\n\n.mdl-color-text--light-blue-500 {\n color: rgb(3,169,244) !important; }\n\n.mdl-color--light-blue-500 {\n background-color: rgb(3,169,244) !important; }\n\n.mdl-color-text--light-blue-600 {\n color: rgb(3,155,229) !important; }\n\n.mdl-color--light-blue-600 {\n background-color: rgb(3,155,229) !important; }\n\n.mdl-color-text--light-blue-700 {\n color: rgb(2,136,209) !important; }\n\n.mdl-color--light-blue-700 {\n background-color: rgb(2,136,209) !important; }\n\n.mdl-color-text--light-blue-800 {\n color: rgb(2,119,189) !important; }\n\n.mdl-color--light-blue-800 {\n background-color: rgb(2,119,189) !important; }\n\n.mdl-color-text--light-blue-900 {\n color: rgb(1,87,155) !important; }\n\n.mdl-color--light-blue-900 {\n background-color: rgb(1,87,155) !important; }\n\n.mdl-color-text--light-blue-A100 {\n color: rgb(128,216,255) !important; }\n\n.mdl-color--light-blue-A100 {\n background-color: rgb(128,216,255) !important; }\n\n.mdl-color-text--light-blue-A200 {\n color: rgb(64,196,255) !important; }\n\n.mdl-color--light-blue-A200 {\n background-color: rgb(64,196,255) !important; }\n\n.mdl-color-text--light-blue-A400 {\n color: rgb(0,176,255) !important; }\n\n.mdl-color--light-blue-A400 {\n background-color: rgb(0,176,255) !important; }\n\n.mdl-color-text--light-blue-A700 {\n color: rgb(0,145,234) !important; }\n\n.mdl-color--light-blue-A700 {\n background-color: rgb(0,145,234) !important; }\n\n.mdl-color-text--cyan {\n color: rgb(0,188,212) !important; }\n\n.mdl-color--cyan {\n background-color: rgb(0,188,212) !important; }\n\n.mdl-color-text--cyan-50 {\n color: rgb(224,247,250) !important; }\n\n.mdl-color--cyan-50 {\n background-color: rgb(224,247,250) !important; }\n\n.mdl-color-text--cyan-100 {\n color: rgb(178,235,242) !important; }\n\n.mdl-color--cyan-100 {\n background-color: rgb(178,235,242) !important; }\n\n.mdl-color-text--cyan-200 {\n color: rgb(128,222,234) !important; }\n\n.mdl-color--cyan-200 {\n background-color: rgb(128,222,234) !important; }\n\n.mdl-color-text--cyan-300 {\n color: rgb(77,208,225) !important; }\n\n.mdl-color--cyan-300 {\n background-color: rgb(77,208,225) !important; }\n\n.mdl-color-text--cyan-400 {\n color: rgb(38,198,218) !important; }\n\n.mdl-color--cyan-400 {\n background-color: rgb(38,198,218) !important; }\n\n.mdl-color-text--cyan-500 {\n color: rgb(0,188,212) !important; }\n\n.mdl-color--cyan-500 {\n background-color: rgb(0,188,212) !important; }\n\n.mdl-color-text--cyan-600 {\n color: rgb(0,172,193) !important; }\n\n.mdl-color--cyan-600 {\n background-color: rgb(0,172,193) !important; }\n\n.mdl-color-text--cyan-700 {\n color: rgb(0,151,167) !important; }\n\n.mdl-color--cyan-700 {\n background-color: rgb(0,151,167) !important; }\n\n.mdl-color-text--cyan-800 {\n color: rgb(0,131,143) !important; }\n\n.mdl-color--cyan-800 {\n background-color: rgb(0,131,143) !important; }\n\n.mdl-color-text--cyan-900 {\n color: rgb(0,96,100) !important; }\n\n.mdl-color--cyan-900 {\n background-color: rgb(0,96,100) !important; }\n\n.mdl-color-text--cyan-A100 {\n color: rgb(132,255,255) !important; }\n\n.mdl-color--cyan-A100 {\n background-color: rgb(132,255,255) !important; }\n\n.mdl-color-text--cyan-A200 {\n color: rgb(24,255,255) !important; }\n\n.mdl-color--cyan-A200 {\n background-color: rgb(24,255,255) !important; }\n\n.mdl-color-text--cyan-A400 {\n color: rgb(0,229,255) !important; }\n\n.mdl-color--cyan-A400 {\n background-color: rgb(0,229,255) !important; }\n\n.mdl-color-text--cyan-A700 {\n color: rgb(0,184,212) !important; }\n\n.mdl-color--cyan-A700 {\n background-color: rgb(0,184,212) !important; }\n\n.mdl-color-text--teal {\n color: rgb(0,150,136) !important; }\n\n.mdl-color--teal {\n background-color: rgb(0,150,136) !important; }\n\n.mdl-color-text--teal-50 {\n color: rgb(224,242,241) !important; }\n\n.mdl-color--teal-50 {\n background-color: rgb(224,242,241) !important; }\n\n.mdl-color-text--teal-100 {\n color: rgb(178,223,219) !important; }\n\n.mdl-color--teal-100 {\n background-color: rgb(178,223,219) !important; }\n\n.mdl-color-text--teal-200 {\n color: rgb(128,203,196) !important; }\n\n.mdl-color--teal-200 {\n background-color: rgb(128,203,196) !important; }\n\n.mdl-color-text--teal-300 {\n color: rgb(77,182,172) !important; }\n\n.mdl-color--teal-300 {\n background-color: rgb(77,182,172) !important; }\n\n.mdl-color-text--teal-400 {\n color: rgb(38,166,154) !important; }\n\n.mdl-color--teal-400 {\n background-color: rgb(38,166,154) !important; }\n\n.mdl-color-text--teal-500 {\n color: rgb(0,150,136) !important; }\n\n.mdl-color--teal-500 {\n background-color: rgb(0,150,136) !important; }\n\n.mdl-color-text--teal-600 {\n color: rgb(0,137,123) !important; }\n\n.mdl-color--teal-600 {\n background-color: rgb(0,137,123) !important; }\n\n.mdl-color-text--teal-700 {\n color: rgb(0,121,107) !important; }\n\n.mdl-color--teal-700 {\n background-color: rgb(0,121,107) !important; }\n\n.mdl-color-text--teal-800 {\n color: rgb(0,105,92) !important; }\n\n.mdl-color--teal-800 {\n background-color: rgb(0,105,92) !important; }\n\n.mdl-color-text--teal-900 {\n color: rgb(0,77,64) !important; }\n\n.mdl-color--teal-900 {\n background-color: rgb(0,77,64) !important; }\n\n.mdl-color-text--teal-A100 {\n color: rgb(167,255,235) !important; }\n\n.mdl-color--teal-A100 {\n background-color: rgb(167,255,235) !important; }\n\n.mdl-color-text--teal-A200 {\n color: rgb(100,255,218) !important; }\n\n.mdl-color--teal-A200 {\n background-color: rgb(100,255,218) !important; }\n\n.mdl-color-text--teal-A400 {\n color: rgb(29,233,182) !important; }\n\n.mdl-color--teal-A400 {\n background-color: rgb(29,233,182) !important; }\n\n.mdl-color-text--teal-A700 {\n color: rgb(0,191,165) !important; }\n\n.mdl-color--teal-A700 {\n background-color: rgb(0,191,165) !important; }\n\n.mdl-color-text--green {\n color: rgb(76,175,80) !important; }\n\n.mdl-color--green {\n background-color: rgb(76,175,80) !important; }\n\n.mdl-color-text--green-50 {\n color: rgb(232,245,233) !important; }\n\n.mdl-color--green-50 {\n background-color: rgb(232,245,233) !important; }\n\n.mdl-color-text--green-100 {\n color: rgb(200,230,201) !important; }\n\n.mdl-color--green-100 {\n background-color: rgb(200,230,201) !important; }\n\n.mdl-color-text--green-200 {\n color: rgb(165,214,167) !important; }\n\n.mdl-color--green-200 {\n background-color: rgb(165,214,167) !important; }\n\n.mdl-color-text--green-300 {\n color: rgb(129,199,132) !important; }\n\n.mdl-color--green-300 {\n background-color: rgb(129,199,132) !important; }\n\n.mdl-color-text--green-400 {\n color: rgb(102,187,106) !important; }\n\n.mdl-color--green-400 {\n background-color: rgb(102,187,106) !important; }\n\n.mdl-color-text--green-500 {\n color: rgb(76,175,80) !important; }\n\n.mdl-color--green-500 {\n background-color: rgb(76,175,80) !important; }\n\n.mdl-color-text--green-600 {\n color: rgb(67,160,71) !important; }\n\n.mdl-color--green-600 {\n background-color: rgb(67,160,71) !important; }\n\n.mdl-color-text--green-700 {\n color: rgb(56,142,60) !important; }\n\n.mdl-color--green-700 {\n background-color: rgb(56,142,60) !important; }\n\n.mdl-color-text--green-800 {\n color: rgb(46,125,50) !important; }\n\n.mdl-color--green-800 {\n background-color: rgb(46,125,50) !important; }\n\n.mdl-color-text--green-900 {\n color: rgb(27,94,32) !important; }\n\n.mdl-color--green-900 {\n background-color: rgb(27,94,32) !important; }\n\n.mdl-color-text--green-A100 {\n color: rgb(185,246,202) !important; }\n\n.mdl-color--green-A100 {\n background-color: rgb(185,246,202) !important; }\n\n.mdl-color-text--green-A200 {\n color: rgb(105,240,174) !important; }\n\n.mdl-color--green-A200 {\n background-color: rgb(105,240,174) !important; }\n\n.mdl-color-text--green-A400 {\n color: rgb(0,230,118) !important; }\n\n.mdl-color--green-A400 {\n background-color: rgb(0,230,118) !important; }\n\n.mdl-color-text--green-A700 {\n color: rgb(0,200,83) !important; }\n\n.mdl-color--green-A700 {\n background-color: rgb(0,200,83) !important; }\n\n.mdl-color-text--light-green {\n color: rgb(139,195,74) !important; }\n\n.mdl-color--light-green {\n background-color: rgb(139,195,74) !important; }\n\n.mdl-color-text--light-green-50 {\n color: rgb(241,248,233) !important; }\n\n.mdl-color--light-green-50 {\n background-color: rgb(241,248,233) !important; }\n\n.mdl-color-text--light-green-100 {\n color: rgb(220,237,200) !important; }\n\n.mdl-color--light-green-100 {\n background-color: rgb(220,237,200) !important; }\n\n.mdl-color-text--light-green-200 {\n color: rgb(197,225,165) !important; }\n\n.mdl-color--light-green-200 {\n background-color: rgb(197,225,165) !important; }\n\n.mdl-color-text--light-green-300 {\n color: rgb(174,213,129) !important; }\n\n.mdl-color--light-green-300 {\n background-color: rgb(174,213,129) !important; }\n\n.mdl-color-text--light-green-400 {\n color: rgb(156,204,101) !important; }\n\n.mdl-color--light-green-400 {\n background-color: rgb(156,204,101) !important; }\n\n.mdl-color-text--light-green-500 {\n color: rgb(139,195,74) !important; }\n\n.mdl-color--light-green-500 {\n background-color: rgb(139,195,74) !important; }\n\n.mdl-color-text--light-green-600 {\n color: rgb(124,179,66) !important; }\n\n.mdl-color--light-green-600 {\n background-color: rgb(124,179,66) !important; }\n\n.mdl-color-text--light-green-700 {\n color: rgb(104,159,56) !important; }\n\n.mdl-color--light-green-700 {\n background-color: rgb(104,159,56) !important; }\n\n.mdl-color-text--light-green-800 {\n color: rgb(85,139,47) !important; }\n\n.mdl-color--light-green-800 {\n background-color: rgb(85,139,47) !important; }\n\n.mdl-color-text--light-green-900 {\n color: rgb(51,105,30) !important; }\n\n.mdl-color--light-green-900 {\n background-color: rgb(51,105,30) !important; }\n\n.mdl-color-text--light-green-A100 {\n color: rgb(204,255,144) !important; }\n\n.mdl-color--light-green-A100 {\n background-color: rgb(204,255,144) !important; }\n\n.mdl-color-text--light-green-A200 {\n color: rgb(178,255,89) !important; }\n\n.mdl-color--light-green-A200 {\n background-color: rgb(178,255,89) !important; }\n\n.mdl-color-text--light-green-A400 {\n color: rgb(118,255,3) !important; }\n\n.mdl-color--light-green-A400 {\n background-color: rgb(118,255,3) !important; }\n\n.mdl-color-text--light-green-A700 {\n color: rgb(100,221,23) !important; }\n\n.mdl-color--light-green-A700 {\n background-color: rgb(100,221,23) !important; }\n\n.mdl-color-text--lime {\n color: rgb(205,220,57) !important; }\n\n.mdl-color--lime {\n background-color: rgb(205,220,57) !important; }\n\n.mdl-color-text--lime-50 {\n color: rgb(249,251,231) !important; }\n\n.mdl-color--lime-50 {\n background-color: rgb(249,251,231) !important; }\n\n.mdl-color-text--lime-100 {\n color: rgb(240,244,195) !important; }\n\n.mdl-color--lime-100 {\n background-color: rgb(240,244,195) !important; }\n\n.mdl-color-text--lime-200 {\n color: rgb(230,238,156) !important; }\n\n.mdl-color--lime-200 {\n background-color: rgb(230,238,156) !important; }\n\n.mdl-color-text--lime-300 {\n color: rgb(220,231,117) !important; }\n\n.mdl-color--lime-300 {\n background-color: rgb(220,231,117) !important; }\n\n.mdl-color-text--lime-400 {\n color: rgb(212,225,87) !important; }\n\n.mdl-color--lime-400 {\n background-color: rgb(212,225,87) !important; }\n\n.mdl-color-text--lime-500 {\n color: rgb(205,220,57) !important; }\n\n.mdl-color--lime-500 {\n background-color: rgb(205,220,57) !important; }\n\n.mdl-color-text--lime-600 {\n color: rgb(192,202,51) !important; }\n\n.mdl-color--lime-600 {\n background-color: rgb(192,202,51) !important; }\n\n.mdl-color-text--lime-700 {\n color: rgb(175,180,43) !important; }\n\n.mdl-color--lime-700 {\n background-color: rgb(175,180,43) !important; }\n\n.mdl-color-text--lime-800 {\n color: rgb(158,157,36) !important; }\n\n.mdl-color--lime-800 {\n background-color: rgb(158,157,36) !important; }\n\n.mdl-color-text--lime-900 {\n color: rgb(130,119,23) !important; }\n\n.mdl-color--lime-900 {\n background-color: rgb(130,119,23) !important; }\n\n.mdl-color-text--lime-A100 {\n color: rgb(244,255,129) !important; }\n\n.mdl-color--lime-A100 {\n background-color: rgb(244,255,129) !important; }\n\n.mdl-color-text--lime-A200 {\n color: rgb(238,255,65) !important; }\n\n.mdl-color--lime-A200 {\n background-color: rgb(238,255,65) !important; }\n\n.mdl-color-text--lime-A400 {\n color: rgb(198,255,0) !important; }\n\n.mdl-color--lime-A400 {\n background-color: rgb(198,255,0) !important; }\n\n.mdl-color-text--lime-A700 {\n color: rgb(174,234,0) !important; }\n\n.mdl-color--lime-A700 {\n background-color: rgb(174,234,0) !important; }\n\n.mdl-color-text--yellow {\n color: rgb(255,235,59) !important; }\n\n.mdl-color--yellow {\n background-color: rgb(255,235,59) !important; }\n\n.mdl-color-text--yellow-50 {\n color: rgb(255,253,231) !important; }\n\n.mdl-color--yellow-50 {\n background-color: rgb(255,253,231) !important; }\n\n.mdl-color-text--yellow-100 {\n color: rgb(255,249,196) !important; }\n\n.mdl-color--yellow-100 {\n background-color: rgb(255,249,196) !important; }\n\n.mdl-color-text--yellow-200 {\n color: rgb(255,245,157) !important; }\n\n.mdl-color--yellow-200 {\n background-color: rgb(255,245,157) !important; }\n\n.mdl-color-text--yellow-300 {\n color: rgb(255,241,118) !important; }\n\n.mdl-color--yellow-300 {\n background-color: rgb(255,241,118) !important; }\n\n.mdl-color-text--yellow-400 {\n color: rgb(255,238,88) !important; }\n\n.mdl-color--yellow-400 {\n background-color: rgb(255,238,88) !important; }\n\n.mdl-color-text--yellow-500 {\n color: rgb(255,235,59) !important; }\n\n.mdl-color--yellow-500 {\n background-color: rgb(255,235,59) !important; }\n\n.mdl-color-text--yellow-600 {\n color: rgb(253,216,53) !important; }\n\n.mdl-color--yellow-600 {\n background-color: rgb(253,216,53) !important; }\n\n.mdl-color-text--yellow-700 {\n color: rgb(251,192,45) !important; }\n\n.mdl-color--yellow-700 {\n background-color: rgb(251,192,45) !important; }\n\n.mdl-color-text--yellow-800 {\n color: rgb(249,168,37) !important; }\n\n.mdl-color--yellow-800 {\n background-color: rgb(249,168,37) !important; }\n\n.mdl-color-text--yellow-900 {\n color: rgb(245,127,23) !important; }\n\n.mdl-color--yellow-900 {\n background-color: rgb(245,127,23) !important; }\n\n.mdl-color-text--yellow-A100 {\n color: rgb(255,255,141) !important; }\n\n.mdl-color--yellow-A100 {\n background-color: rgb(255,255,141) !important; }\n\n.mdl-color-text--yellow-A200 {\n color: rgb(255,255,0) !important; }\n\n.mdl-color--yellow-A200 {\n background-color: rgb(255,255,0) !important; }\n\n.mdl-color-text--yellow-A400 {\n color: rgb(255,234,0) !important; }\n\n.mdl-color--yellow-A400 {\n background-color: rgb(255,234,0) !important; }\n\n.mdl-color-text--yellow-A700 {\n color: rgb(255,214,0) !important; }\n\n.mdl-color--yellow-A700 {\n background-color: rgb(255,214,0) !important; }\n\n.mdl-color-text--amber {\n color: rgb(255,193,7) !important; }\n\n.mdl-color--amber {\n background-color: rgb(255,193,7) !important; }\n\n.mdl-color-text--amber-50 {\n color: rgb(255,248,225) !important; }\n\n.mdl-color--amber-50 {\n background-color: rgb(255,248,225) !important; }\n\n.mdl-color-text--amber-100 {\n color: rgb(255,236,179) !important; }\n\n.mdl-color--amber-100 {\n background-color: rgb(255,236,179) !important; }\n\n.mdl-color-text--amber-200 {\n color: rgb(255,224,130) !important; }\n\n.mdl-color--amber-200 {\n background-color: rgb(255,224,130) !important; }\n\n.mdl-color-text--amber-300 {\n color: rgb(255,213,79) !important; }\n\n.mdl-color--amber-300 {\n background-color: rgb(255,213,79) !important; }\n\n.mdl-color-text--amber-400 {\n color: rgb(255,202,40) !important; }\n\n.mdl-color--amber-400 {\n background-color: rgb(255,202,40) !important; }\n\n.mdl-color-text--amber-500 {\n color: rgb(255,193,7) !important; }\n\n.mdl-color--amber-500 {\n background-color: rgb(255,193,7) !important; }\n\n.mdl-color-text--amber-600 {\n color: rgb(255,179,0) !important; }\n\n.mdl-color--amber-600 {\n background-color: rgb(255,179,0) !important; }\n\n.mdl-color-text--amber-700 {\n color: rgb(255,160,0) !important; }\n\n.mdl-color--amber-700 {\n background-color: rgb(255,160,0) !important; }\n\n.mdl-color-text--amber-800 {\n color: rgb(255,143,0) !important; }\n\n.mdl-color--amber-800 {\n background-color: rgb(255,143,0) !important; }\n\n.mdl-color-text--amber-900 {\n color: rgb(255,111,0) !important; }\n\n.mdl-color--amber-900 {\n background-color: rgb(255,111,0) !important; }\n\n.mdl-color-text--amber-A100 {\n color: rgb(255,229,127) !important; }\n\n.mdl-color--amber-A100 {\n background-color: rgb(255,229,127) !important; }\n\n.mdl-color-text--amber-A200 {\n color: rgb(255,215,64) !important; }\n\n.mdl-color--amber-A200 {\n background-color: rgb(255,215,64) !important; }\n\n.mdl-color-text--amber-A400 {\n color: rgb(255,196,0) !important; }\n\n.mdl-color--amber-A400 {\n background-color: rgb(255,196,0) !important; }\n\n.mdl-color-text--amber-A700 {\n color: rgb(255,171,0) !important; }\n\n.mdl-color--amber-A700 {\n background-color: rgb(255,171,0) !important; }\n\n.mdl-color-text--orange {\n color: rgb(255,152,0) !important; }\n\n.mdl-color--orange {\n background-color: rgb(255,152,0) !important; }\n\n.mdl-color-text--orange-50 {\n color: rgb(255,243,224) !important; }\n\n.mdl-color--orange-50 {\n background-color: rgb(255,243,224) !important; }\n\n.mdl-color-text--orange-100 {\n color: rgb(255,224,178) !important; }\n\n.mdl-color--orange-100 {\n background-color: rgb(255,224,178) !important; }\n\n.mdl-color-text--orange-200 {\n color: rgb(255,204,128) !important; }\n\n.mdl-color--orange-200 {\n background-color: rgb(255,204,128) !important; }\n\n.mdl-color-text--orange-300 {\n color: rgb(255,183,77) !important; }\n\n.mdl-color--orange-300 {\n background-color: rgb(255,183,77) !important; }\n\n.mdl-color-text--orange-400 {\n color: rgb(255,167,38) !important; }\n\n.mdl-color--orange-400 {\n background-color: rgb(255,167,38) !important; }\n\n.mdl-color-text--orange-500 {\n color: rgb(255,152,0) !important; }\n\n.mdl-color--orange-500 {\n background-color: rgb(255,152,0) !important; }\n\n.mdl-color-text--orange-600 {\n color: rgb(251,140,0) !important; }\n\n.mdl-color--orange-600 {\n background-color: rgb(251,140,0) !important; }\n\n.mdl-color-text--orange-700 {\n color: rgb(245,124,0) !important; }\n\n.mdl-color--orange-700 {\n background-color: rgb(245,124,0) !important; }\n\n.mdl-color-text--orange-800 {\n color: rgb(239,108,0) !important; }\n\n.mdl-color--orange-800 {\n background-color: rgb(239,108,0) !important; }\n\n.mdl-color-text--orange-900 {\n color: rgb(230,81,0) !important; }\n\n.mdl-color--orange-900 {\n background-color: rgb(230,81,0) !important; }\n\n.mdl-color-text--orange-A100 {\n color: rgb(255,209,128) !important; }\n\n.mdl-color--orange-A100 {\n background-color: rgb(255,209,128) !important; }\n\n.mdl-color-text--orange-A200 {\n color: rgb(255,171,64) !important; }\n\n.mdl-color--orange-A200 {\n background-color: rgb(255,171,64) !important; }\n\n.mdl-color-text--orange-A400 {\n color: rgb(255,145,0) !important; }\n\n.mdl-color--orange-A400 {\n background-color: rgb(255,145,0) !important; }\n\n.mdl-color-text--orange-A700 {\n color: rgb(255,109,0) !important; }\n\n.mdl-color--orange-A700 {\n background-color: rgb(255,109,0) !important; }\n\n.mdl-color-text--deep-orange {\n color: rgb(255,87,34) !important; }\n\n.mdl-color--deep-orange {\n background-color: rgb(255,87,34) !important; }\n\n.mdl-color-text--deep-orange-50 {\n color: rgb(251,233,231) !important; }\n\n.mdl-color--deep-orange-50 {\n background-color: rgb(251,233,231) !important; }\n\n.mdl-color-text--deep-orange-100 {\n color: rgb(255,204,188) !important; }\n\n.mdl-color--deep-orange-100 {\n background-color: rgb(255,204,188) !important; }\n\n.mdl-color-text--deep-orange-200 {\n color: rgb(255,171,145) !important; }\n\n.mdl-color--deep-orange-200 {\n background-color: rgb(255,171,145) !important; }\n\n.mdl-color-text--deep-orange-300 {\n color: rgb(255,138,101) !important; }\n\n.mdl-color--deep-orange-300 {\n background-color: rgb(255,138,101) !important; }\n\n.mdl-color-text--deep-orange-400 {\n color: rgb(255,112,67) !important; }\n\n.mdl-color--deep-orange-400 {\n background-color: rgb(255,112,67) !important; }\n\n.mdl-color-text--deep-orange-500 {\n color: rgb(255,87,34) !important; }\n\n.mdl-color--deep-orange-500 {\n background-color: rgb(255,87,34) !important; }\n\n.mdl-color-text--deep-orange-600 {\n color: rgb(244,81,30) !important; }\n\n.mdl-color--deep-orange-600 {\n background-color: rgb(244,81,30) !important; }\n\n.mdl-color-text--deep-orange-700 {\n color: rgb(230,74,25) !important; }\n\n.mdl-color--deep-orange-700 {\n background-color: rgb(230,74,25) !important; }\n\n.mdl-color-text--deep-orange-800 {\n color: rgb(216,67,21) !important; }\n\n.mdl-color--deep-orange-800 {\n background-color: rgb(216,67,21) !important; }\n\n.mdl-color-text--deep-orange-900 {\n color: rgb(191,54,12) !important; }\n\n.mdl-color--deep-orange-900 {\n background-color: rgb(191,54,12) !important; }\n\n.mdl-color-text--deep-orange-A100 {\n color: rgb(255,158,128) !important; }\n\n.mdl-color--deep-orange-A100 {\n background-color: rgb(255,158,128) !important; }\n\n.mdl-color-text--deep-orange-A200 {\n color: rgb(255,110,64) !important; }\n\n.mdl-color--deep-orange-A200 {\n background-color: rgb(255,110,64) !important; }\n\n.mdl-color-text--deep-orange-A400 {\n color: rgb(255,61,0) !important; }\n\n.mdl-color--deep-orange-A400 {\n background-color: rgb(255,61,0) !important; }\n\n.mdl-color-text--deep-orange-A700 {\n color: rgb(221,44,0) !important; }\n\n.mdl-color--deep-orange-A700 {\n background-color: rgb(221,44,0) !important; }\n\n.mdl-color-text--brown {\n color: rgb(121,85,72) !important; }\n\n.mdl-color--brown {\n background-color: rgb(121,85,72) !important; }\n\n.mdl-color-text--brown-50 {\n color: rgb(239,235,233) !important; }\n\n.mdl-color--brown-50 {\n background-color: rgb(239,235,233) !important; }\n\n.mdl-color-text--brown-100 {\n color: rgb(215,204,200) !important; }\n\n.mdl-color--brown-100 {\n background-color: rgb(215,204,200) !important; }\n\n.mdl-color-text--brown-200 {\n color: rgb(188,170,164) !important; }\n\n.mdl-color--brown-200 {\n background-color: rgb(188,170,164) !important; }\n\n.mdl-color-text--brown-300 {\n color: rgb(161,136,127) !important; }\n\n.mdl-color--brown-300 {\n background-color: rgb(161,136,127) !important; }\n\n.mdl-color-text--brown-400 {\n color: rgb(141,110,99) !important; }\n\n.mdl-color--brown-400 {\n background-color: rgb(141,110,99) !important; }\n\n.mdl-color-text--brown-500 {\n color: rgb(121,85,72) !important; }\n\n.mdl-color--brown-500 {\n background-color: rgb(121,85,72) !important; }\n\n.mdl-color-text--brown-600 {\n color: rgb(109,76,65) !important; }\n\n.mdl-color--brown-600 {\n background-color: rgb(109,76,65) !important; }\n\n.mdl-color-text--brown-700 {\n color: rgb(93,64,55) !important; }\n\n.mdl-color--brown-700 {\n background-color: rgb(93,64,55) !important; }\n\n.mdl-color-text--brown-800 {\n color: rgb(78,52,46) !important; }\n\n.mdl-color--brown-800 {\n background-color: rgb(78,52,46) !important; }\n\n.mdl-color-text--brown-900 {\n color: rgb(62,39,35) !important; }\n\n.mdl-color--brown-900 {\n background-color: rgb(62,39,35) !important; }\n\n.mdl-color-text--grey {\n color: rgb(158,158,158) !important; }\n\n.mdl-color--grey {\n background-color: rgb(158,158,158) !important; }\n\n.mdl-color-text--grey-50 {\n color: rgb(250,250,250) !important; }\n\n.mdl-color--grey-50 {\n background-color: rgb(250,250,250) !important; }\n\n.mdl-color-text--grey-100 {\n color: rgb(245,245,245) !important; }\n\n.mdl-color--grey-100 {\n background-color: rgb(245,245,245) !important; }\n\n.mdl-color-text--grey-200 {\n color: rgb(238,238,238) !important; }\n\n.mdl-color--grey-200 {\n background-color: rgb(238,238,238) !important; }\n\n.mdl-color-text--grey-300 {\n color: rgb(224,224,224) !important; }\n\n.mdl-color--grey-300 {\n background-color: rgb(224,224,224) !important; }\n\n.mdl-color-text--grey-400 {\n color: rgb(189,189,189) !important; }\n\n.mdl-color--grey-400 {\n background-color: rgb(189,189,189) !important; }\n\n.mdl-color-text--grey-500 {\n color: rgb(158,158,158) !important; }\n\n.mdl-color--grey-500 {\n background-color: rgb(158,158,158) !important; }\n\n.mdl-color-text--grey-600 {\n color: rgb(117,117,117) !important; }\n\n.mdl-color--grey-600 {\n background-color: rgb(117,117,117) !important; }\n\n.mdl-color-text--grey-700 {\n color: rgb(97,97,97) !important; }\n\n.mdl-color--grey-700 {\n background-color: rgb(97,97,97) !important; }\n\n.mdl-color-text--grey-800 {\n color: rgb(66,66,66) !important; }\n\n.mdl-color--grey-800 {\n background-color: rgb(66,66,66) !important; }\n\n.mdl-color-text--grey-900 {\n color: rgb(33,33,33) !important; }\n\n.mdl-color--grey-900 {\n background-color: rgb(33,33,33) !important; }\n\n.mdl-color-text--blue-grey {\n color: rgb(96,125,139) !important; }\n\n.mdl-color--blue-grey {\n background-color: rgb(96,125,139) !important; }\n\n.mdl-color-text--blue-grey-50 {\n color: rgb(236,239,241) !important; }\n\n.mdl-color--blue-grey-50 {\n background-color: rgb(236,239,241) !important; }\n\n.mdl-color-text--blue-grey-100 {\n color: rgb(207,216,220) !important; }\n\n.mdl-color--blue-grey-100 {\n background-color: rgb(207,216,220) !important; }\n\n.mdl-color-text--blue-grey-200 {\n color: rgb(176,190,197) !important; }\n\n.mdl-color--blue-grey-200 {\n background-color: rgb(176,190,197) !important; }\n\n.mdl-color-text--blue-grey-300 {\n color: rgb(144,164,174) !important; }\n\n.mdl-color--blue-grey-300 {\n background-color: rgb(144,164,174) !important; }\n\n.mdl-color-text--blue-grey-400 {\n color: rgb(120,144,156) !important; }\n\n.mdl-color--blue-grey-400 {\n background-color: rgb(120,144,156) !important; }\n\n.mdl-color-text--blue-grey-500 {\n color: rgb(96,125,139) !important; }\n\n.mdl-color--blue-grey-500 {\n background-color: rgb(96,125,139) !important; }\n\n.mdl-color-text--blue-grey-600 {\n color: rgb(84,110,122) !important; }\n\n.mdl-color--blue-grey-600 {\n background-color: rgb(84,110,122) !important; }\n\n.mdl-color-text--blue-grey-700 {\n color: rgb(69,90,100) !important; }\n\n.mdl-color--blue-grey-700 {\n background-color: rgb(69,90,100) !important; }\n\n.mdl-color-text--blue-grey-800 {\n color: rgb(55,71,79) !important; }\n\n.mdl-color--blue-grey-800 {\n background-color: rgb(55,71,79) !important; }\n\n.mdl-color-text--blue-grey-900 {\n color: rgb(38,50,56) !important; }\n\n.mdl-color--blue-grey-900 {\n background-color: rgb(38,50,56) !important; }\n\n.mdl-color--black {\n background-color: rgb(0,0,0) !important; }\n\n.mdl-color-text--black {\n color: rgb(0,0,0) !important; }\n\n.mdl-color--white {\n background-color: rgb(255,255,255) !important; }\n\n.mdl-color-text--white {\n color: rgb(255,255,255) !important; }\n\n.mdl-color--primary {\n background-color: rgb(63,81,181) !important; }\n\n.mdl-color--primary-contrast {\n background-color: rgb(255,255,255) !important; }\n\n.mdl-color--primary-dark {\n background-color: rgb(48,63,159) !important; }\n\n.mdl-color--accent {\n background-color: rgb(255,64,129) !important; }\n\n.mdl-color--accent-contrast {\n background-color: rgb(255,255,255) !important; }\n\n.mdl-color-text--primary {\n color: rgb(63,81,181) !important; }\n\n.mdl-color-text--primary-contrast {\n color: rgb(255,255,255) !important; }\n\n.mdl-color-text--primary-dark {\n color: rgb(48,63,159) !important; }\n\n.mdl-color-text--accent {\n color: rgb(255,64,129) !important; }\n\n.mdl-color-text--accent-contrast {\n color: rgb(255,255,255) !important; }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n.mdl-ripple {\n background: rgb(0,0,0);\n border-radius: 50%;\n height: 50px;\n left: 0;\n opacity: 0;\n pointer-events: none;\n position: absolute;\n top: 0;\n transform: translate(-50%, -50%);\n width: 50px;\n overflow: hidden; }\n .mdl-ripple.is-animating {\n transition: transform 0.3s cubic-bezier(0, 0, 0.2, 1), width 0.3s cubic-bezier(0, 0, 0.2, 1), height 0.3s cubic-bezier(0, 0, 0.2, 1), opacity 0.6s cubic-bezier(0, 0, 0.2, 1); }\n .mdl-ripple.is-visible {\n opacity: 0.3; }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n.mdl-animation--default {\n transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); }\n\n.mdl-animation--fast-out-slow-in {\n transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); }\n\n.mdl-animation--linear-out-slow-in {\n transition-timing-function: cubic-bezier(0, 0, 0.2, 1); }\n\n.mdl-animation--fast-out-linear-in {\n transition-timing-function: cubic-bezier(0.4, 0, 1, 1); }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n.mdl-badge {\n position: relative;\n white-space: nowrap;\n margin-right: 24px; }\n .mdl-badge:not([data-badge]) {\n margin-right: auto; }\n .mdl-badge[data-badge]:after {\n content: attr(data-badge);\n display: flex;\n flex-direction: row;\n flex-wrap: wrap;\n justify-content: center;\n align-content: center;\n align-items: center;\n position: absolute;\n top: -11px;\n right: -24px;\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-weight: 600;\n font-size: 12px;\n width: 22px;\n height: 22px;\n border-radius: 50%;\n background: rgb(255,64,129);\n color: rgb(255,255,255); }\n .mdl-button .mdl-badge[data-badge]:after {\n top: -10px;\n right: -5px; }\n .mdl-badge.mdl-badge--no-background[data-badge]:after {\n color: rgb(255,64,129);\n background: rgb(255,255,255);\n box-shadow: 0 0 1px gray; }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* Typography */\n/* Shadows */\n/* Animations */\n.mdl-button {\n background: transparent;\n border: none;\n border-radius: 2px;\n color: rgb(0,0,0);\n position: relative;\n height: 36px;\n min-width: 64px;\n padding: 0 16px;\n display: inline-block;\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 14px;\n font-weight: 500;\n text-transform: uppercase;\n line-height: 1;\n letter-spacing: 0;\n overflow: hidden;\n will-change: box-shadow, transform;\n transition: box-shadow 0.2s cubic-bezier(0.4, 0, 1, 1), background-color 0.2s cubic-bezier(0.4, 0, 0.2, 1), color 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n outline: none;\n cursor: pointer;\n text-decoration: none;\n text-align: center;\n line-height: 36px;\n vertical-align: middle; }\n .mdl-button::-moz-focus-inner {\n border: 0; }\n .mdl-button:hover {\n background-color: rgba(158,158,158, 0.20); }\n .mdl-button:focus:not(:active) {\n background-color: rgba(0,0,0, 0.12); }\n .mdl-button:active {\n background-color: rgba(158,158,158, 0.40); }\n .mdl-button.mdl-button--colored {\n color: rgb(63,81,181); }\n .mdl-button.mdl-button--colored:focus:not(:active) {\n background-color: rgba(0,0,0, 0.12); }\n\ninput.mdl-button[type=\"submit\"] {\n -webkit-appearance: none; }\n\n.mdl-button--raised {\n background: rgba(158,158,158, 0.20);\n box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12); }\n .mdl-button--raised:active {\n box-shadow: 0 4px 5px 0 rgba(0, 0, 0, 0.14), 0 1px 10px 0 rgba(0, 0, 0, 0.12), 0 2px 4px -1px rgba(0, 0, 0, 0.2);\n background-color: rgba(158,158,158, 0.40); }\n .mdl-button--raised:focus:not(:active) {\n box-shadow: 0 0 8px rgba(0, 0, 0, 0.18), 0 8px 16px rgba(0, 0, 0, 0.36);\n background-color: rgba(158,158,158, 0.40); }\n .mdl-button--raised.mdl-button--colored {\n background: rgb(63,81,181);\n color: rgb(255,255,255); }\n .mdl-button--raised.mdl-button--colored:hover {\n background-color: rgb(63,81,181); }\n .mdl-button--raised.mdl-button--colored:active {\n background-color: rgb(63,81,181); }\n .mdl-button--raised.mdl-button--colored:focus:not(:active) {\n background-color: rgb(63,81,181); }\n .mdl-button--raised.mdl-button--colored .mdl-ripple {\n background: rgb(255,255,255); }\n\n.mdl-button--fab {\n border-radius: 50%;\n font-size: 24px;\n height: 56px;\n margin: auto;\n min-width: 56px;\n width: 56px;\n padding: 0;\n overflow: hidden;\n background: rgba(158,158,158, 0.20);\n box-shadow: 0 1px 1.5px 0 rgba(0, 0, 0, 0.12), 0 1px 1px 0 rgba(0, 0, 0, 0.24);\n position: relative;\n line-height: normal; }\n .mdl-button--fab .material-icons {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-12px, -12px);\n line-height: 24px;\n width: 24px; }\n .mdl-button--fab.mdl-button--mini-fab {\n height: 40px;\n min-width: 40px;\n width: 40px; }\n .mdl-button--fab .mdl-button__ripple-container {\n border-radius: 50%;\n -webkit-mask-image: -webkit-radial-gradient(circle, white, black); }\n .mdl-button--fab:active {\n box-shadow: 0 4px 5px 0 rgba(0, 0, 0, 0.14), 0 1px 10px 0 rgba(0, 0, 0, 0.12), 0 2px 4px -1px rgba(0, 0, 0, 0.2);\n background-color: rgba(158,158,158, 0.40); }\n .mdl-button--fab:focus:not(:active) {\n box-shadow: 0 0 8px rgba(0, 0, 0, 0.18), 0 8px 16px rgba(0, 0, 0, 0.36);\n background-color: rgba(158,158,158, 0.40); }\n .mdl-button--fab.mdl-button--colored {\n background: rgb(255,64,129);\n color: rgb(255,255,255); }\n .mdl-button--fab.mdl-button--colored:hover {\n background-color: rgb(255,64,129); }\n .mdl-button--fab.mdl-button--colored:focus:not(:active) {\n background-color: rgb(255,64,129); }\n .mdl-button--fab.mdl-button--colored:active {\n background-color: rgb(255,64,129); }\n .mdl-button--fab.mdl-button--colored .mdl-ripple {\n background: rgb(255,255,255); }\n\n.mdl-button--icon {\n border-radius: 50%;\n font-size: 24px;\n height: 32px;\n margin-left: 0;\n margin-right: 0;\n min-width: 32px;\n width: 32px;\n padding: 0;\n overflow: hidden;\n color: inherit;\n line-height: normal; }\n .mdl-button--icon .material-icons {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-12px, -12px);\n line-height: 24px;\n width: 24px; }\n .mdl-button--icon.mdl-button--mini-icon {\n height: 24px;\n min-width: 24px;\n width: 24px; }\n .mdl-button--icon.mdl-button--mini-icon .material-icons {\n top: 0px;\n left: 0px; }\n .mdl-button--icon .mdl-button__ripple-container {\n border-radius: 50%;\n -webkit-mask-image: -webkit-radial-gradient(circle, white, black); }\n\n.mdl-button__ripple-container {\n display: block;\n height: 100%;\n left: 0px;\n position: absolute;\n top: 0px;\n width: 100%;\n z-index: 0;\n overflow: hidden; }\n .mdl-button[disabled] .mdl-button__ripple-container .mdl-ripple,\n .mdl-button.mdl-button--disabled .mdl-button__ripple-container .mdl-ripple {\n background-color: transparent; }\n\n.mdl-button--primary.mdl-button--primary {\n color: rgb(63,81,181); }\n .mdl-button--primary.mdl-button--primary .mdl-ripple {\n background: rgb(255,255,255); }\n .mdl-button--primary.mdl-button--primary.mdl-button--raised, .mdl-button--primary.mdl-button--primary.mdl-button--fab {\n color: rgb(255,255,255);\n background-color: rgb(63,81,181); }\n\n.mdl-button--accent.mdl-button--accent {\n color: rgb(255,64,129); }\n .mdl-button--accent.mdl-button--accent .mdl-ripple {\n background: rgb(255,255,255); }\n .mdl-button--accent.mdl-button--accent.mdl-button--raised, .mdl-button--accent.mdl-button--accent.mdl-button--fab {\n color: rgb(255,255,255);\n background-color: rgb(255,64,129); }\n\n.mdl-button[disabled][disabled], .mdl-button.mdl-button--disabled.mdl-button--disabled {\n color: rgba(0,0,0, 0.26);\n cursor: default;\n background-color: transparent; }\n\n.mdl-button--fab[disabled][disabled], .mdl-button--fab.mdl-button--disabled.mdl-button--disabled {\n background-color: rgba(0,0,0, 0.12);\n color: rgba(0,0,0, 0.26);\n box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12); }\n\n.mdl-button--raised[disabled][disabled], .mdl-button--raised.mdl-button--disabled.mdl-button--disabled {\n background-color: rgba(0,0,0, 0.12);\n color: rgba(0,0,0, 0.26);\n box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12); }\n\n.mdl-button--colored[disabled][disabled], .mdl-button--colored.mdl-button--disabled.mdl-button--disabled {\n color: rgba(0,0,0, 0.26); }\n\n.mdl-button .material-icons {\n vertical-align: middle; }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n.mdl-card {\n display: flex;\n flex-direction: column;\n font-size: 16px;\n font-weight: 400;\n min-height: 200px;\n overflow: hidden;\n width: 330px;\n z-index: 1;\n position: relative;\n background: rgb(255,255,255);\n border-radius: 2px;\n box-sizing: border-box; }\n\n.mdl-card__media {\n background-color: rgb(255,64,129);\n background-repeat: repeat;\n background-position: 50% 50%;\n background-size: cover;\n background-origin: padding-box;\n background-attachment: scroll;\n box-sizing: border-box; }\n\n.mdl-card__title {\n align-items: center;\n color: rgb(0,0,0);\n display: block;\n display: flex;\n justify-content: stretch;\n line-height: normal;\n padding: 16px 16px;\n perspective-origin: 165px 56px;\n transform-origin: 165px 56px;\n box-sizing: border-box; }\n .mdl-card__title.mdl-card--border {\n border-bottom: 1px solid rgba(0, 0, 0, 0.1); }\n\n.mdl-card__title-text {\n align-self: flex-end;\n color: inherit;\n display: block;\n display: flex;\n font-size: 24px;\n font-weight: 300;\n line-height: normal;\n overflow: hidden;\n transform-origin: 149px 48px;\n margin: 0; }\n\n.mdl-card__subtitle-text {\n font-size: 14px;\n color: rgba(0,0,0, 0.54);\n margin: 0; }\n\n.mdl-card__supporting-text {\n color: rgba(0,0,0, 0.54);\n font-size: 13px;\n line-height: 18px;\n overflow: hidden;\n padding: 16px 16px;\n width: 90%; }\n\n.mdl-card__actions {\n font-size: 16px;\n line-height: normal;\n width: 100%;\n background-color: transparent;\n padding: 8px;\n box-sizing: border-box; }\n .mdl-card__actions.mdl-card--border {\n border-top: 1px solid rgba(0, 0, 0, 0.1); }\n\n.mdl-card--expand {\n flex-grow: 1; }\n\n.mdl-card__menu {\n position: absolute;\n right: 16px;\n top: 16px; }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* Typography */\n/* Shadows */\n/* Animations */\n.mdl-checkbox {\n position: relative;\n z-index: 1;\n vertical-align: middle;\n display: inline-block;\n box-sizing: border-box;\n width: 100%;\n height: 24px;\n margin: 0;\n padding: 0; }\n .mdl-checkbox.is-upgraded {\n padding-left: 24px; }\n\n.mdl-checkbox__input {\n line-height: 24px; }\n .mdl-checkbox.is-upgraded .mdl-checkbox__input {\n position: absolute;\n width: 0;\n height: 0;\n margin: 0;\n padding: 0;\n opacity: 0;\n -ms-appearance: none;\n -moz-appearance: none;\n -webkit-appearance: none;\n appearance: none;\n border: none; }\n\n.mdl-checkbox__box-outline {\n position: absolute;\n top: 3px;\n left: 0;\n display: inline-block;\n box-sizing: border-box;\n width: 16px;\n height: 16px;\n margin: 0;\n cursor: pointer;\n overflow: hidden;\n border: 2px solid rgba(0,0,0, 0.54);\n border-radius: 2px;\n z-index: 2; }\n .mdl-checkbox.is-checked .mdl-checkbox__box-outline {\n border: 2px solid rgb(63,81,181); }\n .mdl-checkbox.is-disabled .mdl-checkbox__box-outline {\n border: 2px solid rgba(0,0,0, 0.26);\n cursor: auto; }\n\n.mdl-checkbox__focus-helper {\n position: absolute;\n top: 3px;\n left: 0;\n display: inline-block;\n box-sizing: border-box;\n width: 16px;\n height: 16px;\n border-radius: 50%;\n background-color: transparent; }\n .mdl-checkbox.is-focused .mdl-checkbox__focus-helper {\n box-shadow: 0 0 0px 8px rgba(0, 0, 0, 0.1);\n background-color: rgba(0, 0, 0, 0.1); }\n .mdl-checkbox.is-focused.is-checked .mdl-checkbox__focus-helper {\n box-shadow: 0 0 0px 8px rgba(63,81,181, 0.26);\n background-color: rgba(63,81,181, 0.26); }\n\n.mdl-checkbox__tick-outline {\n position: absolute;\n top: 0;\n left: 0;\n height: 100%;\n width: 100%;\n mask: url(\"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcKICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICB4bWxuczpjYz0iaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbnMjIgogICB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiCiAgIHhtbG5zOnN2Zz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgdmVyc2lvbj0iMS4xIgogICB2aWV3Qm94PSIwIDAgMSAxIgogICBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWluWU1pbiBtZWV0Ij4KICA8ZGVmcz4KICAgIDxjbGlwUGF0aCBpZD0iY2xpcCI+CiAgICAgIDxwYXRoCiAgICAgICAgIGQ9Ik0gMCwwIDAsMSAxLDEgMSwwIDAsMCB6IE0gMC44NTM0Mzc1LDAuMTY3MTg3NSAwLjk1OTY4NzUsMC4yNzMxMjUgMC40MjkzNzUsMC44MDM0Mzc1IDAuMzIzMTI1LDAuOTA5Njg3NSAwLjIxNzE4NzUsMC44MDM0Mzc1IDAuMDQwMzEyNSwwLjYyNjg3NSAwLjE0NjU2MjUsMC41MjA2MjUgMC4zMjMxMjUsMC42OTc1IDAuODUzNDM3NSwwLjE2NzE4NzUgeiIKICAgICAgICAgc3R5bGU9ImZpbGw6I2ZmZmZmZjtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZSIgLz4KICAgIDwvY2xpcFBhdGg+CiAgICA8bWFzayBpZD0ibWFzayIgbWFza1VuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgbWFza0NvbnRlbnRVbml0cz0ib2JqZWN0Qm91bmRpbmdCb3giPgogICAgICA8cGF0aAogICAgICAgICBkPSJNIDAsMCAwLDEgMSwxIDEsMCAwLDAgeiBNIDAuODUzNDM3NSwwLjE2NzE4NzUgMC45NTk2ODc1LDAuMjczMTI1IDAuNDI5Mzc1LDAuODAzNDM3NSAwLjMyMzEyNSwwLjkwOTY4NzUgMC4yMTcxODc1LDAuODAzNDM3NSAwLjA0MDMxMjUsMC42MjY4NzUgMC4xNDY1NjI1LDAuNTIwNjI1IDAuMzIzMTI1LDAuNjk3NSAwLjg1MzQzNzUsMC4xNjcxODc1IHoiCiAgICAgICAgIHN0eWxlPSJmaWxsOiNmZmZmZmY7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmUiIC8+CiAgICA8L21hc2s+CiAgPC9kZWZzPgogIDxyZWN0CiAgICAgd2lkdGg9IjEiCiAgICAgaGVpZ2h0PSIxIgogICAgIHg9IjAiCiAgICAgeT0iMCIKICAgICBjbGlwLXBhdGg9InVybCgjY2xpcCkiCiAgICAgc3R5bGU9ImZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZSIgLz4KPC9zdmc+Cg==\");\n background: transparent;\n transition-duration: 0.28s;\n transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n transition-property: background; }\n .mdl-checkbox.is-checked .mdl-checkbox__tick-outline {\n background: rgb(63,81,181) url(\"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcKICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICB4bWxuczpjYz0iaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbnMjIgogICB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiCiAgIHhtbG5zOnN2Zz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgdmVyc2lvbj0iMS4xIgogICB2aWV3Qm94PSIwIDAgMSAxIgogICBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWluWU1pbiBtZWV0Ij4KICA8cGF0aAogICAgIGQ9Ik0gMC4wNDAzODA1OSwwLjYyNjc3NjcgMC4xNDY0NDY2MSwwLjUyMDcxMDY4IDAuNDI5Mjg5MzIsMC44MDM1NTMzOSAwLjMyMzIyMzMsMC45MDk2MTk0MSB6IE0gMC4yMTcxNTcyOSwwLjgwMzU1MzM5IDAuODUzNTUzMzksMC4xNjcxNTcyOSAwLjk1OTYxOTQxLDAuMjczMjIzMyAwLjMyMzIyMzMsMC45MDk2MTk0MSB6IgogICAgIGlkPSJyZWN0Mzc4MCIKICAgICBzdHlsZT0iZmlsbDojZmZmZmZmO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lIiAvPgo8L3N2Zz4K\"); }\n .mdl-checkbox.is-checked.is-disabled .mdl-checkbox__tick-outline {\n background: rgba(0,0,0, 0.26) url(\"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcKICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICB4bWxuczpjYz0iaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbnMjIgogICB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiCiAgIHhtbG5zOnN2Zz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgdmVyc2lvbj0iMS4xIgogICB2aWV3Qm94PSIwIDAgMSAxIgogICBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWluWU1pbiBtZWV0Ij4KICA8cGF0aAogICAgIGQ9Ik0gMC4wNDAzODA1OSwwLjYyNjc3NjcgMC4xNDY0NDY2MSwwLjUyMDcxMDY4IDAuNDI5Mjg5MzIsMC44MDM1NTMzOSAwLjMyMzIyMzMsMC45MDk2MTk0MSB6IE0gMC4yMTcxNTcyOSwwLjgwMzU1MzM5IDAuODUzNTUzMzksMC4xNjcxNTcyOSAwLjk1OTYxOTQxLDAuMjczMjIzMyAwLjMyMzIyMzMsMC45MDk2MTk0MSB6IgogICAgIGlkPSJyZWN0Mzc4MCIKICAgICBzdHlsZT0iZmlsbDojZmZmZmZmO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lIiAvPgo8L3N2Zz4K\"); }\n\n.mdl-checkbox__label {\n position: relative;\n cursor: pointer;\n font-size: 16px;\n line-height: 24px;\n margin: 0; }\n .mdl-checkbox.is-disabled .mdl-checkbox__label {\n color: rgba(0,0,0, 0.26);\n cursor: auto; }\n\n.mdl-checkbox__ripple-container {\n position: absolute;\n z-index: 2;\n top: -6px;\n left: -10px;\n box-sizing: border-box;\n width: 36px;\n height: 36px;\n border-radius: 50%;\n cursor: pointer;\n overflow: hidden;\n -webkit-mask-image: -webkit-radial-gradient(circle, white, black); }\n .mdl-checkbox__ripple-container .mdl-ripple {\n background: rgb(63,81,181); }\n .mdl-checkbox.is-disabled .mdl-checkbox__ripple-container {\n cursor: auto; }\n .mdl-checkbox.is-disabled .mdl-checkbox__ripple-container .mdl-ripple {\n background: transparent; }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* Typography */\n/* Shadows */\n/* Animations */\n.mdl-data-table {\n position: relative;\n border: 1px solid rgba(0, 0, 0, 0.12);\n border-collapse: collapse;\n white-space: nowrap;\n font-size: 13px;\n background-color: rgb(255,255,255); }\n .mdl-data-table thead {\n padding-bottom: 3px; }\n .mdl-data-table thead .mdl-data-table__select {\n margin-top: 0; }\n .mdl-data-table tbody tr {\n position: relative;\n height: 48px;\n transition-duration: 0.28s;\n transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n transition-property: background-color; }\n .mdl-data-table tbody tr.is-selected {\n background-color: #e0e0e0; }\n .mdl-data-table tbody tr:hover {\n background-color: #eeeeee; }\n .mdl-data-table td, .mdl-data-table th {\n padding: 0 18px 0 18px;\n text-align: right; }\n .mdl-data-table td:first-of-type, .mdl-data-table th:first-of-type {\n padding-left: 24px; }\n .mdl-data-table td:last-of-type, .mdl-data-table th:last-of-type {\n padding-right: 24px; }\n .mdl-data-table td {\n position: relative;\n vertical-align: top;\n height: 48px;\n border-top: 1px solid rgba(0, 0, 0, 0.12);\n border-bottom: 1px solid rgba(0, 0, 0, 0.12);\n padding-top: 12px;\n box-sizing: border-box; }\n .mdl-data-table td .mdl-data-table__select {\n vertical-align: top;\n position: absolute;\n left: 24px; }\n .mdl-data-table th {\n position: relative;\n vertical-align: bottom;\n text-overflow: ellipsis;\n font-size: 14px;\n font-weight: bold;\n line-height: 24px;\n letter-spacing: 0;\n height: 48px;\n font-size: 12px;\n color: rgba(0, 0, 0, 0.54);\n padding-bottom: 8px;\n box-sizing: border-box; }\n .mdl-data-table th .mdl-data-table__select {\n position: absolute;\n bottom: 8px;\n left: 24px; }\n\n.mdl-data-table__select {\n width: 16px; }\n\n.mdl-data-table__cell--non-numeric.mdl-data-table__cell--non-numeric {\n text-align: left; }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* Typography */\n/* Shadows */\n/* Animations */\n.mdl-mega-footer {\n padding: 16px 40px;\n color: rgb(158,158,158);\n background-color: rgb(66,66,66); }\n\n.mdl-mega-footer--top-section:after,\n.mdl-mega-footer--middle-section:after,\n.mdl-mega-footer--bottom-section:after,\n.mdl-mega-footer__top-section:after,\n.mdl-mega-footer__middle-section:after,\n.mdl-mega-footer__bottom-section:after {\n content: '';\n display: block;\n clear: both; }\n\n.mdl-mega-footer--left-section,\n.mdl-mega-footer__left-section {\n margin-bottom: 16px; }\n\n.mdl-mega-footer--right-section,\n.mdl-mega-footer__right-section {\n margin-bottom: 16px; }\n\n.mdl-mega-footer--right-section a,\n.mdl-mega-footer__right-section a {\n display: block;\n margin-bottom: 16px;\n color: inherit;\n text-decoration: none; }\n\n@media screen and (min-width: 760px) {\n .mdl-mega-footer--left-section,\n .mdl-mega-footer__left-section {\n float: left; }\n .mdl-mega-footer--right-section,\n .mdl-mega-footer__right-section {\n float: right; }\n .mdl-mega-footer--right-section a,\n .mdl-mega-footer__right-section a {\n display: inline-block;\n margin-left: 16px;\n line-height: 36px;\n vertical-align: middle; } }\n\n.mdl-mega-footer--social-btn,\n.mdl-mega-footer__social-btn {\n width: 36px;\n height: 36px;\n padding: 0;\n margin: 0;\n background-color: rgb(158,158,158);\n border: none; }\n\n.mdl-mega-footer--drop-down-section,\n.mdl-mega-footer__drop-down-section {\n display: block;\n position: relative; }\n\n@media screen and (min-width: 760px) {\n .mdl-mega-footer--drop-down-section,\n .mdl-mega-footer__drop-down-section {\n width: 33%; }\n .mdl-mega-footer--drop-down-section:nth-child(1),\n .mdl-mega-footer--drop-down-section:nth-child(2),\n .mdl-mega-footer__drop-down-section:nth-child(1),\n .mdl-mega-footer__drop-down-section:nth-child(2) {\n float: left; }\n .mdl-mega-footer--drop-down-section:nth-child(3),\n .mdl-mega-footer__drop-down-section:nth-child(3) {\n float: right; }\n .mdl-mega-footer--drop-down-section:nth-child(3):after,\n .mdl-mega-footer__drop-down-section:nth-child(3):after {\n clear: right; }\n .mdl-mega-footer--drop-down-section:nth-child(4),\n .mdl-mega-footer__drop-down-section:nth-child(4) {\n clear: right;\n float: right; }\n .mdl-mega-footer--middle-section:after,\n .mdl-mega-footer__middle-section:after {\n content: '';\n display: block;\n clear: both; }\n .mdl-mega-footer--bottom-section,\n .mdl-mega-footer__bottom-section {\n padding-top: 0; } }\n\n@media screen and (min-width: 1024px) {\n .mdl-mega-footer--drop-down-section,\n .mdl-mega-footer--drop-down-section:nth-child(3),\n .mdl-mega-footer--drop-down-section:nth-child(4),\n .mdl-mega-footer__drop-down-section,\n .mdl-mega-footer__drop-down-section:nth-child(3),\n .mdl-mega-footer__drop-down-section:nth-child(4) {\n width: 24%;\n float: left; } }\n\n.mdl-mega-footer--heading-checkbox,\n.mdl-mega-footer__heading-checkbox {\n position: absolute;\n width: 100%;\n height: 55.8px;\n padding: 32px;\n margin: 0;\n margin-top: -16px;\n cursor: pointer;\n z-index: 1;\n opacity: 0; }\n .mdl-mega-footer--heading-checkbox + .mdl-mega-footer--heading:after,\n .mdl-mega-footer--heading-checkbox + .mdl-mega-footer__heading:after,\n .mdl-mega-footer__heading-checkbox + .mdl-mega-footer--heading:after,\n .mdl-mega-footer__heading-checkbox + .mdl-mega-footer__heading:after {\n font-family: 'Material Icons';\n content: '\\E5CE'; }\n\n.mdl-mega-footer--heading-checkbox:checked ~ .mdl-mega-footer--link-list,\n.mdl-mega-footer--heading-checkbox:checked ~ .mdl-mega-footer__link-list,\n.mdl-mega-footer--heading-checkbox:checked + .mdl-mega-footer--heading + .mdl-mega-footer--link-list,\n.mdl-mega-footer--heading-checkbox:checked + .mdl-mega-footer__heading + .mdl-mega-footer__link-list,\n.mdl-mega-footer__heading-checkbox:checked ~ .mdl-mega-footer--link-list,\n.mdl-mega-footer__heading-checkbox:checked ~ .mdl-mega-footer__link-list,\n.mdl-mega-footer__heading-checkbox:checked + .mdl-mega-footer--heading + .mdl-mega-footer--link-list,\n.mdl-mega-footer__heading-checkbox:checked + .mdl-mega-footer__heading + .mdl-mega-footer__link-list {\n display: none; }\n\n.mdl-mega-footer--heading-checkbox:checked + .mdl-mega-footer--heading:after,\n.mdl-mega-footer--heading-checkbox:checked + .mdl-mega-footer__heading:after,\n.mdl-mega-footer__heading-checkbox:checked + .mdl-mega-footer--heading:after,\n.mdl-mega-footer__heading-checkbox:checked + .mdl-mega-footer__heading:after {\n font-family: 'Material Icons';\n content: '\\E5CF'; }\n\n.mdl-mega-footer--heading,\n.mdl-mega-footer__heading {\n position: relative;\n width: 100%;\n padding-right: 39.8px;\n margin-bottom: 16px;\n box-sizing: border-box;\n font-size: 14px;\n line-height: 23.8px;\n font-weight: 500;\n white-space: nowrap;\n text-overflow: ellipsis;\n overflow: hidden;\n color: rgb(224,224,224); }\n\n.mdl-mega-footer--heading:after,\n.mdl-mega-footer__heading:after {\n content: '';\n position: absolute;\n top: 0;\n right: 0;\n display: block;\n width: 23.8px;\n height: 23.8px;\n background-size: cover; }\n\n.mdl-mega-footer--link-list,\n.mdl-mega-footer__link-list {\n list-style: none;\n margin: 0;\n padding: 0;\n margin-bottom: 32px; }\n .mdl-mega-footer--link-list:after,\n .mdl-mega-footer__link-list:after {\n clear: both;\n display: block;\n content: ''; }\n\n.mdl-mega-footer--link-list li,\n.mdl-mega-footer__link-list li {\n font-size: 14px;\n font-weight: 400;\n line-height: 24px;\n letter-spacing: 0;\n line-height: 20px; }\n\n.mdl-mega-footer--link-list a,\n.mdl-mega-footer__link-list a {\n color: inherit;\n text-decoration: none;\n white-space: nowrap; }\n\n@media screen and (min-width: 760px) {\n .mdl-mega-footer--heading-checkbox,\n .mdl-mega-footer__heading-checkbox {\n display: none; }\n .mdl-mega-footer--heading-checkbox + .mdl-mega-footer--heading:after,\n .mdl-mega-footer--heading-checkbox + .mdl-mega-footer__heading:after,\n .mdl-mega-footer__heading-checkbox + .mdl-mega-footer--heading:after,\n .mdl-mega-footer__heading-checkbox + .mdl-mega-footer__heading:after {\n background-image: none; }\n .mdl-mega-footer--heading-checkbox:checked ~ .mdl-mega-footer--link-list,\n .mdl-mega-footer--heading-checkbox:checked ~ .mdl-mega-footer__link-list,\n .mdl-mega-footer--heading-checkbox:checked + .mdl-mega-footer__heading + .mdl-mega-footer__link-list,\n .mdl-mega-footer--heading-checkbox:checked + .mdl-mega-footer--heading + .mdl-mega-footer--link-list,\n .mdl-mega-footer__heading-checkbox:checked ~ .mdl-mega-footer--link-list,\n .mdl-mega-footer__heading-checkbox:checked ~ .mdl-mega-footer__link-list,\n .mdl-mega-footer__heading-checkbox:checked + .mdl-mega-footer__heading + .mdl-mega-footer__link-list,\n .mdl-mega-footer__heading-checkbox:checked + .mdl-mega-footer--heading + .mdl-mega-footer--link-list {\n display: block; }\n .mdl-mega-footer--heading-checkbox:checked + .mdl-mega-footer--heading:after,\n .mdl-mega-footer--heading-checkbox:checked + .mdl-mega-footer__heading:after,\n .mdl-mega-footer__heading-checkbox:checked + .mdl-mega-footer--heading:after,\n .mdl-mega-footer__heading-checkbox:checked + .mdl-mega-footer__heading:after {\n content: ''; } }\n\n.mdl-mega-footer--bottom-section,\n.mdl-mega-footer__bottom-section {\n padding-top: 16px;\n margin-bottom: 16px; }\n\n.mdl-logo {\n margin-bottom: 16px;\n color: white; }\n\n.mdl-mega-footer--bottom-section .mdl-mega-footer--link-list li,\n.mdl-mega-footer__bottom-section .mdl-mega-footer__link-list li {\n float: left;\n margin-bottom: 0;\n margin-right: 16px; }\n\n@media screen and (min-width: 760px) {\n .mdl-logo {\n float: left;\n margin-bottom: 0;\n margin-right: 16px; } }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n.mdl-mini-footer {\n display: flex;\n flex-flow: row wrap;\n justify-content: space-between;\n padding: 32px 16px;\n color: rgb(158,158,158);\n background-color: rgb(66,66,66); }\n .mdl-mini-footer:after {\n content: '';\n display: block; }\n .mdl-mini-footer .mdl-logo {\n line-height: 36px; }\n\n.mdl-mini-footer--link-list,\n.mdl-mini-footer__link-list {\n display: flex;\n flex-flow: row nowrap;\n list-style: none;\n margin: 0;\n padding: 0; }\n .mdl-mini-footer--link-list li,\n .mdl-mini-footer__link-list li {\n margin-bottom: 0;\n margin-right: 16px; }\n @media screen and (min-width: 760px) {\n .mdl-mini-footer--link-list li,\n .mdl-mini-footer__link-list li {\n line-height: 36px; } }\n .mdl-mini-footer--link-list a,\n .mdl-mini-footer__link-list a {\n color: inherit;\n text-decoration: none;\n white-space: nowrap; }\n\n.mdl-mini-footer--left-section,\n.mdl-mini-footer__left-section {\n display: inline-block;\n order: 0; }\n\n.mdl-mini-footer--right-section,\n.mdl-mini-footer__right-section {\n display: inline-block;\n order: 1; }\n\n.mdl-mini-footer--social-btn,\n.mdl-mini-footer__social-btn {\n width: 36px;\n height: 36px;\n padding: 0;\n margin: 0;\n background-color: rgb(158,158,158);\n border: none; }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n.mdl-icon-toggle {\n position: relative;\n z-index: 1;\n vertical-align: middle;\n display: inline-block;\n height: 32px;\n margin: 0;\n padding: 0; }\n\n.mdl-icon-toggle__input {\n line-height: 32px; }\n .mdl-icon-toggle.is-upgraded .mdl-icon-toggle__input {\n position: absolute;\n width: 0;\n height: 0;\n margin: 0;\n padding: 0;\n opacity: 0;\n -ms-appearance: none;\n -moz-appearance: none;\n -webkit-appearance: none;\n appearance: none;\n border: none; }\n\n.mdl-icon-toggle__label {\n display: inline-block;\n position: relative;\n cursor: pointer;\n height: 32px;\n width: 32px;\n min-width: 32px;\n color: rgb(97,97,97);\n border-radius: 50%;\n padding: 0;\n margin-left: 0;\n margin-right: 0;\n text-align: center;\n background-color: transparent;\n will-change: background-color;\n transition: background-color 0.2s cubic-bezier(0.4, 0, 0.2, 1), color 0.2s cubic-bezier(0.4, 0, 0.2, 1); }\n .mdl-icon-toggle__label.material-icons {\n line-height: 32px;\n font-size: 24px; }\n .mdl-icon-toggle.is-checked .mdl-icon-toggle__label {\n color: rgb(63,81,181); }\n .mdl-icon-toggle.is-disabled .mdl-icon-toggle__label {\n color: rgba(0,0,0, 0.26);\n cursor: auto;\n transition: none; }\n .mdl-icon-toggle.is-focused .mdl-icon-toggle__label {\n background-color: rgba(0,0,0, 0.12); }\n .mdl-icon-toggle.is-focused.is-checked .mdl-icon-toggle__label {\n background-color: rgba(63,81,181, 0.26); }\n\n.mdl-icon-toggle__ripple-container {\n position: absolute;\n z-index: 2;\n top: -2px;\n left: -2px;\n box-sizing: border-box;\n width: 36px;\n height: 36px;\n border-radius: 50%;\n cursor: pointer;\n overflow: hidden;\n -webkit-mask-image: -webkit-radial-gradient(circle, white, black); }\n .mdl-icon-toggle__ripple-container .mdl-ripple {\n background: rgb(97,97,97); }\n .mdl-icon-toggle.is-disabled .mdl-icon-toggle__ripple-container {\n cursor: auto; }\n .mdl-icon-toggle.is-disabled .mdl-icon-toggle__ripple-container .mdl-ripple {\n background: transparent; }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* Typography */\n/* Shadows */\n/* Animations */\n.mdl-menu__container {\n display: block;\n margin: 0;\n padding: 0;\n border: none;\n position: absolute;\n overflow: visible;\n height: 0;\n width: 0;\n visibility: hidden;\n z-index: -1; }\n .mdl-menu__container.is-visible, .mdl-menu__container.is-animating {\n z-index: 999;\n visibility: visible; }\n\n.mdl-menu__outline {\n display: block;\n background: rgb(255,255,255);\n margin: 0;\n padding: 0;\n border: none;\n border-radius: 2px;\n position: absolute;\n top: 0;\n left: 0;\n overflow: hidden;\n opacity: 0;\n transform: scale(0);\n transform-origin: 0 0;\n box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12);\n will-change: transform;\n transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n z-index: -1; }\n .mdl-menu__container.is-visible .mdl-menu__outline {\n opacity: 1;\n transform: scale(1);\n z-index: 999; }\n .mdl-menu__outline.mdl-menu--bottom-right {\n transform-origin: 100% 0; }\n .mdl-menu__outline.mdl-menu--top-left {\n transform-origin: 0 100%; }\n .mdl-menu__outline.mdl-menu--top-right {\n transform-origin: 100% 100%; }\n\n.mdl-menu {\n position: absolute;\n list-style: none;\n top: 0;\n left: 0;\n height: auto;\n width: auto;\n min-width: 124px;\n padding: 8px 0;\n margin: 0;\n opacity: 0;\n clip: rect(0 0 0 0);\n z-index: -1; }\n .mdl-menu__container.is-visible .mdl-menu {\n opacity: 1;\n z-index: 999; }\n .mdl-menu.is-animating {\n transition: opacity 0.2s cubic-bezier(0.4, 0, 0.2, 1), clip 0.3s cubic-bezier(0.4, 0, 0.2, 1); }\n .mdl-menu.mdl-menu--bottom-right {\n left: auto;\n right: 0; }\n .mdl-menu.mdl-menu--top-left {\n top: auto;\n bottom: 0; }\n .mdl-menu.mdl-menu--top-right {\n top: auto;\n left: auto;\n bottom: 0;\n right: 0; }\n .mdl-menu.mdl-menu--unaligned {\n top: auto;\n left: auto; }\n\n.mdl-menu__item {\n display: block;\n border: none;\n color: rgba(0,0,0, 0.87);\n background-color: transparent;\n text-align: left;\n margin: 0;\n padding: 0 16px;\n outline-color: rgb(189,189,189);\n position: relative;\n overflow: hidden;\n font-size: 14px;\n font-weight: 400;\n line-height: 24px;\n letter-spacing: 0;\n text-decoration: none;\n cursor: pointer;\n height: 48px;\n line-height: 48px;\n white-space: nowrap;\n opacity: 0;\n transition: opacity 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n user-select: none; }\n .mdl-menu__container.is-visible .mdl-menu__item {\n opacity: 1; }\n .mdl-menu__item::-moz-focus-inner {\n border: 0; }\n .mdl-menu__item[disabled] {\n color: rgb(189,189,189);\n background-color: transparent;\n cursor: auto; }\n .mdl-menu__item[disabled]:hover {\n background-color: transparent; }\n .mdl-menu__item[disabled]:focus {\n background-color: transparent; }\n .mdl-menu__item[disabled] .mdl-ripple {\n background: transparent; }\n .mdl-menu__item:hover {\n background-color: rgb(238,238,238); }\n .mdl-menu__item:focus {\n outline: none;\n background-color: rgb(238,238,238); }\n .mdl-menu__item:active {\n background-color: rgb(224,224,224); }\n\n.mdl-menu__item--ripple-container {\n display: block;\n height: 100%;\n left: 0px;\n position: absolute;\n top: 0px;\n width: 100%;\n z-index: 0;\n overflow: hidden; }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n.mdl-progress {\n display: block;\n position: relative;\n height: 4px;\n width: 500px; }\n\n.mdl-progress > .bar {\n display: block;\n position: absolute;\n top: 0;\n bottom: 0;\n width: 0%;\n transition: width 0.2s cubic-bezier(0.4, 0, 0.2, 1); }\n\n.mdl-progress > .progressbar {\n background-color: rgb(63,81,181);\n z-index: 1;\n left: 0; }\n\n.mdl-progress > .bufferbar {\n background-image: linear-gradient(to right, rgba(255,255,255, 0.7), rgba(255,255,255, 0.7)), linear-gradient(to right, rgb(63,81,181), rgb(63,81,181));\n z-index: 0;\n left: 0; }\n\n.mdl-progress > .auxbar {\n right: 0; }\n\n@supports (-webkit-appearance: none) {\n .mdl-progress:not(.mdl-progress__indeterminate):not(.mdl-progress__indeterminate) > .auxbar {\n background-image: linear-gradient(to right, rgba(255,255,255, 0.7), rgba(255,255,255, 0.7)), linear-gradient(to right, rgb(63,81,181), rgb(63,81,181));\n mask: url(\"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIj8+Cjxzdmcgd2lkdGg9IjEyIiBoZWlnaHQ9IjQiIHZpZXdQb3J0PSIwIDAgMTIgNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgogIDxlbGxpcHNlIGN4PSIyIiBjeT0iMiIgcng9IjIiIHJ5PSIyIj4KICAgIDxhbmltYXRlIGF0dHJpYnV0ZU5hbWU9ImN4IiBmcm9tPSIyIiB0bz0iLTEwIiBkdXI9IjAuNnMiIHJlcGVhdENvdW50PSJpbmRlZmluaXRlIiAvPgogIDwvZWxsaXBzZT4KICA8ZWxsaXBzZSBjeD0iMTQiIGN5PSIyIiByeD0iMiIgcnk9IjIiIGNsYXNzPSJsb2FkZXIiPgogICAgPGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iY3giIGZyb209IjE0IiB0bz0iMiIgZHVyPSIwLjZzIiByZXBlYXRDb3VudD0iaW5kZWZpbml0ZSIgLz4KICA8L2VsbGlwc2U+Cjwvc3ZnPgo=\"); } }\n\n.mdl-progress:not(.mdl-progress__indeterminate) > .auxbar {\n background-image: linear-gradient(to right, rgba(255,255,255, 0.9), rgba(255,255,255, 0.9)), linear-gradient(to right, rgb(63,81,181), rgb(63,81,181)); }\n\n.mdl-progress.mdl-progress__indeterminate > .bar1 {\n background-color: rgb(63,81,181);\n animation-name: indeterminate1;\n animation-duration: 2s;\n animation-iteration-count: infinite;\n animation-timing-function: linear; }\n\n.mdl-progress.mdl-progress__indeterminate > .bar3 {\n background-image: none;\n background-color: rgb(63,81,181);\n animation-name: indeterminate2;\n animation-duration: 2s;\n animation-iteration-count: infinite;\n animation-timing-function: linear; }\n\n@keyframes indeterminate1 {\n 0% {\n left: 0%;\n width: 0%; }\n 50% {\n left: 25%;\n width: 75%; }\n 75% {\n left: 100%;\n width: 0%; } }\n\n@keyframes indeterminate2 {\n 0% {\n left: 0%;\n width: 0%; }\n 50% {\n left: 0%;\n width: 0%; }\n 75% {\n left: 0%;\n width: 25%; }\n 100% {\n left: 100%;\n width: 0%; } }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* Typography */\n/* Shadows */\n/* Animations */\n.mdl-navigation {\n display: flex;\n flex-wrap: nowrap;\n box-sizing: border-box; }\n\n.mdl-navigation__link {\n color: rgb(66,66,66);\n text-decoration: none;\n font-weight: 500;\n font-size: 13px;\n margin: 0; }\n\n.mdl-layout {\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: column;\n overflow-y: auto;\n overflow-x: hidden;\n position: relative;\n -webkit-overflow-scrolling: touch; }\n\n.mdl-layout.is-small-screen .mdl-layout--large-screen-only {\n display: none; }\n\n.mdl-layout:not(.is-small-screen) .mdl-layout--small-screen-only {\n display: none; }\n\n.mdl-layout__container {\n position: absolute;\n width: 100%;\n height: 100%; }\n\n.mdl-layout__title,\n.mdl-layout-title {\n display: block;\n position: relative;\n font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\n font-size: 20px;\n font-weight: 500;\n line-height: 1;\n letter-spacing: 0.02em;\n font-weight: 400;\n box-sizing: border-box; }\n\n.mdl-layout-spacer {\n flex-grow: 1; }\n\n.mdl-layout__drawer {\n display: flex;\n flex-direction: column;\n flex-wrap: nowrap;\n width: 240px;\n height: 100%;\n max-height: 100%;\n position: absolute;\n top: 0;\n left: 0;\n box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12);\n box-sizing: border-box;\n border-right: 1px solid rgb(224,224,224);\n background: rgb(250,250,250);\n transform: translateX(-250px);\n transform-style: preserve-3d;\n will-change: transform;\n transition-duration: 0.2s;\n transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n transition-property: transform;\n color: rgb(66,66,66);\n overflow: visible;\n overflow-y: auto;\n z-index: 5; }\n .mdl-layout__drawer.is-visible {\n transform: translateX(0); }\n .mdl-layout__drawer.is-visible ~ .mdl-layout__content.mdl-layout__content {\n overflow: hidden; }\n .mdl-layout__drawer > * {\n flex-shrink: 0; }\n .mdl-layout__drawer > .mdl-layout__title,\n .mdl-layout__drawer > .mdl-layout-title {\n line-height: 64px;\n padding-left: 40px; }\n @media screen and (max-width: 1024px) {\n .mdl-layout__drawer > .mdl-layout__title,\n .mdl-layout__drawer > .mdl-layout-title {\n line-height: 56px;\n padding-left: 16px; } }\n .mdl-layout__drawer .mdl-navigation {\n flex-direction: column;\n align-items: stretch;\n padding-top: 16px; }\n .mdl-layout__drawer .mdl-navigation .mdl-navigation__link {\n display: block;\n flex-shrink: 0;\n padding: 16px 40px;\n margin: 0;\n color: #757575; }\n @media screen and (max-width: 1024px) {\n .mdl-layout__drawer .mdl-navigation .mdl-navigation__link {\n padding: 16px 16px; } }\n .mdl-layout__drawer .mdl-navigation .mdl-navigation__link:hover {\n background-color: rgb(224,224,224); }\n .mdl-layout__drawer .mdl-navigation .mdl-navigation__link--current {\n background-color: rgb(0,0,0);\n color: rgb(224,224,224); }\n @media screen and (min-width: 1025px) {\n .mdl-layout--fixed-drawer > .mdl-layout__drawer {\n transform: translateX(0); } }\n\n.mdl-layout__drawer-button {\n display: block;\n position: absolute;\n height: 48px;\n width: 48px;\n border: 0;\n flex-shrink: 0;\n overflow: hidden;\n text-align: center;\n cursor: pointer;\n font-size: 26px;\n line-height: 50px;\n font-family: Helvetica, Arial, sans-serif;\n margin: 10px 12px;\n top: 0;\n left: 0;\n color: rgb(255,255,255);\n z-index: 4; }\n .mdl-layout__header .mdl-layout__drawer-button {\n position: absolute;\n color: rgb(255,255,255);\n background-color: inherit; }\n @media screen and (max-width: 1024px) {\n .mdl-layout__header .mdl-layout__drawer-button {\n margin: 4px; } }\n @media screen and (max-width: 1024px) {\n .mdl-layout__drawer-button {\n margin: 4px;\n color: rgba(0, 0, 0, 0.5); } }\n @media screen and (min-width: 1025px) {\n .mdl-layout--fixed-drawer > .mdl-layout__drawer-button {\n display: none; } }\n\n.mdl-layout__header {\n display: flex;\n flex-direction: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n box-sizing: border-box;\n flex-shrink: 0;\n width: 100%;\n margin: 0;\n padding: 0;\n border: none;\n min-height: 64px;\n max-height: 1000px;\n z-index: 3;\n background-color: rgb(63,81,181);\n color: rgb(255,255,255);\n box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12);\n transition-duration: 0.2s;\n transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n transition-property: max-height, box-shadow; }\n @media screen and (max-width: 1024px) {\n .mdl-layout__header {\n min-height: 56px; } }\n .mdl-layout--fixed-drawer.is-upgraded:not(.is-small-screen) > .mdl-layout__header {\n margin-left: 240px;\n width: calc(100% - 240px); }\n @media screen and (min-width: 1025px) {\n .mdl-layout--fixed-drawer > .mdl-layout__header .mdl-layout__header-row {\n padding-left: 40px; } }\n .mdl-layout__header > .mdl-layout-icon {\n position: absolute;\n left: 40px;\n top: 16px;\n height: 32px;\n width: 32px;\n overflow: hidden;\n z-index: 3;\n display: block; }\n @media screen and (max-width: 1024px) {\n .mdl-layout__header > .mdl-layout-icon {\n left: 16px;\n top: 12px; } }\n .mdl-layout.has-drawer .mdl-layout__header > .mdl-layout-icon {\n display: none; }\n .mdl-layout__header.is-compact {\n max-height: 64px; }\n @media screen and (max-width: 1024px) {\n .mdl-layout__header.is-compact {\n max-height: 56px; } }\n .mdl-layout__header.is-compact.has-tabs {\n height: 112px; }\n @media screen and (max-width: 1024px) {\n .mdl-layout__header.is-compact.has-tabs {\n min-height: 104px; } }\n @media screen and (max-width: 1024px) {\n .mdl-layout__header {\n display: none; }\n .mdl-layout--fixed-header > .mdl-layout__header {\n display: flex; } }\n\n.mdl-layout__header--transparent.mdl-layout__header--transparent {\n background-color: transparent;\n box-shadow: none; }\n\n.mdl-layout__header--seamed {\n box-shadow: none; }\n\n.mdl-layout__header--scroll {\n box-shadow: none; }\n\n.mdl-layout__header--waterfall {\n box-shadow: none;\n overflow: hidden; }\n .mdl-layout__header--waterfall.is-casting-shadow {\n box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12); }\n\n.mdl-layout__header-row {\n display: flex;\n flex-direction: row;\n flex-wrap: nowrap;\n flex-shrink: 0;\n box-sizing: border-box;\n align-self: stretch;\n align-items: center;\n height: 64px;\n margin: 0;\n padding: 0 40px 0 80px; }\n @media screen and (max-width: 1024px) {\n .mdl-layout__header-row {\n height: 56px;\n padding: 0 16px 0 72px; } }\n .mdl-layout__header-row > * {\n flex-shrink: 0; }\n .mdl-layout__header--scroll .mdl-layout__header-row {\n width: 100%; }\n .mdl-layout__header-row .mdl-navigation {\n margin: 0;\n padding: 0;\n height: 64px;\n flex-direction: row;\n align-items: center; }\n @media screen and (max-width: 1024px) {\n .mdl-layout__header-row .mdl-navigation {\n height: 56px; } }\n .mdl-layout__header-row .mdl-navigation__link {\n display: block;\n color: rgb(255,255,255);\n line-height: 64px;\n padding: 0 24px; }\n @media screen and (max-width: 1024px) {\n .mdl-layout__header-row .mdl-navigation__link {\n line-height: 56px;\n padding: 0 16px; } }\n\n.mdl-layout__obfuscator {\n background-color: transparent;\n position: absolute;\n top: 0;\n left: 0;\n height: 100%;\n width: 100%;\n z-index: 4;\n visibility: hidden;\n transition-property: background-color;\n transition-duration: 0.2s;\n transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); }\n .mdl-layout__obfuscator.is-visible {\n background-color: rgba(0, 0, 0, 0.5);\n visibility: visible; }\n\n.mdl-layout__content {\n -ms-flex: 0 1 auto;\n display: inline-block;\n overflow-y: auto;\n overflow-x: hidden;\n flex-grow: 1;\n z-index: 1;\n -webkit-overflow-scrolling: touch; }\n .mdl-layout--fixed-drawer > .mdl-layout__content {\n margin-left: 240px; }\n .mdl-layout__container.has-scrolling-header .mdl-layout__content {\n overflow: visible; }\n @media screen and (max-width: 1024px) {\n .mdl-layout--fixed-drawer > .mdl-layout__content {\n margin-left: 0; }\n .mdl-layout__container.has-scrolling-header .mdl-layout__content {\n overflow-y: auto;\n overflow-x: hidden; } }\n\n.mdl-layout__tab-bar {\n height: 96px;\n margin: 0;\n width: calc(100% - 112px);\n padding: 0 0 0 56px;\n display: flex;\n background-color: rgb(63,81,181);\n overflow-y: hidden;\n overflow-x: scroll; }\n .mdl-layout__tab-bar::-webkit-scrollbar {\n display: none; }\n @media screen and (max-width: 1024px) {\n .mdl-layout__tab-bar {\n width: calc(100% - 60px);\n padding: 0 0 0 60px; } }\n .mdl-layout--fixed-tabs .mdl-layout__tab-bar {\n padding: 0;\n overflow: hidden;\n width: 100%; }\n\n.mdl-layout__tab-bar-container {\n position: relative;\n height: 48px;\n width: 100%;\n border: none;\n margin: 0;\n z-index: 2;\n flex-grow: 0;\n flex-shrink: 0;\n overflow: hidden; }\n .mdl-layout__container > .mdl-layout__tab-bar-container {\n position: absolute;\n top: 0;\n left: 0; }\n\n.mdl-layout__tab-bar-button {\n display: inline-block;\n position: absolute;\n top: 0;\n height: 48px;\n width: 56px;\n z-index: 4;\n text-align: center;\n background-color: rgb(63,81,181);\n color: transparent;\n cursor: pointer;\n user-select: none; }\n @media screen and (max-width: 1024px) {\n .mdl-layout__tab-bar-button {\n display: none;\n width: 60px; } }\n .mdl-layout--fixed-tabs .mdl-layout__tab-bar-button {\n display: none; }\n .mdl-layout__tab-bar-button .material-icons {\n line-height: 48px; }\n .mdl-layout__tab-bar-button.is-active {\n color: rgb(255,255,255); }\n\n.mdl-layout__tab-bar-left-button {\n left: 0; }\n\n.mdl-layout__tab-bar-right-button {\n right: 0; }\n\n.mdl-layout__tab {\n margin: 0;\n border: none;\n padding: 0 24px 0 24px;\n float: left;\n position: relative;\n display: block;\n flex-grow: 0;\n flex-shrink: 0;\n text-decoration: none;\n height: 48px;\n line-height: 48px;\n text-align: center;\n font-weight: 500;\n font-size: 14px;\n text-transform: uppercase;\n color: rgba(255,255,255, 0.6);\n overflow: hidden; }\n @media screen and (max-width: 1024px) {\n .mdl-layout__tab {\n padding: 0 12px 0 12px; } }\n .mdl-layout--fixed-tabs .mdl-layout__tab {\n float: none;\n flex-grow: 1;\n padding: 0; }\n .mdl-layout.is-upgraded .mdl-layout__tab.is-active {\n color: rgb(255,255,255); }\n .mdl-layout.is-upgraded .mdl-layout__tab.is-active::after {\n height: 2px;\n width: 100%;\n display: block;\n content: \" \";\n bottom: 0;\n left: 0;\n position: absolute;\n background: rgb(255,64,129);\n animation: border-expand 0.2s cubic-bezier(0.4, 0, 0.4, 1) 0.01s alternate forwards;\n transition: all 1s cubic-bezier(0.4, 0, 1, 1); }\n .mdl-layout__tab .mdl-layout__tab-ripple-container {\n display: block;\n position: absolute;\n height: 100%;\n width: 100%;\n left: 0;\n top: 0;\n z-index: 1;\n overflow: hidden; }\n .mdl-layout__tab .mdl-layout__tab-ripple-container .mdl-ripple {\n background-color: rgb(255,255,255); }\n\n.mdl-layout__tab-panel {\n display: block; }\n .mdl-layout.is-upgraded .mdl-layout__tab-panel {\n display: none; }\n .mdl-layout.is-upgraded .mdl-layout__tab-panel.is-active {\n display: block; }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* Typography */\n/* Shadows */\n/* Animations */\n.mdl-radio {\n position: relative;\n font-size: 16px;\n line-height: 24px;\n display: inline-block;\n box-sizing: border-box;\n margin: 0;\n padding-left: 0; }\n .mdl-radio.is-upgraded {\n padding-left: 24px; }\n\n.mdl-radio__button {\n line-height: 24px; }\n .mdl-radio.is-upgraded .mdl-radio__button {\n position: absolute;\n width: 0;\n height: 0;\n margin: 0;\n padding: 0;\n opacity: 0;\n -ms-appearance: none;\n -moz-appearance: none;\n -webkit-appearance: none;\n appearance: none;\n border: none; }\n\n.mdl-radio__outer-circle {\n position: absolute;\n top: 4px;\n left: 0;\n display: inline-block;\n box-sizing: border-box;\n width: 16px;\n height: 16px;\n margin: 0;\n cursor: pointer;\n border: 2px solid rgba(0,0,0, 0.54);\n border-radius: 50%;\n z-index: 2; }\n .mdl-radio.is-checked .mdl-radio__outer-circle {\n border: 2px solid rgb(63,81,181); }\n .mdl-radio.is-disabled .mdl-radio__outer-circle {\n border: 2px solid rgba(0,0,0, 0.26);\n cursor: auto; }\n\n.mdl-radio__inner-circle {\n position: absolute;\n z-index: 1;\n margin: 0;\n top: 8px;\n left: 4px;\n box-sizing: border-box;\n width: 8px;\n height: 8px;\n cursor: pointer;\n transition-duration: 0.28s;\n transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n transition-property: transform;\n transform: scale3d(0, 0, 0);\n border-radius: 50%;\n background: rgb(63,81,181); }\n .mdl-radio.is-checked .mdl-radio__inner-circle {\n transform: scale3d(1, 1, 1); }\n .mdl-radio.is-disabled .mdl-radio__inner-circle {\n background: rgba(0,0,0, 0.26);\n cursor: auto; }\n .mdl-radio.is-focused .mdl-radio__inner-circle {\n box-shadow: 0 0 0px 10px rgba(0, 0, 0, 0.1); }\n\n.mdl-radio__label {\n cursor: pointer; }\n .mdl-radio.is-disabled .mdl-radio__label {\n color: rgba(0,0,0, 0.26);\n cursor: auto; }\n\n.mdl-radio__ripple-container {\n position: absolute;\n z-index: 2;\n top: -9px;\n left: -13px;\n box-sizing: border-box;\n width: 42px;\n height: 42px;\n border-radius: 50%;\n cursor: pointer;\n overflow: hidden;\n -webkit-mask-image: -webkit-radial-gradient(circle, white, black); }\n .mdl-radio__ripple-container .mdl-ripple {\n background: rgb(63,81,181); }\n .mdl-radio.is-disabled .mdl-radio__ripple-container {\n cursor: auto; }\n .mdl-radio.is-disabled .mdl-radio__ripple-container .mdl-ripple {\n background: transparent; }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n_:-ms-input-placeholder, :root .mdl-slider.mdl-slider.is-upgraded {\n -ms-appearance: none;\n height: 32px;\n margin: 0; }\n\n.mdl-slider {\n width: calc(100% - 40px);\n margin: 0 20px; }\n .mdl-slider.is-upgraded {\n -webkit-appearance: none;\n -moz-appearance: none;\n appearance: none;\n height: 2px;\n background: transparent;\n -webkit-user-select: none;\n -moz-user-select: none;\n user-select: none;\n outline: 0;\n padding: 0;\n color: rgb(63,81,181);\n align-self: center;\n z-index: 1;\n cursor: pointer;\n /**************************** Tracks ****************************/\n /**************************** Thumbs ****************************/\n /**************************** 0-value ****************************/\n /**************************** Disabled ****************************/ }\n .mdl-slider.is-upgraded::-moz-focus-outer {\n border: 0; }\n .mdl-slider.is-upgraded::-ms-tooltip {\n display: none; }\n .mdl-slider.is-upgraded::-webkit-slider-runnable-track {\n background: transparent; }\n .mdl-slider.is-upgraded::-moz-range-track {\n background: transparent;\n border: none; }\n .mdl-slider.is-upgraded::-ms-track {\n background: none;\n color: transparent;\n height: 2px;\n width: 100%;\n border: none; }\n .mdl-slider.is-upgraded::-ms-fill-lower {\n padding: 0;\n background: linear-gradient(to right, transparent, transparent 16px, rgb(63,81,181) 16px, rgb(63,81,181) 0); }\n .mdl-slider.is-upgraded::-ms-fill-upper {\n padding: 0;\n background: linear-gradient(to left, transparent, transparent 16px, rgba(0,0,0, 0.26) 16px, rgba(0,0,0, 0.26) 0); }\n .mdl-slider.is-upgraded::-webkit-slider-thumb {\n -webkit-appearance: none;\n width: 12px;\n height: 12px;\n box-sizing: border-box;\n border-radius: 50%;\n background: rgb(63,81,181);\n border: none;\n transition: transform 0.18s cubic-bezier(0.4, 0, 0.2, 1), border 0.18s cubic-bezier(0.4, 0, 0.2, 1), box-shadow 0.18s cubic-bezier(0.4, 0, 0.2, 1), background 0.28s cubic-bezier(0.4, 0, 0.2, 1); }\n .mdl-slider.is-upgraded::-moz-range-thumb {\n -moz-appearance: none;\n width: 12px;\n height: 12px;\n box-sizing: border-box;\n border-radius: 50%;\n background-image: none;\n background: rgb(63,81,181);\n border: none; }\n .mdl-slider.is-upgraded:focus:not(:active)::-webkit-slider-thumb {\n box-shadow: 0 0 0 10px rgba(63,81,181, 0.26); }\n .mdl-slider.is-upgraded:focus:not(:active)::-moz-range-thumb {\n box-shadow: 0 0 0 10px rgba(63,81,181, 0.26); }\n .mdl-slider.is-upgraded:active::-webkit-slider-thumb {\n background-image: none;\n background: rgb(63,81,181);\n transform: scale(1.5); }\n .mdl-slider.is-upgraded:active::-moz-range-thumb {\n background-image: none;\n background: rgb(63,81,181);\n transform: scale(1.5); }\n .mdl-slider.is-upgraded::-ms-thumb {\n width: 32px;\n height: 32px;\n border: none;\n border-radius: 50%;\n background: rgb(63,81,181);\n transform: scale(0.375);\n transition: transform 0.18s cubic-bezier(0.4, 0, 0.2, 1), background 0.28s cubic-bezier(0.4, 0, 0.2, 1); }\n .mdl-slider.is-upgraded:focus:not(:active)::-ms-thumb {\n background: radial-gradient(circle closest-side, rgb(63,81,181) 0%, rgb(63,81,181) 37.5%, rgba(63,81,181, 0.26) 37.5%, rgba(63,81,181, 0.26) 100%);\n transform: scale(1); }\n .mdl-slider.is-upgraded:active::-ms-thumb {\n background: rgb(63,81,181);\n transform: scale(0.5625); }\n .mdl-slider.is-upgraded.is-lowest-value::-webkit-slider-thumb {\n border: 2px solid rgba(0,0,0, 0.26);\n background: transparent; }\n .mdl-slider.is-upgraded.is-lowest-value::-moz-range-thumb {\n border: 2px solid rgba(0,0,0, 0.26);\n background: transparent; }\n .mdl-slider.is-upgraded.is-lowest-value +\n.mdl-slider__background-flex > .mdl-slider__background-upper {\n left: 6px; }\n .mdl-slider.is-upgraded.is-lowest-value:focus:not(:active)::-webkit-slider-thumb {\n box-shadow: 0 0 0 10px rgba(0,0,0, 0.12);\n background: rgba(0,0,0, 0.12); }\n .mdl-slider.is-upgraded.is-lowest-value:focus:not(:active)::-moz-range-thumb {\n box-shadow: 0 0 0 10px rgba(0,0,0, 0.12);\n background: rgba(0,0,0, 0.12); }\n .mdl-slider.is-upgraded.is-lowest-value:active::-webkit-slider-thumb {\n border: 1.6px solid rgba(0,0,0, 0.26);\n transform: scale(1.5); }\n .mdl-slider.is-upgraded.is-lowest-value:active +\n.mdl-slider__background-flex > .mdl-slider__background-upper {\n left: 9px; }\n .mdl-slider.is-upgraded.is-lowest-value:active::-moz-range-thumb {\n border: 1.5px solid rgba(0,0,0, 0.26);\n transform: scale(1.5); }\n .mdl-slider.is-upgraded.is-lowest-value::-ms-thumb {\n background: radial-gradient(circle closest-side, transparent 0%, transparent 66.67%, rgba(0,0,0, 0.26) 66.67%, rgba(0,0,0, 0.26) 100%); }\n .mdl-slider.is-upgraded.is-lowest-value:focus:not(:active)::-ms-thumb {\n background: radial-gradient(circle closest-side, rgba(0,0,0, 0.12) 0%, rgba(0,0,0, 0.12) 25%, rgba(0,0,0, 0.26) 25%, rgba(0,0,0, 0.26) 37.5%, rgba(0,0,0, 0.12) 37.5%, rgba(0,0,0, 0.12) 100%);\n transform: scale(1); }\n .mdl-slider.is-upgraded.is-lowest-value:active::-ms-thumb {\n transform: scale(0.5625);\n background: radial-gradient(circle closest-side, transparent 0%, transparent 77.78%, rgba(0,0,0, 0.26) 77.78%, rgba(0,0,0, 0.26) 100%); }\n .mdl-slider.is-upgraded.is-lowest-value::-ms-fill-lower {\n background: transparent; }\n .mdl-slider.is-upgraded.is-lowest-value::-ms-fill-upper {\n margin-left: 6px; }\n .mdl-slider.is-upgraded.is-lowest-value:active::-ms-fill-upper {\n margin-left: 9px; }\n .mdl-slider.is-upgraded:disabled:focus::-webkit-slider-thumb, .mdl-slider.is-upgraded:disabled:active::-webkit-slider-thumb, .mdl-slider.is-upgraded:disabled::-webkit-slider-thumb {\n transform: scale(0.667);\n background: rgba(0,0,0, 0.26); }\n .mdl-slider.is-upgraded:disabled:focus::-moz-range-thumb, .mdl-slider.is-upgraded:disabled:active::-moz-range-thumb, .mdl-slider.is-upgraded:disabled::-moz-range-thumb {\n transform: scale(0.667);\n background: rgba(0,0,0, 0.26); }\n .mdl-slider.is-upgraded:disabled +\n.mdl-slider__background-flex > .mdl-slider__background-lower {\n background-color: rgba(0,0,0, 0.26);\n left: -6px; }\n .mdl-slider.is-upgraded:disabled +\n.mdl-slider__background-flex > .mdl-slider__background-upper {\n left: 6px; }\n .mdl-slider.is-upgraded.is-lowest-value:disabled:focus::-webkit-slider-thumb, .mdl-slider.is-upgraded.is-lowest-value:disabled:active::-webkit-slider-thumb, .mdl-slider.is-upgraded.is-lowest-value:disabled::-webkit-slider-thumb {\n border: 3px solid rgba(0,0,0, 0.26);\n background: transparent;\n transform: scale(0.667); }\n .mdl-slider.is-upgraded.is-lowest-value:disabled:focus::-moz-range-thumb, .mdl-slider.is-upgraded.is-lowest-value:disabled:active::-moz-range-thumb, .mdl-slider.is-upgraded.is-lowest-value:disabled::-moz-range-thumb {\n border: 3px solid rgba(0,0,0, 0.26);\n background: transparent;\n transform: scale(0.667); }\n .mdl-slider.is-upgraded.is-lowest-value:disabled:active +\n.mdl-slider__background-flex > .mdl-slider__background-upper {\n left: 6px; }\n .mdl-slider.is-upgraded:disabled:focus::-ms-thumb, .mdl-slider.is-upgraded:disabled:active::-ms-thumb, .mdl-slider.is-upgraded:disabled::-ms-thumb {\n transform: scale(0.25);\n background: rgba(0,0,0, 0.26); }\n .mdl-slider.is-upgraded.is-lowest-value:disabled:focus::-ms-thumb, .mdl-slider.is-upgraded.is-lowest-value:disabled:active::-ms-thumb, .mdl-slider.is-upgraded.is-lowest-value:disabled::-ms-thumb {\n transform: scale(0.25);\n background: radial-gradient(circle closest-side, transparent 0%, transparent 50%, rgba(0,0,0, 0.26) 50%, rgba(0,0,0, 0.26) 100%); }\n .mdl-slider.is-upgraded:disabled::-ms-fill-lower {\n margin-right: 6px;\n background: linear-gradient(to right, transparent, transparent 25px, rgba(0,0,0, 0.26) 25px, rgba(0,0,0, 0.26) 0); }\n .mdl-slider.is-upgraded:disabled::-ms-fill-upper {\n margin-left: 6px; }\n .mdl-slider.is-upgraded.is-lowest-value:disabled:active::-ms-fill-upper {\n margin-left: 6px; }\n\n.mdl-slider__ie-container {\n height: 18px;\n overflow: visible;\n border: none;\n margin: none;\n padding: none; }\n\n.mdl-slider__container {\n height: 18px;\n position: relative;\n background: none;\n display: flex;\n flex-direction: row; }\n\n.mdl-slider__background-flex {\n background: transparent;\n position: absolute;\n height: 2px;\n width: calc(100% - 52px);\n top: 50%;\n left: 0;\n margin: 0 26px;\n display: flex;\n overflow: hidden;\n border: 0;\n padding: 0;\n transform: translate(0, -1px); }\n\n.mdl-slider__background-lower {\n background: rgb(63,81,181);\n flex: 0;\n position: relative;\n border: 0;\n padding: 0; }\n\n.mdl-slider__background-upper {\n background: rgba(0,0,0, 0.26);\n flex: 0;\n position: relative;\n border: 0;\n padding: 0;\n transition: left 0.18s cubic-bezier(0.4, 0, 0.2, 1); }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n.mdl-spinner {\n display: inline-block;\n position: relative;\n width: 28px;\n height: 28px; }\n .mdl-spinner:not(.is-upgraded).is-active:after {\n content: \"Loading...\"; }\n .mdl-spinner.is-upgraded.is-active {\n animation: mdl-spinner__container-rotate 1568.23529412ms linear infinite; }\n\n@keyframes mdl-spinner__container-rotate {\n to {\n transform: rotate(360deg); } }\n\n.mdl-spinner__layer {\n position: absolute;\n width: 100%;\n height: 100%;\n opacity: 0; }\n\n.mdl-spinner__layer-1 {\n border-color: rgb(66,165,245); }\n .mdl-spinner--single-color .mdl-spinner__layer-1 {\n border-color: rgb(63,81,181); }\n .mdl-spinner.is-active .mdl-spinner__layer-1 {\n animation: mdl-spinner__fill-unfill-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both, mdl-spinner__layer-1-fade-in-out 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both; }\n\n.mdl-spinner__layer-2 {\n border-color: rgb(244,67,54); }\n .mdl-spinner--single-color .mdl-spinner__layer-2 {\n border-color: rgb(63,81,181); }\n .mdl-spinner.is-active .mdl-spinner__layer-2 {\n animation: mdl-spinner__fill-unfill-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both, mdl-spinner__layer-2-fade-in-out 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both; }\n\n.mdl-spinner__layer-3 {\n border-color: rgb(253,216,53); }\n .mdl-spinner--single-color .mdl-spinner__layer-3 {\n border-color: rgb(63,81,181); }\n .mdl-spinner.is-active .mdl-spinner__layer-3 {\n animation: mdl-spinner__fill-unfill-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both, mdl-spinner__layer-3-fade-in-out 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both; }\n\n.mdl-spinner__layer-4 {\n border-color: rgb(76,175,80); }\n .mdl-spinner--single-color .mdl-spinner__layer-4 {\n border-color: rgb(63,81,181); }\n .mdl-spinner.is-active .mdl-spinner__layer-4 {\n animation: mdl-spinner__fill-unfill-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both, mdl-spinner__layer-4-fade-in-out 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both; }\n\n@keyframes mdl-spinner__fill-unfill-rotate {\n 12.5% {\n transform: rotate(135deg); }\n 25% {\n transform: rotate(270deg); }\n 37.5% {\n transform: rotate(405deg); }\n 50% {\n transform: rotate(540deg); }\n 62.5% {\n transform: rotate(675deg); }\n 75% {\n transform: rotate(810deg); }\n 87.5% {\n transform: rotate(945deg); }\n to {\n transform: rotate(1080deg); } }\n\n/**\n* HACK: Even though the intention is to have the current .mdl-spinner__layer-N\n* at `opacity: 1`, we set it to `opacity: 0.99` instead since this forces Chrome\n* to do proper subpixel rendering for the elements being animated. This is\n* especially visible in Chrome 39 on Ubuntu 14.04. See:\n*\n* - https://github.com/Polymer/paper-spinner/issues/9\n* - https://code.google.com/p/chromium/issues/detail?id=436255\n*/\n@keyframes mdl-spinner__layer-1-fade-in-out {\n from {\n opacity: 0.99; }\n 25% {\n opacity: 0.99; }\n 26% {\n opacity: 0; }\n 89% {\n opacity: 0; }\n 90% {\n opacity: 0.99; }\n 100% {\n opacity: 0.99; } }\n\n@keyframes mdl-spinner__layer-2-fade-in-out {\n from {\n opacity: 0; }\n 15% {\n opacity: 0; }\n 25% {\n opacity: 0.99; }\n 50% {\n opacity: 0.99; }\n 51% {\n opacity: 0; } }\n\n@keyframes mdl-spinner__layer-3-fade-in-out {\n from {\n opacity: 0; }\n 40% {\n opacity: 0; }\n 50% {\n opacity: 0.99; }\n 75% {\n opacity: 0.99; }\n 76% {\n opacity: 0; } }\n\n@keyframes mdl-spinner__layer-4-fade-in-out {\n from {\n opacity: 0; }\n 65% {\n opacity: 0; }\n 75% {\n opacity: 0.99; }\n 90% {\n opacity: 0.99; }\n 100% {\n opacity: 0; } }\n\n/**\n* Patch the gap that appear between the two adjacent\n* div.mdl-spinner__circle-clipper while the spinner is rotating\n* (appears on Chrome 38, Safari 7.1, and IE 11).\n*\n* Update: the gap no longer appears on Chrome when .mdl-spinner__layer-N's\n* opacity is 0.99, but still does on Safari and IE.\n*/\n.mdl-spinner__gap-patch {\n position: absolute;\n box-sizing: border-box;\n top: 0;\n left: 45%;\n width: 10%;\n height: 100%;\n overflow: hidden;\n border-color: inherit; }\n .mdl-spinner__gap-patch .mdl-spinner__circle {\n width: 1000%;\n left: -450%; }\n\n.mdl-spinner__circle-clipper {\n display: inline-block;\n position: relative;\n width: 50%;\n height: 100%;\n overflow: hidden;\n border-color: inherit; }\n .mdl-spinner__circle-clipper .mdl-spinner__circle {\n width: 200%; }\n\n.mdl-spinner__circle {\n box-sizing: border-box;\n height: 100%;\n border-width: 3px;\n border-style: solid;\n border-color: inherit;\n border-bottom-color: transparent !important;\n border-radius: 50%;\n animation: none;\n position: absolute;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0; }\n .mdl-spinner__left .mdl-spinner__circle {\n border-right-color: transparent !important;\n transform: rotate(129deg); }\n .mdl-spinner.is-active .mdl-spinner__left .mdl-spinner__circle {\n animation: mdl-spinner__left-spin 1333ms cubic-bezier(0.4, 0, 0.2, 1) infinite both; }\n .mdl-spinner__right .mdl-spinner__circle {\n left: -100%;\n border-left-color: transparent !important;\n transform: rotate(-129deg); }\n .mdl-spinner.is-active .mdl-spinner__right .mdl-spinner__circle {\n animation: mdl-spinner__right-spin 1333ms cubic-bezier(0.4, 0, 0.2, 1) infinite both; }\n\n@keyframes mdl-spinner__left-spin {\n from {\n transform: rotate(130deg); }\n 50% {\n transform: rotate(-5deg); }\n to {\n transform: rotate(130deg); } }\n\n@keyframes mdl-spinner__right-spin {\n from {\n transform: rotate(-130deg); }\n 50% {\n transform: rotate(5deg); }\n to {\n transform: rotate(-130deg); } }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* Typography */\n/* Shadows */\n/* Animations */\n.mdl-switch {\n position: relative;\n z-index: 1;\n vertical-align: middle;\n display: inline-block;\n box-sizing: border-box;\n width: 100%;\n height: 24px;\n margin: 0;\n padding: 0;\n overflow: visible;\n -webkit-touch-callout: none;\n -webkit-user-select: none;\n -moz-user-select: none;\n -ms-user-select: none;\n user-select: none; }\n .mdl-switch.is-upgraded {\n padding-left: 28px; }\n\n.mdl-switch__input {\n line-height: 24px; }\n .mdl-switch.is-upgraded .mdl-switch__input {\n position: absolute;\n width: 0;\n height: 0;\n margin: 0;\n padding: 0;\n opacity: 0;\n -ms-appearance: none;\n -moz-appearance: none;\n -webkit-appearance: none;\n appearance: none;\n border: none; }\n\n.mdl-switch__track {\n background: rgba(0,0,0, 0.26);\n position: absolute;\n left: 0;\n top: 5px;\n height: 14px;\n width: 36px;\n border-radius: 14px;\n cursor: pointer; }\n .mdl-switch.is-checked .mdl-switch__track {\n background: rgba(63,81,181, 0.5); }\n .mdl-switch.is-disabled .mdl-switch__track {\n background: rgba(0,0,0, 0.12);\n cursor: auto; }\n\n.mdl-switch__thumb {\n background: rgb(250,250,250);\n position: absolute;\n left: 0;\n top: 2px;\n height: 20px;\n width: 20px;\n border-radius: 50%;\n cursor: pointer;\n box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12);\n transition-duration: 0.28s;\n transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n transition-property: left; }\n .mdl-switch.is-checked .mdl-switch__thumb {\n background: rgb(63,81,181);\n left: 16px;\n box-shadow: 0 3px 4px 0 rgba(0, 0, 0, 0.14), 0 3px 3px -2px rgba(0, 0, 0, 0.2), 0 1px 8px 0 rgba(0, 0, 0, 0.12); }\n .mdl-switch.is-disabled .mdl-switch__thumb {\n background: rgb(189,189,189);\n cursor: auto; }\n\n.mdl-switch__focus-helper {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-4px, -4px);\n display: inline-block;\n box-sizing: border-box;\n width: 8px;\n height: 8px;\n border-radius: 50%;\n background-color: transparent; }\n .mdl-switch.is-focused .mdl-switch__focus-helper {\n box-shadow: 0 0 0px 20px rgba(0, 0, 0, 0.1);\n background-color: rgba(0, 0, 0, 0.1); }\n .mdl-switch.is-focused.is-checked .mdl-switch__focus-helper {\n box-shadow: 0 0 0px 20px rgba(63,81,181, 0.26);\n background-color: rgba(63,81,181, 0.26); }\n\n.mdl-switch__label {\n position: relative;\n cursor: pointer;\n font-size: 16px;\n line-height: 24px;\n margin: 0;\n left: 24px; }\n .mdl-switch.is-disabled .mdl-switch__label {\n color: rgb(189,189,189);\n cursor: auto; }\n\n.mdl-switch__ripple-container {\n position: absolute;\n z-index: 2;\n top: -12px;\n left: -14px;\n box-sizing: border-box;\n width: 48px;\n height: 48px;\n border-radius: 50%;\n cursor: pointer;\n overflow: hidden;\n -webkit-mask-image: -webkit-radial-gradient(circle, white, black);\n transition-duration: 0.40s;\n transition-timing-function: step-end;\n transition-property: left; }\n .mdl-switch__ripple-container .mdl-ripple {\n background: rgb(63,81,181); }\n .mdl-switch.is-disabled .mdl-switch__ripple-container {\n cursor: auto; }\n .mdl-switch.is-disabled .mdl-switch__ripple-container .mdl-ripple {\n background: transparent; }\n .mdl-switch.is-checked .mdl-switch__ripple-container {\n cursor: auto;\n left: 2px; }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n.mdl-tabs {\n display: block;\n width: 100%; }\n\n.mdl-tabs__tab-bar {\n display: flex;\n flex-direction: row;\n justify-content: center;\n align-content: space-between;\n align-items: flex-start;\n height: 48px;\n padding: 0 0 0 0;\n margin: 0;\n border-bottom: 1px solid rgb(224,224,224); }\n\n.mdl-tabs__tab {\n margin: 0;\n border: none;\n padding: 0 24px 0 24px;\n float: left;\n position: relative;\n display: block;\n color: red;\n text-decoration: none;\n height: 48px;\n line-height: 48px;\n text-align: center;\n font-weight: 500;\n font-size: 14px;\n text-transform: uppercase;\n color: rgba(0,0,0, 0.54);\n overflow: hidden; }\n .mdl-tabs.is-upgraded .mdl-tabs__tab.is-active {\n color: rgba(0,0,0, 0.87); }\n .mdl-tabs.is-upgraded .mdl-tabs__tab.is-active:after {\n height: 2px;\n width: 100%;\n display: block;\n content: \" \";\n bottom: 0px;\n left: 0px;\n position: absolute;\n background: rgb(63,81,181);\n animation: border-expand 0.2s cubic-bezier(0.4, 0, 0.4, 1) 0.01s alternate forwards;\n transition: all 1s cubic-bezier(0.4, 0, 1, 1); }\n .mdl-tabs__tab .mdl-tabs__ripple-container {\n display: block;\n position: absolute;\n height: 100%;\n width: 100%;\n left: 0px;\n top: 0px;\n z-index: 1;\n overflow: hidden; }\n .mdl-tabs__tab .mdl-tabs__ripple-container .mdl-ripple {\n background: rgb(63,81,181); }\n\n.mdl-tabs__panel {\n display: block; }\n .mdl-tabs.is-upgraded .mdl-tabs__panel {\n display: none; }\n .mdl-tabs.is-upgraded .mdl-tabs__panel.is-active {\n display: block; }\n\n@keyframes border-expand {\n 0% {\n opacity: 0;\n width: 0; }\n 100% {\n opacity: 1;\n width: 100%; } }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* Typography */\n/* Shadows */\n/* Animations */\n.mdl-textfield {\n position: relative;\n font-size: 16px;\n display: inline-block;\n box-sizing: border-box;\n width: 300px;\n max-width: 100%;\n margin: 0;\n padding: 20px 0; }\n .mdl-textfield .mdl-button {\n position: absolute;\n bottom: 20px; }\n\n.mdl-textfield--align-right {\n text-align: right; }\n\n.mdl-textfield--full-width {\n width: 100%; }\n\n.mdl-textfield--expandable {\n min-width: 32px;\n width: auto;\n min-height: 32px; }\n\n.mdl-textfield__input {\n border: none;\n border-bottom: 1px solid rgba(0,0,0, 0.12);\n display: block;\n font-size: 16px;\n margin: 0;\n padding: 4px 0;\n width: 100%;\n background: none;\n text-align: left;\n color: inherit; }\n .mdl-textfield.is-focused .mdl-textfield__input {\n outline: none; }\n .mdl-textfield.is-invalid .mdl-textfield__input {\n border-color: rgb(222, 50, 38);\n box-shadow: none; }\n .mdl-textfield.is-disabled .mdl-textfield__input {\n background-color: transparent;\n border-bottom: 1px dotted rgba(0,0,0, 0.12);\n color: rgba(0,0,0, 0.26); }\n\n.mdl-textfield textarea.mdl-textfield__input {\n display: block; }\n\n.mdl-textfield__label {\n bottom: 0;\n color: rgba(0,0,0, 0.26);\n font-size: 16px;\n left: 0;\n right: 0;\n pointer-events: none;\n position: absolute;\n display: block;\n top: 24px;\n width: 100%;\n overflow: hidden;\n white-space: nowrap;\n text-align: left; }\n .mdl-textfield.is-dirty .mdl-textfield__label {\n visibility: hidden; }\n .mdl-textfield--floating-label .mdl-textfield__label {\n transition-duration: 0.2s;\n transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); }\n .mdl-textfield.is-disabled.is-disabled .mdl-textfield__label {\n color: rgba(0,0,0, 0.26); }\n .mdl-textfield--floating-label.is-focused .mdl-textfield__label,\n .mdl-textfield--floating-label.is-dirty .mdl-textfield__label {\n color: rgb(63,81,181);\n font-size: 12px;\n top: 4px;\n visibility: visible; }\n .mdl-textfield--floating-label.is-focused .mdl-textfield__expandable-holder .mdl-textfield__label,\n .mdl-textfield--floating-label.is-dirty .mdl-textfield__expandable-holder .mdl-textfield__label {\n top: -16px; }\n .mdl-textfield--floating-label.is-invalid .mdl-textfield__label {\n color: rgb(222, 50, 38);\n font-size: 12px; }\n .mdl-textfield__label:after {\n background-color: rgb(63,81,181);\n bottom: 20px;\n content: '';\n height: 2px;\n left: 45%;\n position: absolute;\n transition-duration: 0.2s;\n transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n visibility: hidden;\n width: 10px; }\n .mdl-textfield.is-focused .mdl-textfield__label:after {\n left: 0;\n visibility: visible;\n width: 100%; }\n .mdl-textfield.is-invalid .mdl-textfield__label:after {\n background-color: rgb(222, 50, 38); }\n\n.mdl-textfield__error {\n color: rgb(222, 50, 38);\n position: absolute;\n font-size: 12px;\n margin-top: 3px;\n visibility: hidden;\n display: block; }\n .mdl-textfield.is-invalid .mdl-textfield__error {\n visibility: visible; }\n\n.mdl-textfield__expandable-holder {\n display: inline-block;\n position: relative;\n margin-left: 32px;\n transition-duration: 0.2s;\n transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n display: inline-block;\n max-width: 0.1px; }\n .mdl-textfield.is-focused .mdl-textfield__expandable-holder, .mdl-textfield.is-dirty .mdl-textfield__expandable-holder {\n max-width: 600px; }\n .mdl-textfield__expandable-holder .mdl-textfield__label:after {\n bottom: 0; }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n.mdl-tooltip {\n transform: scale(0);\n transform-origin: top center;\n will-change: transform;\n z-index: 999;\n background: rgba(97,97,97, 0.9);\n border-radius: 2px;\n color: rgb(255,255,255);\n display: inline-block;\n font-size: 10px;\n font-weight: 500;\n line-height: 14px;\n max-width: 170px;\n position: fixed;\n top: -500px;\n left: -500px;\n padding: 8px;\n text-align: center; }\n\n.mdl-tooltip.is-active {\n animation: pulse 200ms cubic-bezier(0, 0, 0.2, 1) forwards; }\n\n.mdl-tooltip--large {\n line-height: 14px;\n font-size: 14px;\n padding: 16px; }\n\n@keyframes pulse {\n 0% {\n transform: scale(0);\n opacity: 0; }\n 50% {\n transform: scale(0.99); }\n 100% {\n transform: scale(1);\n opacity: 1;\n visibility: visible; } }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* Typography */\n/* Shadows */\n/* Animations */\n.mdl-shadow--2dp {\n box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12); }\n\n.mdl-shadow--3dp {\n box-shadow: 0 3px 4px 0 rgba(0, 0, 0, 0.14), 0 3px 3px -2px rgba(0, 0, 0, 0.2), 0 1px 8px 0 rgba(0, 0, 0, 0.12); }\n\n.mdl-shadow--4dp {\n box-shadow: 0 4px 5px 0 rgba(0, 0, 0, 0.14), 0 1px 10px 0 rgba(0, 0, 0, 0.12), 0 2px 4px -1px rgba(0, 0, 0, 0.2); }\n\n.mdl-shadow--6dp {\n box-shadow: 0 6px 10px 0 rgba(0, 0, 0, 0.14), 0 1px 18px 0 rgba(0, 0, 0, 0.12), 0 3px 5px -1px rgba(0, 0, 0, 0.2); }\n\n.mdl-shadow--8dp {\n box-shadow: 0 8px 10px 1px rgba(0, 0, 0, 0.14), 0 3px 14px 2px rgba(0, 0, 0, 0.12), 0 5px 5px -3px rgba(0, 0, 0, 0.2); }\n\n.mdl-shadow--16dp {\n box-shadow: 0 16px 24px 2px rgba(0, 0, 0, 0.14), 0 6px 30px 5px rgba(0, 0, 0, 0.12), 0 8px 10px -5px rgba(0, 0, 0, 0.2); }\n\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*\n* NOTE: Some rules here are applied using duplicate selectors.\n* This is on purpose to increase their specificity when applied.\n* For example: `.mdl-cell--1-col-phone.mdl-cell--1-col-phone`\n*/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*------------------------------------* $CONTENTS\n\\*------------------------------------*/\n/**\n * STYLE GUIDE VARIABLES------------------Declarations of Sass variables\n * -----Typography\n * -----Colors\n * -----Textfield\n * -----Switch\n * -----Spinner\n * -----Radio\n * -----Menu\n * -----List\n * -----Layout\n * -----Icon toggles\n * -----Footer\n * -----Column\n * -----Checkbox\n * -----Card\n * -----Button\n * -----Animation\n * -----Progress\n * -----Badge\n * -----Shadows\n * -----Grid\n * -----Data table\n */\n/* ========== TYPOGRAPHY ========== */\n/* We're splitting fonts into \"preferred\" and \"performance\" in order to optimize\n page loading. For important text, such as the body, we want it to load\n immediately and not wait for the web font load, whereas for other sections,\n such as headers and titles, we're OK with things taking a bit longer to load.\n We do have some optional classes and parameters in the mixins, in case you\n definitely want to make sure you're using the preferred font and don't mind\n the performance hit.\n We should be able to improve on this once CSS Font Loading L3 becomes more\n widely available.\n*/\n/* ========== COLORS ========== */\n/**\n*\n* Material design color palettes.\n* @see http://www.google.com/design/spec/style/color.html\n*\n**/\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== Color Palettes ========== */\n/* colors.scss */\n/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/* ========== IMAGES ========== */\n/* ========== Color & Themes ========== */\n/* ========== Typography ========== */\n/* ========== Components ========== */\n/* ========== Standard Buttons ========== */\n/* ========== Icon Toggles ========== */\n/* ========== Radio Buttons ========== */\n/* ========== Ripple effect ========== */\n/* ========== Layout ========== */\n/* ========== Content Tabs ========== */\n/* ========== Checkboxes ========== */\n/* ========== Switches ========== */\n/* ========== Spinner ========== */\n/* ========== Text fields ========== */\n/* ========== Card ========== */\n/* ========== Sliders ========== */\n/* ========== Progress ========== */\n/* ========== List ========== */\n/* ========== Item ========== */\n/* ========== Dropdown menu ========== */\n/* ========== Tooltips ========== */\n/* ========== Footer ========== */\n/* TEXTFIELD */\n/* SWITCH */\n/* SPINNER */\n/* RADIO */\n/* MENU */\n/* LIST */\n/* LAYOUT */\n/* ICON TOGGLE */\n/* FOOTER */\n/*mega-footer*/\n/*mini-footer*/\n/* CHECKBOX */\n/* CARD */\n/* Card dimensions */\n/* Cover image */\n/* BUTTON */\n/**\n *\n * Dimensions\n *\n */\n/* ANIMATION */\n/* PROGRESS */\n/* BADGE */\n/* SHADOWS */\n/* GRID */\n/* DATA TABLE */\n/* TOOLTIP */\n.mdl-grid {\n display: flex;\n flex-flow: row wrap;\n margin: 0 auto 0 auto;\n align-items: stretch; }\n .mdl-grid.mdl-grid--no-spacing {\n padding: 0; }\n\n.mdl-cell {\n box-sizing: border-box; }\n\n.mdl-cell--top {\n align-self: flex-start; }\n\n.mdl-cell--middle {\n align-self: center; }\n\n.mdl-cell--bottom {\n align-self: flex-end; }\n\n.mdl-cell--stretch {\n align-self: stretch; }\n\n.mdl-grid.mdl-grid--no-spacing > .mdl-cell {\n margin: 0; }\n\n@media (max-width: 479px) {\n .mdl-grid {\n padding: 8px; }\n .mdl-cell {\n margin: 8px;\n width: calc(100% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell {\n width: 100%; }\n .mdl-cell--hide-phone {\n display: none !important; }\n .mdl-cell--1-col,\n .mdl-cell--1-col-phone.mdl-cell--1-col-phone {\n width: calc(25% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--1-col, .mdl-grid--no-spacing >\n .mdl-cell--1-col-phone.mdl-cell--1-col-phone {\n width: 25%; }\n .mdl-cell--2-col,\n .mdl-cell--2-col-phone.mdl-cell--2-col-phone {\n width: calc(50% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--2-col, .mdl-grid--no-spacing >\n .mdl-cell--2-col-phone.mdl-cell--2-col-phone {\n width: 50%; }\n .mdl-cell--3-col,\n .mdl-cell--3-col-phone.mdl-cell--3-col-phone {\n width: calc(75% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--3-col, .mdl-grid--no-spacing >\n .mdl-cell--3-col-phone.mdl-cell--3-col-phone {\n width: 75%; }\n .mdl-cell--4-col,\n .mdl-cell--4-col-phone.mdl-cell--4-col-phone {\n width: calc(100% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--4-col, .mdl-grid--no-spacing >\n .mdl-cell--4-col-phone.mdl-cell--4-col-phone {\n width: 100%; }\n .mdl-cell--5-col,\n .mdl-cell--5-col-phone.mdl-cell--5-col-phone {\n width: calc(100% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--5-col, .mdl-grid--no-spacing >\n .mdl-cell--5-col-phone.mdl-cell--5-col-phone {\n width: 100%; }\n .mdl-cell--6-col,\n .mdl-cell--6-col-phone.mdl-cell--6-col-phone {\n width: calc(100% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--6-col, .mdl-grid--no-spacing >\n .mdl-cell--6-col-phone.mdl-cell--6-col-phone {\n width: 100%; }\n .mdl-cell--7-col,\n .mdl-cell--7-col-phone.mdl-cell--7-col-phone {\n width: calc(100% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--7-col, .mdl-grid--no-spacing >\n .mdl-cell--7-col-phone.mdl-cell--7-col-phone {\n width: 100%; }\n .mdl-cell--8-col,\n .mdl-cell--8-col-phone.mdl-cell--8-col-phone {\n width: calc(100% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--8-col, .mdl-grid--no-spacing >\n .mdl-cell--8-col-phone.mdl-cell--8-col-phone {\n width: 100%; }\n .mdl-cell--9-col,\n .mdl-cell--9-col-phone.mdl-cell--9-col-phone {\n width: calc(100% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--9-col, .mdl-grid--no-spacing >\n .mdl-cell--9-col-phone.mdl-cell--9-col-phone {\n width: 100%; }\n .mdl-cell--10-col,\n .mdl-cell--10-col-phone.mdl-cell--10-col-phone {\n width: calc(100% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--10-col, .mdl-grid--no-spacing >\n .mdl-cell--10-col-phone.mdl-cell--10-col-phone {\n width: 100%; }\n .mdl-cell--11-col,\n .mdl-cell--11-col-phone.mdl-cell--11-col-phone {\n width: calc(100% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--11-col, .mdl-grid--no-spacing >\n .mdl-cell--11-col-phone.mdl-cell--11-col-phone {\n width: 100%; }\n .mdl-cell--12-col,\n .mdl-cell--12-col-phone.mdl-cell--12-col-phone {\n width: calc(100% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--12-col, .mdl-grid--no-spacing >\n .mdl-cell--12-col-phone.mdl-cell--12-col-phone {\n width: 100%; } }\n\n@media (min-width: 480px) and (max-width: 839px) {\n .mdl-grid {\n padding: 8px; }\n .mdl-cell {\n margin: 8px;\n width: calc(50% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell {\n width: 50%; }\n .mdl-cell--hide-tablet {\n display: none !important; }\n .mdl-cell--1-col,\n .mdl-cell--1-col-tablet.mdl-cell--1-col-tablet {\n width: calc(12.5% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--1-col, .mdl-grid--no-spacing >\n .mdl-cell--1-col-tablet.mdl-cell--1-col-tablet {\n width: 12.5%; }\n .mdl-cell--2-col,\n .mdl-cell--2-col-tablet.mdl-cell--2-col-tablet {\n width: calc(25% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--2-col, .mdl-grid--no-spacing >\n .mdl-cell--2-col-tablet.mdl-cell--2-col-tablet {\n width: 25%; }\n .mdl-cell--3-col,\n .mdl-cell--3-col-tablet.mdl-cell--3-col-tablet {\n width: calc(37.5% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--3-col, .mdl-grid--no-spacing >\n .mdl-cell--3-col-tablet.mdl-cell--3-col-tablet {\n width: 37.5%; }\n .mdl-cell--4-col,\n .mdl-cell--4-col-tablet.mdl-cell--4-col-tablet {\n width: calc(50% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--4-col, .mdl-grid--no-spacing >\n .mdl-cell--4-col-tablet.mdl-cell--4-col-tablet {\n width: 50%; }\n .mdl-cell--5-col,\n .mdl-cell--5-col-tablet.mdl-cell--5-col-tablet {\n width: calc(62.5% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--5-col, .mdl-grid--no-spacing >\n .mdl-cell--5-col-tablet.mdl-cell--5-col-tablet {\n width: 62.5%; }\n .mdl-cell--6-col,\n .mdl-cell--6-col-tablet.mdl-cell--6-col-tablet {\n width: calc(75% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--6-col, .mdl-grid--no-spacing >\n .mdl-cell--6-col-tablet.mdl-cell--6-col-tablet {\n width: 75%; }\n .mdl-cell--7-col,\n .mdl-cell--7-col-tablet.mdl-cell--7-col-tablet {\n width: calc(87.5% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--7-col, .mdl-grid--no-spacing >\n .mdl-cell--7-col-tablet.mdl-cell--7-col-tablet {\n width: 87.5%; }\n .mdl-cell--8-col,\n .mdl-cell--8-col-tablet.mdl-cell--8-col-tablet {\n width: calc(100% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--8-col, .mdl-grid--no-spacing >\n .mdl-cell--8-col-tablet.mdl-cell--8-col-tablet {\n width: 100%; }\n .mdl-cell--9-col,\n .mdl-cell--9-col-tablet.mdl-cell--9-col-tablet {\n width: calc(100% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--9-col, .mdl-grid--no-spacing >\n .mdl-cell--9-col-tablet.mdl-cell--9-col-tablet {\n width: 100%; }\n .mdl-cell--10-col,\n .mdl-cell--10-col-tablet.mdl-cell--10-col-tablet {\n width: calc(100% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--10-col, .mdl-grid--no-spacing >\n .mdl-cell--10-col-tablet.mdl-cell--10-col-tablet {\n width: 100%; }\n .mdl-cell--11-col,\n .mdl-cell--11-col-tablet.mdl-cell--11-col-tablet {\n width: calc(100% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--11-col, .mdl-grid--no-spacing >\n .mdl-cell--11-col-tablet.mdl-cell--11-col-tablet {\n width: 100%; }\n .mdl-cell--12-col,\n .mdl-cell--12-col-tablet.mdl-cell--12-col-tablet {\n width: calc(100% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--12-col, .mdl-grid--no-spacing >\n .mdl-cell--12-col-tablet.mdl-cell--12-col-tablet {\n width: 100%; } }\n\n@media (min-width: 840px) {\n .mdl-grid {\n padding: 8px; }\n .mdl-cell {\n margin: 8px;\n width: calc(33.3333333333% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell {\n width: 33.3333333333%; }\n .mdl-cell--hide-desktop {\n display: none !important; }\n .mdl-cell--1-col,\n .mdl-cell--1-col-desktop.mdl-cell--1-col-desktop {\n width: calc(8.3333333333% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--1-col, .mdl-grid--no-spacing >\n .mdl-cell--1-col-desktop.mdl-cell--1-col-desktop {\n width: 8.3333333333%; }\n .mdl-cell--2-col,\n .mdl-cell--2-col-desktop.mdl-cell--2-col-desktop {\n width: calc(16.6666666667% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--2-col, .mdl-grid--no-spacing >\n .mdl-cell--2-col-desktop.mdl-cell--2-col-desktop {\n width: 16.6666666667%; }\n .mdl-cell--3-col,\n .mdl-cell--3-col-desktop.mdl-cell--3-col-desktop {\n width: calc(25% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--3-col, .mdl-grid--no-spacing >\n .mdl-cell--3-col-desktop.mdl-cell--3-col-desktop {\n width: 25%; }\n .mdl-cell--4-col,\n .mdl-cell--4-col-desktop.mdl-cell--4-col-desktop {\n width: calc(33.3333333333% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--4-col, .mdl-grid--no-spacing >\n .mdl-cell--4-col-desktop.mdl-cell--4-col-desktop {\n width: 33.3333333333%; }\n .mdl-cell--5-col,\n .mdl-cell--5-col-desktop.mdl-cell--5-col-desktop {\n width: calc(41.6666666667% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--5-col, .mdl-grid--no-spacing >\n .mdl-cell--5-col-desktop.mdl-cell--5-col-desktop {\n width: 41.6666666667%; }\n .mdl-cell--6-col,\n .mdl-cell--6-col-desktop.mdl-cell--6-col-desktop {\n width: calc(50% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--6-col, .mdl-grid--no-spacing >\n .mdl-cell--6-col-desktop.mdl-cell--6-col-desktop {\n width: 50%; }\n .mdl-cell--7-col,\n .mdl-cell--7-col-desktop.mdl-cell--7-col-desktop {\n width: calc(58.3333333333% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--7-col, .mdl-grid--no-spacing >\n .mdl-cell--7-col-desktop.mdl-cell--7-col-desktop {\n width: 58.3333333333%; }\n .mdl-cell--8-col,\n .mdl-cell--8-col-desktop.mdl-cell--8-col-desktop {\n width: calc(66.6666666667% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--8-col, .mdl-grid--no-spacing >\n .mdl-cell--8-col-desktop.mdl-cell--8-col-desktop {\n width: 66.6666666667%; }\n .mdl-cell--9-col,\n .mdl-cell--9-col-desktop.mdl-cell--9-col-desktop {\n width: calc(75% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--9-col, .mdl-grid--no-spacing >\n .mdl-cell--9-col-desktop.mdl-cell--9-col-desktop {\n width: 75%; }\n .mdl-cell--10-col,\n .mdl-cell--10-col-desktop.mdl-cell--10-col-desktop {\n width: calc(83.3333333333% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--10-col, .mdl-grid--no-spacing >\n .mdl-cell--10-col-desktop.mdl-cell--10-col-desktop {\n width: 83.3333333333%; }\n .mdl-cell--11-col,\n .mdl-cell--11-col-desktop.mdl-cell--11-col-desktop {\n width: calc(91.6666666667% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--11-col, .mdl-grid--no-spacing >\n .mdl-cell--11-col-desktop.mdl-cell--11-col-desktop {\n width: 91.6666666667%; }\n .mdl-cell--12-col,\n .mdl-cell--12-col-desktop.mdl-cell--12-col-desktop {\n width: calc(100% - 16px); }\n .mdl-grid--no-spacing > .mdl-cell--12-col, .mdl-grid--no-spacing >\n .mdl-cell--12-col-desktop.mdl-cell--12-col-desktop {\n width: 100%; } }\n","/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/* Material Design Lite */\n\n// Variables and mixins\n@import \"variables\";\n@import \"mixins\";\n\n// Resets and dependencies\n@import \"resets/resets\";\n@import \"typography/typography\";\n\n// Components\n@import \"palette/palette\";\n@import \"ripple/ripple\";\n@import \"animation/animation\";\n@import \"badge/badge\";\n@import \"button/button\";\n@import \"card/card\";\n@import \"checkbox/checkbox\";\n@import \"data-table/data-table\";\n@import \"footer/mega_footer\";\n@import \"footer/mini_footer\";\n@import \"icon-toggle/icon-toggle\";\n@import \"menu/menu\";\n@import \"progress/progress\";\n@import \"layout/layout\";\n@import \"radio/radio\";\n@import \"slider/slider\";\n@import \"spinner/spinner\";\n@import \"switch/switch\";\n@import \"tabs/tabs\";\n@import \"textfield/textfield\";\n@import \"tooltip/tooltip\";\n@import \"shadow/shadow\";\n@import \"grid/grid\";\n",null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null],"sourceRoot":"/source/"} \ No newline at end of file diff --git a/examples/scalajs-play-core-react/server/src/main/assets/material/material.min.js b/examples/scalajs-play-core-react/server/src/main/assets/material/material.min.js deleted file mode 100644 index cb9bbca..0000000 --- a/examples/scalajs-play-core-react/server/src/main/assets/material/material.min.js +++ /dev/null @@ -1,10 +0,0 @@ -/** - * material-design-lite - Material Design Components in CSS, JS and HTML - * @version v1.0.6 - * @license Apache-2.0 - * @copyright 2015 Google, Inc. - * @link https://github.com/google/material-design-lite - */ -!function(){"use strict";function e(e,t){if(e){if(t.element_.classList.contains(t.CssClasses_.MDL_JS_RIPPLE_EFFECT)){var s=document.createElement("span");s.classList.add(t.CssClasses_.MDL_RIPPLE_CONTAINER),s.classList.add(t.CssClasses_.MDL_JS_RIPPLE_EFFECT);var i=document.createElement("span");i.classList.add(t.CssClasses_.MDL_RIPPLE),s.appendChild(i),e.appendChild(s)}e.addEventListener("click",function(s){s.preventDefault();var i=e.href.split("#")[1],n=t.element_.querySelector("#"+i);t.resetTabState_(),t.resetPanelState_(),e.classList.add(t.CssClasses_.ACTIVE_CLASS),n.classList.add(t.CssClasses_.ACTIVE_CLASS)})}}function t(e,t,s,i){if(i.tabBar_.classList.contains(i.CssClasses_.JS_RIPPLE_EFFECT)){var n=document.createElement("span");n.classList.add(i.CssClasses_.RIPPLE_CONTAINER),n.classList.add(i.CssClasses_.JS_RIPPLE_EFFECT);var a=document.createElement("span");a.classList.add(i.CssClasses_.RIPPLE),n.appendChild(a),e.appendChild(n)}e.addEventListener("click",function(n){n.preventDefault();var a=e.href.split("#")[1],l=i.content_.querySelector("#"+a);i.resetTabState_(t),i.resetPanelState_(s),e.classList.add(i.CssClasses_.IS_ACTIVE),l.classList.add(i.CssClasses_.IS_ACTIVE)})}var s={upgradeDom:function(e,t){},upgradeElement:function(e,t){},upgradeElements:function(e){},upgradeAllRegistered:function(){},registerUpgradedCallback:function(e,t){},register:function(e){},downgradeElements:function(e){}};s=function(){function e(e,t){for(var s=0;sd;d++){if(r=l[d],!r)throw new Error("Unable to find a registered component for the given class.");a.push(r.className),i.setAttribute("data-upgraded",a.join(","));var h=new r.classConstructor(i);h[C]=r,c.push(h);for(var u=0,m=r.callbacks.length;m>u;u++)r.callbacks[u](i);r.widget&&(i[r.className]=h);var E=document.createEvent("Events");E.initEvent("mdl-componentupgraded",!0,!0),i.dispatchEvent(E)}}function a(e){Array.isArray(e)||(e="function"==typeof e.item?Array.prototype.slice.call(e):[e]);for(var t,s=0,i=e.length;i>s;s++)t=e[s],t instanceof HTMLElement&&(n(t),t.children.length>0&&a(t.children))}function l(t){var s="undefined"==typeof t.widget&&"undefined"==typeof t.widget,i=!0;s||(i=t.widget||t.widget);var n={classConstructor:t.constructor||t.constructor,className:t.classAsString||t.classAsString,cssClass:t.cssClass||t.cssClass,widget:i,callbacks:[]};if(p.forEach(function(e){if(e.cssClass===n.cssClass)throw new Error("The provided cssClass has already been registered: "+e.cssClass);if(e.className===n.className)throw new Error("The provided className has already been registered")}),t.constructor.prototype.hasOwnProperty(C))throw new Error("MDL component classes must not have "+C+" defined as a property.");var a=e(t.classAsString,n);a||p.push(n)}function o(t,s){var i=e(t);i&&i.callbacks.push(s)}function r(){for(var e=0;e0&&this.container_.classList.contains(this.CssClasses_.IS_VISIBLE)&&(e.keyCode===this.Keycodes_.UP_ARROW?(e.preventDefault(),t[t.length-1].focus()):e.keyCode===this.Keycodes_.DOWN_ARROW&&(e.preventDefault(),t[0].focus()))}},_.prototype.handleItemKeyboardEvent_=function(e){if(this.element_&&this.container_){var t=this.element_.querySelectorAll("."+this.CssClasses_.ITEM+":not([disabled])");if(t&&t.length>0&&this.container_.classList.contains(this.CssClasses_.IS_VISIBLE)){var s=Array.prototype.slice.call(t).indexOf(e.target);if(e.keyCode===this.Keycodes_.UP_ARROW)e.preventDefault(),s>0?t[s-1].focus():t[t.length-1].focus();else if(e.keyCode===this.Keycodes_.DOWN_ARROW)e.preventDefault(),t.length>s+1?t[s+1].focus():t[0].focus();else if(e.keyCode===this.Keycodes_.SPACE||e.keyCode===this.Keycodes_.ENTER){e.preventDefault();var i=new MouseEvent("mousedown");e.target.dispatchEvent(i),i=new MouseEvent("mouseup"),e.target.dispatchEvent(i),e.target.click()}else e.keyCode===this.Keycodes_.ESCAPE&&(e.preventDefault(),this.hide())}}},_.prototype.handleItemClick_=function(e){e.target.hasAttribute("disabled")?e.stopPropagation():(this.closing_=!0,window.setTimeout(function(e){this.hide(),this.closing_=!1}.bind(this),this.Constant_.CLOSE_TIMEOUT))},_.prototype.applyClip_=function(e,t){this.element_.classList.contains(this.CssClasses_.UNALIGNED)?this.element_.style.clip="":this.element_.classList.contains(this.CssClasses_.BOTTOM_RIGHT)?this.element_.style.clip="rect(0 "+t+"px 0 "+t+"px)":this.element_.classList.contains(this.CssClasses_.TOP_LEFT)?this.element_.style.clip="rect("+e+"px 0 "+e+"px 0)":this.element_.classList.contains(this.CssClasses_.TOP_RIGHT)?this.element_.style.clip="rect("+e+"px "+t+"px "+e+"px "+t+"px)":this.element_.style.clip=""},_.prototype.addAnimationEndListener_=function(){var e=function(){this.element_.removeEventListener("transitionend",e),this.element_.removeEventListener("webkitTransitionEnd",e),this.element_.classList.remove(this.CssClasses_.IS_ANIMATING)}.bind(this);this.element_.addEventListener("transitionend",e),this.element_.addEventListener("webkitTransitionEnd",e)},_.prototype.show=function(e){if(this.element_&&this.container_&&this.outline_){var t=this.element_.getBoundingClientRect().height,s=this.element_.getBoundingClientRect().width;this.container_.style.width=s+"px",this.container_.style.height=t+"px",this.outline_.style.width=s+"px",this.outline_.style.height=t+"px";for(var i=this.Constant_.TRANSITION_DURATION_SECONDS*this.Constant_.TRANSITION_DURATION_FRACTION,n=this.element_.querySelectorAll("."+this.CssClasses_.ITEM),a=0;a=this.maxRows&&e.preventDefault()},E.prototype.onFocus_=function(e){this.element_.classList.add(this.CssClasses_.IS_FOCUSED)},E.prototype.onBlur_=function(e){this.element_.classList.remove(this.CssClasses_.IS_FOCUSED)},E.prototype.updateClasses_=function(){this.checkDisabled(),this.checkValidity(),this.checkDirty()},E.prototype.checkDisabled=function(){this.input_.disabled?this.element_.classList.add(this.CssClasses_.IS_DISABLED):this.element_.classList.remove(this.CssClasses_.IS_DISABLED)},E.prototype.checkDisabled=E.prototype.checkDisabled,E.prototype.checkValidity=function(){this.input_.validity&&(this.input_.validity.valid?this.element_.classList.remove(this.CssClasses_.IS_INVALID):this.element_.classList.add(this.CssClasses_.IS_INVALID))},E.prototype.checkValidity=E.prototype.checkValidity,E.prototype.checkDirty=function(){this.input_.value&&this.input_.value.length>0?this.element_.classList.add(this.CssClasses_.IS_DIRTY):this.element_.classList.remove(this.CssClasses_.IS_DIRTY)},E.prototype.checkDirty=E.prototype.checkDirty,E.prototype.disable=function(){this.input_.disabled=!0,this.updateClasses_()},E.prototype.disable=E.prototype.disable,E.prototype.enable=function(){this.input_.disabled=!1,this.updateClasses_()},E.prototype.enable=E.prototype.enable,E.prototype.change=function(e){this.input_.value=e||"",this.updateClasses_()},E.prototype.change=E.prototype.change,E.prototype.init=function(){if(this.element_&&(this.label_=this.element_.querySelector("."+this.CssClasses_.LABEL),this.input_=this.element_.querySelector("."+this.CssClasses_.INPUT),this.input_)){this.input_.hasAttribute(this.Constant_.MAX_ROWS_ATTRIBUTE)&&(this.maxRows=parseInt(this.input_.getAttribute(this.Constant_.MAX_ROWS_ATTRIBUTE),10),isNaN(this.maxRows)&&(this.maxRows=this.Constant_.NO_MAX_ROWS)),this.boundUpdateClassesHandler=this.updateClasses_.bind(this),this.boundFocusHandler=this.onFocus_.bind(this),this.boundBlurHandler=this.onBlur_.bind(this),this.input_.addEventListener("input",this.boundUpdateClassesHandler),this.input_.addEventListener("focus",this.boundFocusHandler),this.input_.addEventListener("blur",this.boundBlurHandler),this.maxRows!==this.Constant_.NO_MAX_ROWS&&(this.boundKeyDownHandler=this.onKeyDown_.bind(this),this.input_.addEventListener("keydown",this.boundKeyDownHandler));var e=this.element_.classList.contains(this.CssClasses_.IS_INVALID);this.updateClasses_(),this.element_.classList.add(this.CssClasses_.IS_UPGRADED),e&&this.element_.classList.add(this.CssClasses_.IS_INVALID)}},E.prototype.mdlDowngrade_=function(){this.input_.removeEventListener("input",this.boundUpdateClassesHandler),this.input_.removeEventListener("focus",this.boundFocusHandler),this.input_.removeEventListener("blur",this.boundBlurHandler),this.boundKeyDownHandler&&this.input_.removeEventListener("keydown",this.boundKeyDownHandler)},E.prototype.mdlDowngrade=E.prototype.mdlDowngrade_,E.prototype.mdlDowngrade=E.prototype.mdlDowngrade,s.register({constructor:E,classAsString:"MaterialTextfield",cssClass:"mdl-js-textfield",widget:!0});var L=function(e){this.element_=e,this.init()};window.MaterialTooltip=L,L.prototype.Constant_={},L.prototype.CssClasses_={IS_ACTIVE:"is-active"},L.prototype.handleMouseEnter_=function(e){e.stopPropagation();var t=e.target.getBoundingClientRect(),s=t.left+t.width/2,i=-1*(this.element_.offsetWidth/2);0>s+i?(this.element_.style.left=0,this.element_.style.marginLeft=0):(this.element_.style.left=s+"px",this.element_.style.marginLeft=i+"px"),this.element_.style.top=t.top+t.height+10+"px",this.element_.classList.add(this.CssClasses_.IS_ACTIVE),window.addEventListener("scroll",this.boundMouseLeaveHandler,!1),window.addEventListener("touchmove",this.boundMouseLeaveHandler,!1)},L.prototype.handleMouseLeave_=function(e){e.stopPropagation(),this.element_.classList.remove(this.CssClasses_.IS_ACTIVE),window.removeEventListener("scroll",this.boundMouseLeaveHandler),window.removeEventListener("touchmove",this.boundMouseLeaveHandler,!1)},L.prototype.init=function(){if(this.element_){var e=this.element_.getAttribute("for");e&&(this.forElement_=document.getElementById(e)),this.forElement_&&(this.forElement_.hasAttribute("tabindex")||this.forElement_.setAttribute("tabindex","0"),this.boundMouseEnterHandler=this.handleMouseEnter_.bind(this),this.boundMouseLeaveHandler=this.handleMouseLeave_.bind(this),this.forElement_.addEventListener("mouseenter",this.boundMouseEnterHandler,!1),this.forElement_.addEventListener("click",this.boundMouseEnterHandler,!1),this.forElement_.addEventListener("blur",this.boundMouseLeaveHandler),this.forElement_.addEventListener("touchstart",this.boundMouseEnterHandler,!1),this.forElement_.addEventListener("mouseleave",this.boundMouseLeaveHandler))}},L.prototype.mdlDowngrade_=function(){this.forElement_&&(this.forElement_.removeEventListener("mouseenter",this.boundMouseEnterHandler,!1),this.forElement_.removeEventListener("click",this.boundMouseEnterHandler,!1),this.forElement_.removeEventListener("touchstart",this.boundMouseEnterHandler,!1),this.forElement_.removeEventListener("mouseleave",this.boundMouseLeaveHandler))},L.prototype.mdlDowngrade=L.prototype.mdlDowngrade_,L.prototype.mdlDowngrade=L.prototype.mdlDowngrade,s.register({constructor:L,classAsString:"MaterialTooltip",cssClass:"mdl-tooltip"});var I=function(e){this.element_=e,this.init()};window.MaterialLayout=I,I.prototype.Constant_={MAX_WIDTH:"(max-width: 1024px)",TAB_SCROLL_PIXELS:100,MENU_ICON:"menu",CHEVRON_LEFT:"chevron_left",CHEVRON_RIGHT:"chevron_right"},I.prototype.Mode_={STANDARD:0,SEAMED:1,WATERFALL:2,SCROLL:3},I.prototype.CssClasses_={CONTAINER:"mdl-layout__container",HEADER:"mdl-layout__header",DRAWER:"mdl-layout__drawer",CONTENT:"mdl-layout__content",DRAWER_BTN:"mdl-layout__drawer-button",ICON:"material-icons",JS_RIPPLE_EFFECT:"mdl-js-ripple-effect",RIPPLE_CONTAINER:"mdl-layout__tab-ripple-container",RIPPLE:"mdl-ripple",RIPPLE_IGNORE_EVENTS:"mdl-js-ripple-effect--ignore-events",HEADER_SEAMED:"mdl-layout__header--seamed",HEADER_WATERFALL:"mdl-layout__header--waterfall",HEADER_SCROLL:"mdl-layout__header--scroll",FIXED_HEADER:"mdl-layout--fixed-header",OBFUSCATOR:"mdl-layout__obfuscator",TAB_BAR:"mdl-layout__tab-bar",TAB_CONTAINER:"mdl-layout__tab-bar-container",TAB:"mdl-layout__tab",TAB_BAR_BUTTON:"mdl-layout__tab-bar-button",TAB_BAR_LEFT_BUTTON:"mdl-layout__tab-bar-left-button",TAB_BAR_RIGHT_BUTTON:"mdl-layout__tab-bar-right-button",PANEL:"mdl-layout__tab-panel",HAS_DRAWER:"has-drawer",HAS_TABS:"has-tabs",HAS_SCROLLING_HEADER:"has-scrolling-header",CASTING_SHADOW:"is-casting-shadow",IS_COMPACT:"is-compact",IS_SMALL_SCREEN:"is-small-screen",IS_DRAWER_OPEN:"is-visible",IS_ACTIVE:"is-active",IS_UPGRADED:"is-upgraded",IS_ANIMATING:"is-animating",ON_LARGE_SCREEN:"mdl-layout--large-screen-only",ON_SMALL_SCREEN:"mdl-layout--small-screen-only"},I.prototype.contentScrollHandler_=function(){this.header_.classList.contains(this.CssClasses_.IS_ANIMATING)||(this.content_.scrollTop>0&&!this.header_.classList.contains(this.CssClasses_.IS_COMPACT)?(this.header_.classList.add(this.CssClasses_.CASTING_SHADOW),this.header_.classList.add(this.CssClasses_.IS_COMPACT),this.header_.classList.add(this.CssClasses_.IS_ANIMATING)):this.content_.scrollTop<=0&&this.header_.classList.contains(this.CssClasses_.IS_COMPACT)&&(this.header_.classList.remove(this.CssClasses_.CASTING_SHADOW),this.header_.classList.remove(this.CssClasses_.IS_COMPACT),this.header_.classList.add(this.CssClasses_.IS_ANIMATING)))},I.prototype.screenSizeHandler_=function(){this.screenSizeMediaQuery_.matches?this.element_.classList.add(this.CssClasses_.IS_SMALL_SCREEN):(this.element_.classList.remove(this.CssClasses_.IS_SMALL_SCREEN),this.drawer_&&(this.drawer_.classList.remove(this.CssClasses_.IS_DRAWER_OPEN),this.obfuscator_.classList.remove(this.CssClasses_.IS_DRAWER_OPEN)))},I.prototype.drawerToggleHandler_=function(){this.drawer_.classList.toggle(this.CssClasses_.IS_DRAWER_OPEN),this.obfuscator_.classList.toggle(this.CssClasses_.IS_DRAWER_OPEN)},I.prototype.headerTransitionEndHandler_=function(){this.header_.classList.remove(this.CssClasses_.IS_ANIMATING)},I.prototype.headerClickHandler_=function(){this.header_.classList.contains(this.CssClasses_.IS_COMPACT)&&(this.header_.classList.remove(this.CssClasses_.IS_COMPACT),this.header_.classList.add(this.CssClasses_.IS_ANIMATING))},I.prototype.resetTabState_=function(e){for(var t=0;tn;n++){var a=s[n];a.classList&&a.classList.contains(this.CssClasses_.HEADER)&&(this.header_=a),a.classList&&a.classList.contains(this.CssClasses_.DRAWER)&&(this.drawer_=a),a.classList&&a.classList.contains(this.CssClasses_.CONTENT)&&(this.content_=a)}this.header_&&(this.tabBar_=this.header_.querySelector("."+this.CssClasses_.TAB_BAR));var l=this.Mode_.STANDARD;if(this.header_&&(this.header_.classList.contains(this.CssClasses_.HEADER_SEAMED)?l=this.Mode_.SEAMED:this.header_.classList.contains(this.CssClasses_.HEADER_WATERFALL)?(l=this.Mode_.WATERFALL,this.header_.addEventListener("transitionend",this.headerTransitionEndHandler_.bind(this)),this.header_.addEventListener("click",this.headerClickHandler_.bind(this))):this.header_.classList.contains(this.CssClasses_.HEADER_SCROLL)&&(l=this.Mode_.SCROLL,e.classList.add(this.CssClasses_.HAS_SCROLLING_HEADER)),l===this.Mode_.STANDARD?(this.header_.classList.add(this.CssClasses_.CASTING_SHADOW),this.tabBar_&&this.tabBar_.classList.add(this.CssClasses_.CASTING_SHADOW)):l===this.Mode_.SEAMED||l===this.Mode_.SCROLL?(this.header_.classList.remove(this.CssClasses_.CASTING_SHADOW),this.tabBar_&&this.tabBar_.classList.remove(this.CssClasses_.CASTING_SHADOW)):l===this.Mode_.WATERFALL&&(this.content_.addEventListener("scroll",this.contentScrollHandler_.bind(this)),this.contentScrollHandler_())),this.drawer_){var o=this.element_.querySelector("."+this.CssClasses_.DRAWER_BTN);if(!o){o=document.createElement("div"),o.classList.add(this.CssClasses_.DRAWER_BTN);var r=document.createElement("i");r.classList.add(this.CssClasses_.ICON),r.textContent=this.Constant_.MENU_ICON,o.appendChild(r)}this.drawer_.classList.contains(this.CssClasses_.ON_LARGE_SCREEN)?o.classList.add(this.CssClasses_.ON_LARGE_SCREEN):this.drawer_.classList.contains(this.CssClasses_.ON_SMALL_SCREEN)&&o.classList.add(this.CssClasses_.ON_SMALL_SCREEN),o.addEventListener("click",this.drawerToggleHandler_.bind(this)),this.element_.classList.add(this.CssClasses_.HAS_DRAWER),this.element_.classList.contains(this.CssClasses_.FIXED_HEADER)?this.header_.insertBefore(o,this.header_.firstChild):this.element_.insertBefore(o,this.content_);var d=document.createElement("div");d.classList.add(this.CssClasses_.OBFUSCATOR),this.element_.appendChild(d),d.addEventListener("click",this.drawerToggleHandler_.bind(this)),this.obfuscator_=d}if(this.screenSizeMediaQuery_=window.matchMedia(this.Constant_.MAX_WIDTH),this.screenSizeMediaQuery_.addListener(this.screenSizeHandler_.bind(this)),this.screenSizeHandler_(),this.header_&&this.tabBar_){this.element_.classList.add(this.CssClasses_.HAS_TABS);var _=document.createElement("div");_.classList.add(this.CssClasses_.TAB_CONTAINER),this.header_.insertBefore(_,this.tabBar_),this.header_.removeChild(this.tabBar_);var h=document.createElement("div");h.classList.add(this.CssClasses_.TAB_BAR_BUTTON),h.classList.add(this.CssClasses_.TAB_BAR_LEFT_BUTTON);var p=document.createElement("i");p.classList.add(this.CssClasses_.ICON),p.textContent=this.Constant_.CHEVRON_LEFT,h.appendChild(p),h.addEventListener("click",function(){this.tabBar_.scrollLeft-=this.Constant_.TAB_SCROLL_PIXELS}.bind(this));var c=document.createElement("div");c.classList.add(this.CssClasses_.TAB_BAR_BUTTON),c.classList.add(this.CssClasses_.TAB_BAR_RIGHT_BUTTON);var u=document.createElement("i");u.classList.add(this.CssClasses_.ICON),u.textContent=this.Constant_.CHEVRON_RIGHT,c.appendChild(u),c.addEventListener("click",function(){this.tabBar_.scrollLeft+=this.Constant_.TAB_SCROLL_PIXELS}.bind(this)),_.appendChild(h),_.appendChild(this.tabBar_),_.appendChild(c);var C=function(){this.tabBar_.scrollLeft>0?h.classList.add(this.CssClasses_.IS_ACTIVE):h.classList.remove(this.CssClasses_.IS_ACTIVE),this.tabBar_.scrollLeft0)return;this.setFrameCount(1);var i,n,a=e.currentTarget.getBoundingClientRect();if(0===e.clientX&&0===e.clientY)i=Math.round(a.width/2),n=Math.round(a.height/2);else{var l=e.clientX?e.clientX:e.touches[0].clientX,o=e.clientY?e.clientY:e.touches[0].clientY;i=Math.round(l-a.left),n=Math.round(o-a.top)}this.setRippleXY(i,n),this.setRippleStyles(!0),window.requestAnimationFrame(this.animFrameHandler.bind(this))}},b.prototype.upHandler_=function(e){e&&2!==e.detail&&this.rippleElement_.classList.remove(this.CssClasses_.IS_VISIBLE),window.setTimeout(function(){this.rippleElement_.classList.remove(this.CssClasses_.IS_VISIBLE)}.bind(this),0)},b.prototype.init=function(){if(this.element_){var e=this.element_.classList.contains(this.CssClasses_.RIPPLE_CENTER);this.element_.classList.contains(this.CssClasses_.RIPPLE_EFFECT_IGNORE_EVENTS)||(this.rippleElement_=this.element_.querySelector("."+this.CssClasses_.RIPPLE),this.frameCount_=0,this.rippleSize_=0,this.x_=0,this.y_=0,this.ignoringMouseDown_=!1,this.boundDownHandler=this.downHandler_.bind(this),this.element_.addEventListener("mousedown",this.boundDownHandler),this.element_.addEventListener("touchstart",this.boundDownHandler),this.boundUpHandler=this.upHandler_.bind(this),this.element_.addEventListener("mouseup",this.boundUpHandler),this.element_.addEventListener("mouseleave",this.boundUpHandler),this.element_.addEventListener("touchend",this.boundUpHandler),this.element_.addEventListener("blur",this.boundUpHandler),this.getFrameCount=function(){return this.frameCount_},this.setFrameCount=function(e){this.frameCount_=e},this.getRippleElement=function(){return this.rippleElement_},this.setRippleXY=function(e,t){this.x_=e,this.y_=t},this.setRippleStyles=function(t){if(null!==this.rippleElement_){var s,i,n,a="translate("+this.x_+"px, "+this.y_+"px)";t?(i=this.Constant_.INITIAL_SCALE,n=this.Constant_.INITIAL_SIZE):(i=this.Constant_.FINAL_SCALE,n=this.rippleSize_+"px",e&&(a="translate("+this.boundWidth/2+"px, "+this.boundHeight/2+"px)")),s="translate(-50%, -50%) "+a+i,this.rippleElement_.style.webkitTransform=s,this.rippleElement_.style.msTransform=s,this.rippleElement_.style.transform=s,t?this.rippleElement_.classList.remove(this.CssClasses_.IS_ANIMATING):this.rippleElement_.classList.add(this.CssClasses_.IS_ANIMATING)}},this.animFrameHandler=function(){this.frameCount_-->0?window.requestAnimationFrame(this.animFrameHandler.bind(this)):this.setRippleStyles(!1)})}},b.prototype.mdlDowngrade_=function(){this.element_.removeEventListener("mousedown",this.boundDownHandler),this.element_.removeEventListener("touchstart",this.boundDownHandler),this.element_.removeEventListener("mouseup",this.boundUpHandler),this.element_.removeEventListener("mouseleave",this.boundUpHandler),this.element_.removeEventListener("touchend",this.boundUpHandler),this.element_.removeEventListener("blur",this.boundUpHandler)},b.prototype.mdlDowngrade=b.prototype.mdlDowngrade_,b.prototype.mdlDowngrade=b.prototype.mdlDowngrade,s.register({constructor:b,classAsString:"MaterialRipple",cssClass:"mdl-js-ripple-effect",widget:!1})}(); -//# sourceMappingURL=material.min.js.map diff --git a/examples/scalajs-play-core-react/server/src/main/assets/material/material.min.js.map b/examples/scalajs-play-core-react/server/src/main/assets/material/material.min.js.map deleted file mode 100644 index 86ca989..0000000 --- a/examples/scalajs-play-core-react/server/src/main/assets/material/material.min.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["material.js","mdlComponentHandler.js","tabs.js","layout.js","rAF.js","button.js","checkbox.js","icon-toggle.js","menu.js","progress.js","radio.js","slider.js","spinner.js","switch.js","textfield.js","tooltip.js","data-table.js","ripple.js"],"names":["MaterialTab","tab","ctx","element_","classList","contains","CssClasses_","MDL_JS_RIPPLE_EFFECT","rippleContainer","document","createElement","add","MDL_RIPPLE_CONTAINER","ripple","MDL_RIPPLE","appendChild","addEventListener","e","preventDefault","href","split","panel","querySelector","resetTabState_","resetPanelState_","ACTIVE_CLASS","MaterialLayoutTab","tabs","panels","layout","tabBar_","JS_RIPPLE_EFFECT","RIPPLE_CONTAINER","RIPPLE","content_","IS_ACTIVE","componentHandler","upgradeDom","optJsClass","optCssClass","upgradeElement","element","upgradeElements","elements","upgradeAllRegistered","registerUpgradedCallback","jsClass","callback","register","config","downgradeElements","nodes","findRegisteredClass_","name","optReplace","i","registeredComponents_","length","className","getUpgradedListOfElement_","dataUpgraded","getAttribute","isElementUpgraded_","upgradedList","indexOf","upgradeDomInternal","cssClass","registeredClass","querySelectorAll","n","upgradeElementInternal","Element","Error","classesToUpgrade","push","forEach","component","setAttribute","join","instance","classConstructor","componentConfigProperty_","createdComponents_","j","m","callbacks","widget","ev","createEvent","initEvent","dispatchEvent","upgradeElementsInternal","Array","isArray","item","prototype","slice","call","HTMLElement","children","registerInternal","widgetMissing","newConfig","constructor","classAsString","hasOwnProperty","found","registerUpgradedCallbackInternal","regClass","upgradeAllRegisteredInternal","findCreatedComponentByNodeInternal","node","deconstructComponentInternal","downgradeMethod_","componentIndex","splice","upgrades","componentPlace","downgradeNodesInternal","downgradeNode","NodeList","Node","ComponentConfigPublic","ComponentConfig","Component","window","documentElement","Date","now","getTime","vendors","requestAnimationFrame","vp","cancelAnimationFrame","test","navigator","userAgent","lastTime","nextTime","Math","max","setTimeout","clearTimeout","MaterialButton","this","init","Constant_","RIPPLE_EFFECT","blurHandler_","event","blur","disable","disabled","enable","rippleElement_","boundRippleBlurHandler","bind","boundButtonBlurHandler","mdlDowngrade_","removeEventListener","mdlDowngrade","MaterialCheckbox","TINY_TIMEOUT","INPUT","BOX_OUTLINE","FOCUS_HELPER","TICK_OUTLINE","RIPPLE_IGNORE_EVENTS","RIPPLE_CENTER","IS_FOCUSED","IS_DISABLED","IS_CHECKED","IS_UPGRADED","onChange_","updateClasses_","onFocus_","onBlur_","remove","onMouseUp_","blur_","checkDisabled","checkToggleState","inputElement_","checked","check","uncheck","boxOutline","tickContainer","tickOutline","rippleContainerElement_","boundRippleMouseUp","boundInputOnChange","boundInputOnFocus","boundInputOnBlur","boundElementMouseUp","MaterialIconToggle","boundElementOnMouseUp","MaterialMenu","TRANSITION_DURATION_SECONDS","TRANSITION_DURATION_FRACTION","CLOSE_TIMEOUT","Keycodes_","ENTER","ESCAPE","SPACE","UP_ARROW","DOWN_ARROW","CONTAINER","OUTLINE","ITEM","ITEM_RIPPLE_CONTAINER","IS_VISIBLE","IS_ANIMATING","BOTTOM_LEFT","BOTTOM_RIGHT","TOP_LEFT","TOP_RIGHT","UNALIGNED","container","parentElement","insertBefore","removeChild","container_","outline","outline_","forElId","forEl","getElementById","forElement_","handleForClick_","handleForKeyboardEvent_","items","boundItemKeydown_","handleItemKeyboardEvent_","boundItemClick_","handleItemClick_","tabIndex","evt","rect","getBoundingClientRect","forRect","style","right","top","offsetTop","offsetHeight","left","offsetLeft","bottom","toggle","keyCode","focus","currentIndex","target","MouseEvent","click","hide","hasAttribute","stopPropagation","closing_","applyClip_","height","width","clip","addAnimationEndListener_","cleanup","show","transitionDuration","itemDelay","transitionDelay","parentNode","MaterialProgress","INDETERMINATE_CLASS","setProgress","p","progressbar_","setBuffer","bufferbar_","auxbar_","el","firstChild","MaterialRadio","JS_RADIO","RADIO_BTN","RADIO_OUTER_CIRCLE","RADIO_INNER_CIRCLE","radios","getElementsByClassName","button","btnElement_","onMouseup_","boundChangeHandler_","boundFocusHandler_","boundBlurHandler_","boundMouseUpHandler_","outerCircle","innerCircle","MaterialSlider","isIE_","msPointerEnabled","IE_CONTAINER","SLIDER_CONTAINER","BACKGROUND_FLEX","BACKGROUND_LOWER","BACKGROUND_UPPER","IS_LOWEST_VALUE","onInput_","updateValueStyles_","onContainerMouseDown_","newEvent","buttons","clientX","clientY","y","fraction","value","min","backgroundLower_","flex","webkitFlex","backgroundUpper_","change","containerIE","backgroundFlex","boundInputHandler","boundChangeHandler","boundMouseUpHandler","boundContainerMouseDownHandler","MaterialSpinner","MDL_SPINNER_LAYER_COUNT","MDL_SPINNER_LAYER","MDL_SPINNER_CIRCLE_CLIPPER","MDL_SPINNER_CIRCLE","MDL_SPINNER_GAP_PATCH","MDL_SPINNER_LEFT","MDL_SPINNER_RIGHT","createLayer","index","layer","leftClipper","gapPatch","rightClipper","circleOwners","circle","stop","start","MaterialSwitch","TRACK","THUMB","on","off","track","thumb","focusHelper","boundFocusHandler","boundBlurHandler","MaterialTabs","TAB_CLASS","PANEL_CLASS","UPGRADED_CLASS","MDL_JS_RIPPLE_EFFECT_IGNORE_EVENTS","initTabs_","tabs_","panels_","k","MaterialTextfield","maxRows","NO_MAX_ROWS","MAX_ROWS_ATTRIBUTE","LABEL","IS_DIRTY","IS_INVALID","onKeyDown_","currentRowCount","checkValidity","checkDirty","input_","validity","valid","label_","parseInt","isNaN","boundUpdateClassesHandler","boundKeyDownHandler","invalid","MaterialTooltip","handleMouseEnter_","props","marginLeft","offsetWidth","boundMouseLeaveHandler","handleMouseLeave_","boundMouseEnterHandler","MaterialLayout","MAX_WIDTH","TAB_SCROLL_PIXELS","MENU_ICON","CHEVRON_LEFT","CHEVRON_RIGHT","Mode_","STANDARD","SEAMED","WATERFALL","SCROLL","HEADER","DRAWER","CONTENT","DRAWER_BTN","ICON","HEADER_SEAMED","HEADER_WATERFALL","HEADER_SCROLL","FIXED_HEADER","OBFUSCATOR","TAB_BAR","TAB_CONTAINER","TAB","TAB_BAR_BUTTON","TAB_BAR_LEFT_BUTTON","TAB_BAR_RIGHT_BUTTON","PANEL","HAS_DRAWER","HAS_TABS","HAS_SCROLLING_HEADER","CASTING_SHADOW","IS_COMPACT","IS_SMALL_SCREEN","IS_DRAWER_OPEN","ON_LARGE_SCREEN","ON_SMALL_SCREEN","contentScrollHandler_","header_","scrollTop","screenSizeHandler_","screenSizeMediaQuery_","matches","drawer_","obfuscator_","drawerToggleHandler_","headerTransitionEndHandler_","headerClickHandler_","tabBar","directChildren","childNodes","numChildren","c","child","mode","drawerButton","drawerButtonIcon","textContent","obfuscator","matchMedia","addListener","tabContainer","leftButton","leftButtonIcon","scrollLeft","rightButton","rightButtonIcon","tabScrollHandler","scrollWidth","MaterialDataTable","DATA_TABLE","SELECTABLE","SELECT_ELEMENT","IS_SELECTED","selectRow_","checkbox","row","opt_rows","createCheckbox_","label","labelClasses","type","firstHeader","rows","th","headerCheckbox","firstCell","td","rowCheckbox","MaterialRipple","INITIAL_SCALE","INITIAL_SIZE","INITIAL_OPACITY","FINAL_OPACITY","FINAL_SCALE","RIPPLE_EFFECT_IGNORE_EVENTS","downHandler_","boundHeight","boundWidth","rippleSize_","sqrt","ignoringMouseDown_","frameCount","getFrameCount","setFrameCount","x","bound","currentTarget","round","touches","setRippleXY","setRippleStyles","animFrameHandler","upHandler_","detail","recentering","frameCount_","x_","y_","boundDownHandler","boundUpHandler","fC","getRippleElement","newX","newY","transformString","scale","size","offset","webkitTransform","msTransform","transform"],"mappings":"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","file":"material.min.js","sourcesContent":[";(function() {\n\"use strict\";\n\n/**\n * @license\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * A component handler interface using the revealing module design pattern.\n * More details on this design pattern here:\n * https://github.com/jasonmayes/mdl-component-design-pattern\n *\n * @author Jason Mayes.\n */\n/* exported componentHandler */\n\n// Pre-defining the componentHandler interface, for closure documentation and\n// static verification.\nvar componentHandler = {\n /**\n * Searches existing DOM for elements of our component type and upgrades them\n * if they have not already been upgraded.\n *\n * @param {string=} optJsClass the programatic name of the element class we\n * need to create a new instance of.\n * @param {string=} optCssClass the name of the CSS class elements of this\n * type will have.\n */\n upgradeDom: function(optJsClass, optCssClass) {},\n /**\n * Upgrades a specific element rather than all in the DOM.\n *\n * @param {!Element} element The element we wish to upgrade.\n * @param {string=} optJsClass Optional name of the class we want to upgrade\n * the element to.\n */\n upgradeElement: function(element, optJsClass) {},\n /**\n * Upgrades a specific list of elements rather than all in the DOM.\n *\n * @param {!Element|!Array|!NodeList|!HTMLCollection} elements\n * The elements we wish to upgrade.\n */\n upgradeElements: function(elements) {},\n /**\n * Upgrades all registered components found in the current DOM. This is\n * automatically called on window load.\n */\n upgradeAllRegistered: function() {},\n /**\n * Allows user to be alerted to any upgrades that are performed for a given\n * component type\n *\n * @param {string} jsClass The class name of the MDL component we wish\n * to hook into for any upgrades performed.\n * @param {function(!HTMLElement)} callback The function to call upon an\n * upgrade. This function should expect 1 parameter - the HTMLElement which\n * got upgraded.\n */\n registerUpgradedCallback: function(jsClass, callback) {},\n /**\n * Registers a class for future use and attempts to upgrade existing DOM.\n *\n * @param {componentHandler.ComponentConfigPublic} config the registration configuration\n */\n register: function(config) {},\n /**\n * Downgrade either a given node, an array of nodes, or a NodeList.\n *\n * @param {!Node|!Array|!NodeList} nodes\n */\n downgradeElements: function(nodes) {}\n};\n\ncomponentHandler = (function() {\n 'use strict';\n\n /** @type {!Array} */\n var registeredComponents_ = [];\n\n /** @type {!Array} */\n var createdComponents_ = [];\n\n var downgradeMethod_ = 'mdlDowngrade';\n var componentConfigProperty_ = 'mdlComponentConfigInternal_';\n\n /**\n * Searches registered components for a class we are interested in using.\n * Optionally replaces a match with passed object if specified.\n *\n * @param {string} name The name of a class we want to use.\n * @param {componentHandler.ComponentConfig=} optReplace Optional object to replace match with.\n * @return {!Object|boolean}\n * @private\n */\n function findRegisteredClass_(name, optReplace) {\n for (var i = 0; i < registeredComponents_.length; i++) {\n if (registeredComponents_[i].className === name) {\n if (typeof optReplace !== 'undefined') {\n registeredComponents_[i] = optReplace;\n }\n return registeredComponents_[i];\n }\n }\n return false;\n }\n\n /**\n * Returns an array of the classNames of the upgraded classes on the element.\n *\n * @param {!Element} element The element to fetch data from.\n * @return {!Array}\n * @private\n */\n function getUpgradedListOfElement_(element) {\n var dataUpgraded = element.getAttribute('data-upgraded');\n // Use `['']` as default value to conform the `,name,name...` style.\n return dataUpgraded === null ? [''] : dataUpgraded.split(',');\n }\n\n /**\n * Returns true if the given element has already been upgraded for the given\n * class.\n *\n * @param {!Element} element The element we want to check.\n * @param {string} jsClass The class to check for.\n * @returns {boolean}\n * @private\n */\n function isElementUpgraded_(element, jsClass) {\n var upgradedList = getUpgradedListOfElement_(element);\n return upgradedList.indexOf(jsClass) !== -1;\n }\n\n /**\n * Searches existing DOM for elements of our component type and upgrades them\n * if they have not already been upgraded.\n *\n * @param {string=} optJsClass the programatic name of the element class we\n * need to create a new instance of.\n * @param {string=} optCssClass the name of the CSS class elements of this\n * type will have.\n */\n function upgradeDomInternal(optJsClass, optCssClass) {\n if (typeof optJsClass === 'undefined' &&\n typeof optCssClass === 'undefined') {\n for (var i = 0; i < registeredComponents_.length; i++) {\n upgradeDomInternal(registeredComponents_[i].className,\n registeredComponents_[i].cssClass);\n }\n } else {\n var jsClass = /** @type {string} */ (optJsClass);\n if (typeof optCssClass === 'undefined') {\n var registeredClass = findRegisteredClass_(jsClass);\n if (registeredClass) {\n optCssClass = registeredClass.cssClass;\n }\n }\n\n var elements = document.querySelectorAll('.' + optCssClass);\n for (var n = 0; n < elements.length; n++) {\n upgradeElementInternal(elements[n], jsClass);\n }\n }\n }\n\n /**\n * Upgrades a specific element rather than all in the DOM.\n *\n * @param {!Element} element The element we wish to upgrade.\n * @param {string=} optJsClass Optional name of the class we want to upgrade\n * the element to.\n */\n function upgradeElementInternal(element, optJsClass) {\n // Verify argument type.\n if (!(typeof element === 'object' && element instanceof Element)) {\n throw new Error('Invalid argument provided to upgrade MDL element.');\n }\n var upgradedList = getUpgradedListOfElement_(element);\n var classesToUpgrade = [];\n // If jsClass is not provided scan the registered components to find the\n // ones matching the element's CSS classList.\n if (!optJsClass) {\n var classList = element.classList;\n registeredComponents_.forEach(function(component) {\n // Match CSS & Not to be upgraded & Not upgraded.\n if (classList.contains(component.cssClass) &&\n classesToUpgrade.indexOf(component) === -1 &&\n !isElementUpgraded_(element, component.className)) {\n classesToUpgrade.push(component);\n }\n });\n } else if (!isElementUpgraded_(element, optJsClass)) {\n classesToUpgrade.push(findRegisteredClass_(optJsClass));\n }\n\n // Upgrade the element for each classes.\n for (var i = 0, n = classesToUpgrade.length, registeredClass; i < n; i++) {\n registeredClass = classesToUpgrade[i];\n if (registeredClass) {\n // Mark element as upgraded.\n upgradedList.push(registeredClass.className);\n element.setAttribute('data-upgraded', upgradedList.join(','));\n var instance = new registeredClass.classConstructor(element);\n instance[componentConfigProperty_] = registeredClass;\n createdComponents_.push(instance);\n // Call any callbacks the user has registered with this component type.\n for (var j = 0, m = registeredClass.callbacks.length; j < m; j++) {\n registeredClass.callbacks[j](element);\n }\n\n if (registeredClass.widget) {\n // Assign per element instance for control over API\n element[registeredClass.className] = instance;\n }\n } else {\n throw new Error(\n 'Unable to find a registered component for the given class.');\n }\n\n var ev = document.createEvent('Events');\n ev.initEvent('mdl-componentupgraded', true, true);\n element.dispatchEvent(ev);\n }\n }\n\n /**\n * Upgrades a specific list of elements rather than all in the DOM.\n *\n * @param {!Element|!Array|!NodeList|!HTMLCollection} elements\n * The elements we wish to upgrade.\n */\n function upgradeElementsInternal(elements) {\n if (!Array.isArray(elements)) {\n if (typeof elements.item === 'function') {\n elements = Array.prototype.slice.call(/** @type {Array} */ (elements));\n } else {\n elements = [elements];\n }\n }\n for (var i = 0, n = elements.length, element; i < n; i++) {\n element = elements[i];\n if (element instanceof HTMLElement) {\n upgradeElementInternal(element);\n if (element.children.length > 0) {\n upgradeElementsInternal(element.children);\n }\n }\n }\n }\n\n /**\n * Registers a class for future use and attempts to upgrade existing DOM.\n *\n * @param {componentHandler.ComponentConfigPublic} config\n */\n function registerInternal(config) {\n // In order to support both Closure-compiled and uncompiled code accessing\n // this method, we need to allow for both the dot and array syntax for\n // property access. You'll therefore see the `foo.bar || foo['bar']`\n // pattern repeated across this method.\n var widgetMissing = (typeof config.widget === 'undefined' &&\n typeof config['widget'] === 'undefined');\n var widget = true;\n\n if (!widgetMissing) {\n widget = config.widget || config['widget'];\n }\n\n var newConfig = /** @type {componentHandler.ComponentConfig} */ ({\n classConstructor: config.constructor || config['constructor'],\n className: config.classAsString || config['classAsString'],\n cssClass: config.cssClass || config['cssClass'],\n widget: widget,\n callbacks: []\n });\n\n registeredComponents_.forEach(function(item) {\n if (item.cssClass === newConfig.cssClass) {\n throw new Error('The provided cssClass has already been registered: ' + item.cssClass);\n }\n if (item.className === newConfig.className) {\n throw new Error('The provided className has already been registered');\n }\n });\n\n if (config.constructor.prototype\n .hasOwnProperty(componentConfigProperty_)) {\n throw new Error(\n 'MDL component classes must not have ' + componentConfigProperty_ +\n ' defined as a property.');\n }\n\n var found = findRegisteredClass_(config.classAsString, newConfig);\n\n if (!found) {\n registeredComponents_.push(newConfig);\n }\n }\n\n /**\n * Allows user to be alerted to any upgrades that are performed for a given\n * component type\n *\n * @param {string} jsClass The class name of the MDL component we wish\n * to hook into for any upgrades performed.\n * @param {function(!HTMLElement)} callback The function to call upon an\n * upgrade. This function should expect 1 parameter - the HTMLElement which\n * got upgraded.\n */\n function registerUpgradedCallbackInternal(jsClass, callback) {\n var regClass = findRegisteredClass_(jsClass);\n if (regClass) {\n regClass.callbacks.push(callback);\n }\n }\n\n /**\n * Upgrades all registered components found in the current DOM. This is\n * automatically called on window load.\n */\n function upgradeAllRegisteredInternal() {\n for (var n = 0; n < registeredComponents_.length; n++) {\n upgradeDomInternal(registeredComponents_[n].className);\n }\n }\n\n /**\n * Finds a created component by a given DOM node.\n *\n * @param {!Node} node\n * @return {*}\n */\n function findCreatedComponentByNodeInternal(node) {\n for (var n = 0; n < createdComponents_.length; n++) {\n var component = createdComponents_[n];\n if (component.element_ === node) {\n return component;\n }\n }\n }\n\n /**\n * Check the component for the downgrade method.\n * Execute if found.\n * Remove component from createdComponents list.\n *\n * @param {*} component\n */\n function deconstructComponentInternal(component) {\n if (component &&\n component[componentConfigProperty_]\n .classConstructor.prototype\n .hasOwnProperty(downgradeMethod_)) {\n component[downgradeMethod_]();\n var componentIndex = createdComponents_.indexOf(component);\n createdComponents_.splice(componentIndex, 1);\n\n var upgrades = component.element_.getAttribute('data-upgraded').split(',');\n var componentPlace = upgrades.indexOf(\n component[componentConfigProperty_].classAsString);\n upgrades.splice(componentPlace, 1);\n component.element_.setAttribute('data-upgraded', upgrades.join(','));\n\n var ev = document.createEvent('Events');\n ev.initEvent('mdl-componentdowngraded', true, true);\n component.element_.dispatchEvent(ev);\n }\n }\n\n /**\n * Downgrade either a given node, an array of nodes, or a NodeList.\n *\n * @param {!Node|!Array|!NodeList} nodes\n */\n function downgradeNodesInternal(nodes) {\n /**\n * Auxiliary function to downgrade a single node.\n * @param {!Node} node the node to be downgraded\n */\n var downgradeNode = function(node) {\n deconstructComponentInternal(findCreatedComponentByNodeInternal(node));\n };\n if (nodes instanceof Array || nodes instanceof NodeList) {\n for (var n = 0; n < nodes.length; n++) {\n downgradeNode(nodes[n]);\n }\n } else if (nodes instanceof Node) {\n downgradeNode(nodes);\n } else {\n throw new Error('Invalid argument provided to downgrade MDL nodes.');\n }\n }\n\n // Now return the functions that should be made public with their publicly\n // facing names...\n return {\n upgradeDom: upgradeDomInternal,\n upgradeElement: upgradeElementInternal,\n upgradeElements: upgradeElementsInternal,\n upgradeAllRegistered: upgradeAllRegisteredInternal,\n registerUpgradedCallback: registerUpgradedCallbackInternal,\n register: registerInternal,\n downgradeElements: downgradeNodesInternal\n };\n})();\n\n/**\n * Describes the type of a registered component type managed by\n * componentHandler. Provided for benefit of the Closure compiler.\n *\n * @typedef {{\n * constructor: Function,\n * classAsString: string,\n * cssClass: string,\n * widget: (string|boolean|undefined)\n * }}\n */\ncomponentHandler.ComponentConfigPublic; // jshint ignore:line\n\n/**\n * Describes the type of a registered component type managed by\n * componentHandler. Provided for benefit of the Closure compiler.\n *\n * @typedef {{\n * constructor: !Function,\n * className: string,\n * cssClass: string,\n * widget: (string|boolean),\n * callbacks: !Array\n * }}\n */\ncomponentHandler.ComponentConfig; // jshint ignore:line\n\n/**\n * Created component (i.e., upgraded element) type as managed by\n * componentHandler. Provided for benefit of the Closure compiler.\n *\n * @typedef {{\n * element_: !HTMLElement,\n * className: string,\n * classAsString: string,\n * cssClass: string,\n * widget: string\n * }}\n */\ncomponentHandler.Component; // jshint ignore:line\n\n// Export all symbols, for the benefit of Closure compiler.\n// No effect on uncompiled code.\ncomponentHandler['upgradeDom'] = componentHandler.upgradeDom;\ncomponentHandler['upgradeElement'] = componentHandler.upgradeElement;\ncomponentHandler['upgradeElements'] = componentHandler.upgradeElements;\ncomponentHandler['upgradeAllRegistered'] =\n componentHandler.upgradeAllRegistered;\ncomponentHandler['registerUpgradedCallback'] =\n componentHandler.registerUpgradedCallback;\ncomponentHandler['register'] = componentHandler.register;\ncomponentHandler['downgradeElements'] = componentHandler.downgradeElements;\nwindow.componentHandler = componentHandler;\nwindow['componentHandler'] = componentHandler;\n\nwindow.addEventListener('load', function() {\n 'use strict';\n\n /**\n * Performs a \"Cutting the mustard\" test. If the browser supports the features\n * tested, adds a mdl-js class to the element. It then upgrades all MDL\n * components requiring JavaScript.\n */\n if ('classList' in document.createElement('div') &&\n 'querySelector' in document &&\n 'addEventListener' in window && Array.prototype.forEach) {\n document.documentElement.classList.add('mdl-js');\n componentHandler.upgradeAllRegistered();\n } else {\n /**\n * Dummy function to avoid JS errors.\n */\n componentHandler.upgradeElement = function() {};\n /**\n * Dummy function to avoid JS errors.\n */\n componentHandler.register = function() {};\n }\n});\n\n// Source: https://github.com/darius/requestAnimationFrame/blob/master/requestAnimationFrame.js\n// Adapted from https://gist.github.com/paulirish/1579671 which derived from\n// http://paulirish.com/2011/requestanimationframe-for-smart-animating/\n// http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating\n// requestAnimationFrame polyfill by Erik Möller.\n// Fixes from Paul Irish, Tino Zijdel, Andrew Mao, Klemen Slavič, Darius Bacon\n// MIT license\nif (!Date.now) {\n /**\n * Date.now polyfill.\n * @return {number} the current Date\n */\n Date.now = function () {\n return new Date().getTime();\n };\n Date['now'] = Date.now;\n}\nvar vendors = [\n 'webkit',\n 'moz'\n];\nfor (var i = 0; i < vendors.length && !window.requestAnimationFrame; ++i) {\n var vp = vendors[i];\n window.requestAnimationFrame = window[vp + 'RequestAnimationFrame'];\n window.cancelAnimationFrame = window[vp + 'CancelAnimationFrame'] || window[vp + 'CancelRequestAnimationFrame'];\n window['requestAnimationFrame'] = window.requestAnimationFrame;\n window['cancelAnimationFrame'] = window.cancelAnimationFrame;\n}\nif (/iP(ad|hone|od).*OS 6/.test(window.navigator.userAgent) || !window.requestAnimationFrame || !window.cancelAnimationFrame) {\n var lastTime = 0;\n /**\n * requestAnimationFrame polyfill.\n * @param {!Function} callback the callback function.\n */\n window.requestAnimationFrame = function (callback) {\n var now = Date.now();\n var nextTime = Math.max(lastTime + 16, now);\n return setTimeout(function () {\n callback(lastTime = nextTime);\n }, nextTime - now);\n };\n window.cancelAnimationFrame = clearTimeout;\n window['requestAnimationFrame'] = window.requestAnimationFrame;\n window['cancelAnimationFrame'] = window.cancelAnimationFrame;\n}\n/**\n * @license\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Class constructor for Button MDL component.\n * Implements MDL component design pattern defined at:\n * https://github.com/jasonmayes/mdl-component-design-pattern\n *\n * @param {HTMLElement} element The element that will be upgraded.\n */\nvar MaterialButton = function MaterialButton(element) {\n this.element_ = element;\n // Initialize instance.\n this.init();\n};\nwindow['MaterialButton'] = MaterialButton;\n/**\n * Store constants in one place so they can be updated easily.\n *\n * @enum {string | number}\n * @private\n */\nMaterialButton.prototype.Constant_ = {};\n/**\n * Store strings for class names defined by this component that are used in\n * JavaScript. This allows us to simply change it in one place should we\n * decide to modify at a later date.\n *\n * @enum {string}\n * @private\n */\nMaterialButton.prototype.CssClasses_ = {\n RIPPLE_EFFECT: 'mdl-js-ripple-effect',\n RIPPLE_CONTAINER: 'mdl-button__ripple-container',\n RIPPLE: 'mdl-ripple'\n};\n/**\n * Handle blur of element.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialButton.prototype.blurHandler_ = function (event) {\n if (event) {\n this.element_.blur();\n }\n};\n// Public methods.\n/**\n * Disable button.\n *\n * @public\n */\nMaterialButton.prototype.disable = function () {\n this.element_.disabled = true;\n};\nMaterialButton.prototype['disable'] = MaterialButton.prototype.disable;\n/**\n * Enable button.\n *\n * @public\n */\nMaterialButton.prototype.enable = function () {\n this.element_.disabled = false;\n};\nMaterialButton.prototype['enable'] = MaterialButton.prototype.enable;\n/**\n * Initialize element.\n */\nMaterialButton.prototype.init = function () {\n if (this.element_) {\n if (this.element_.classList.contains(this.CssClasses_.RIPPLE_EFFECT)) {\n var rippleContainer = document.createElement('span');\n rippleContainer.classList.add(this.CssClasses_.RIPPLE_CONTAINER);\n this.rippleElement_ = document.createElement('span');\n this.rippleElement_.classList.add(this.CssClasses_.RIPPLE);\n rippleContainer.appendChild(this.rippleElement_);\n this.boundRippleBlurHandler = this.blurHandler_.bind(this);\n this.rippleElement_.addEventListener('mouseup', this.boundRippleBlurHandler);\n this.element_.appendChild(rippleContainer);\n }\n this.boundButtonBlurHandler = this.blurHandler_.bind(this);\n this.element_.addEventListener('mouseup', this.boundButtonBlurHandler);\n this.element_.addEventListener('mouseleave', this.boundButtonBlurHandler);\n }\n};\n/**\n * Downgrade the element.\n *\n * @private\n */\nMaterialButton.prototype.mdlDowngrade_ = function () {\n if (this.rippleElement_) {\n this.rippleElement_.removeEventListener('mouseup', this.boundRippleBlurHandler);\n }\n this.element_.removeEventListener('mouseup', this.boundButtonBlurHandler);\n this.element_.removeEventListener('mouseleave', this.boundButtonBlurHandler);\n};\n/**\n * Public alias for the downgrade method.\n *\n * @public\n */\nMaterialButton.prototype.mdlDowngrade = MaterialButton.prototype.mdlDowngrade_;\nMaterialButton.prototype['mdlDowngrade'] = MaterialButton.prototype.mdlDowngrade;\n// The component registers itself. It can assume componentHandler is available\n// in the global scope.\ncomponentHandler.register({\n constructor: MaterialButton,\n classAsString: 'MaterialButton',\n cssClass: 'mdl-js-button',\n widget: true\n});\n/**\n * @license\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Class constructor for Checkbox MDL component.\n * Implements MDL component design pattern defined at:\n * https://github.com/jasonmayes/mdl-component-design-pattern\n *\n * @constructor\n * @param {HTMLElement} element The element that will be upgraded.\n */\nvar MaterialCheckbox = function MaterialCheckbox(element) {\n this.element_ = element;\n // Initialize instance.\n this.init();\n};\nwindow['MaterialCheckbox'] = MaterialCheckbox;\n/**\n * Store constants in one place so they can be updated easily.\n *\n * @enum {string | number}\n * @private\n */\nMaterialCheckbox.prototype.Constant_ = { TINY_TIMEOUT: 0.001 };\n/**\n * Store strings for class names defined by this component that are used in\n * JavaScript. This allows us to simply change it in one place should we\n * decide to modify at a later date.\n *\n * @enum {string}\n * @private\n */\nMaterialCheckbox.prototype.CssClasses_ = {\n INPUT: 'mdl-checkbox__input',\n BOX_OUTLINE: 'mdl-checkbox__box-outline',\n FOCUS_HELPER: 'mdl-checkbox__focus-helper',\n TICK_OUTLINE: 'mdl-checkbox__tick-outline',\n RIPPLE_EFFECT: 'mdl-js-ripple-effect',\n RIPPLE_IGNORE_EVENTS: 'mdl-js-ripple-effect--ignore-events',\n RIPPLE_CONTAINER: 'mdl-checkbox__ripple-container',\n RIPPLE_CENTER: 'mdl-ripple--center',\n RIPPLE: 'mdl-ripple',\n IS_FOCUSED: 'is-focused',\n IS_DISABLED: 'is-disabled',\n IS_CHECKED: 'is-checked',\n IS_UPGRADED: 'is-upgraded'\n};\n/**\n * Handle change of state.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialCheckbox.prototype.onChange_ = function (event) {\n this.updateClasses_();\n};\n/**\n * Handle focus of element.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialCheckbox.prototype.onFocus_ = function (event) {\n this.element_.classList.add(this.CssClasses_.IS_FOCUSED);\n};\n/**\n * Handle lost focus of element.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialCheckbox.prototype.onBlur_ = function (event) {\n this.element_.classList.remove(this.CssClasses_.IS_FOCUSED);\n};\n/**\n * Handle mouseup.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialCheckbox.prototype.onMouseUp_ = function (event) {\n this.blur_();\n};\n/**\n * Handle class updates.\n *\n * @private\n */\nMaterialCheckbox.prototype.updateClasses_ = function () {\n this.checkDisabled();\n this.checkToggleState();\n};\n/**\n * Add blur.\n *\n * @private\n */\nMaterialCheckbox.prototype.blur_ = function () {\n // TODO: figure out why there's a focus event being fired after our blur,\n // so that we can avoid this hack.\n window.setTimeout(function () {\n this.inputElement_.blur();\n }.bind(this), this.Constant_.TINY_TIMEOUT);\n};\n// Public methods.\n/**\n * Check the inputs toggle state and update display.\n *\n * @public\n */\nMaterialCheckbox.prototype.checkToggleState = function () {\n if (this.inputElement_.checked) {\n this.element_.classList.add(this.CssClasses_.IS_CHECKED);\n } else {\n this.element_.classList.remove(this.CssClasses_.IS_CHECKED);\n }\n};\nMaterialCheckbox.prototype['checkToggleState'] = MaterialCheckbox.prototype.checkToggleState;\n/**\n * Check the inputs disabled state and update display.\n *\n * @public\n */\nMaterialCheckbox.prototype.checkDisabled = function () {\n if (this.inputElement_.disabled) {\n this.element_.classList.add(this.CssClasses_.IS_DISABLED);\n } else {\n this.element_.classList.remove(this.CssClasses_.IS_DISABLED);\n }\n};\nMaterialCheckbox.prototype['checkDisabled'] = MaterialCheckbox.prototype.checkDisabled;\n/**\n * Disable checkbox.\n *\n * @public\n */\nMaterialCheckbox.prototype.disable = function () {\n this.inputElement_.disabled = true;\n this.updateClasses_();\n};\nMaterialCheckbox.prototype['disable'] = MaterialCheckbox.prototype.disable;\n/**\n * Enable checkbox.\n *\n * @public\n */\nMaterialCheckbox.prototype.enable = function () {\n this.inputElement_.disabled = false;\n this.updateClasses_();\n};\nMaterialCheckbox.prototype['enable'] = MaterialCheckbox.prototype.enable;\n/**\n * Check checkbox.\n *\n * @public\n */\nMaterialCheckbox.prototype.check = function () {\n this.inputElement_.checked = true;\n this.updateClasses_();\n};\nMaterialCheckbox.prototype['check'] = MaterialCheckbox.prototype.check;\n/**\n * Uncheck checkbox.\n *\n * @public\n */\nMaterialCheckbox.prototype.uncheck = function () {\n this.inputElement_.checked = false;\n this.updateClasses_();\n};\nMaterialCheckbox.prototype['uncheck'] = MaterialCheckbox.prototype.uncheck;\n/**\n * Initialize element.\n */\nMaterialCheckbox.prototype.init = function () {\n if (this.element_) {\n this.inputElement_ = this.element_.querySelector('.' + this.CssClasses_.INPUT);\n var boxOutline = document.createElement('span');\n boxOutline.classList.add(this.CssClasses_.BOX_OUTLINE);\n var tickContainer = document.createElement('span');\n tickContainer.classList.add(this.CssClasses_.FOCUS_HELPER);\n var tickOutline = document.createElement('span');\n tickOutline.classList.add(this.CssClasses_.TICK_OUTLINE);\n boxOutline.appendChild(tickOutline);\n this.element_.appendChild(tickContainer);\n this.element_.appendChild(boxOutline);\n if (this.element_.classList.contains(this.CssClasses_.RIPPLE_EFFECT)) {\n this.element_.classList.add(this.CssClasses_.RIPPLE_IGNORE_EVENTS);\n this.rippleContainerElement_ = document.createElement('span');\n this.rippleContainerElement_.classList.add(this.CssClasses_.RIPPLE_CONTAINER);\n this.rippleContainerElement_.classList.add(this.CssClasses_.RIPPLE_EFFECT);\n this.rippleContainerElement_.classList.add(this.CssClasses_.RIPPLE_CENTER);\n this.boundRippleMouseUp = this.onMouseUp_.bind(this);\n this.rippleContainerElement_.addEventListener('mouseup', this.boundRippleMouseUp);\n var ripple = document.createElement('span');\n ripple.classList.add(this.CssClasses_.RIPPLE);\n this.rippleContainerElement_.appendChild(ripple);\n this.element_.appendChild(this.rippleContainerElement_);\n }\n this.boundInputOnChange = this.onChange_.bind(this);\n this.boundInputOnFocus = this.onFocus_.bind(this);\n this.boundInputOnBlur = this.onBlur_.bind(this);\n this.boundElementMouseUp = this.onMouseUp_.bind(this);\n this.inputElement_.addEventListener('change', this.boundInputOnChange);\n this.inputElement_.addEventListener('focus', this.boundInputOnFocus);\n this.inputElement_.addEventListener('blur', this.boundInputOnBlur);\n this.element_.addEventListener('mouseup', this.boundElementMouseUp);\n this.updateClasses_();\n this.element_.classList.add(this.CssClasses_.IS_UPGRADED);\n }\n};\n/**\n * Downgrade the component.\n *\n * @private\n */\nMaterialCheckbox.prototype.mdlDowngrade_ = function () {\n if (this.rippleContainerElement_) {\n this.rippleContainerElement_.removeEventListener('mouseup', this.boundRippleMouseUp);\n }\n this.inputElement_.removeEventListener('change', this.boundInputOnChange);\n this.inputElement_.removeEventListener('focus', this.boundInputOnFocus);\n this.inputElement_.removeEventListener('blur', this.boundInputOnBlur);\n this.element_.removeEventListener('mouseup', this.boundElementMouseUp);\n};\n/**\n * Public alias for the downgrade method.\n *\n * @public\n */\nMaterialCheckbox.prototype.mdlDowngrade = MaterialCheckbox.prototype.mdlDowngrade_;\nMaterialCheckbox.prototype['mdlDowngrade'] = MaterialCheckbox.prototype.mdlDowngrade;\n// The component registers itself. It can assume componentHandler is available\n// in the global scope.\ncomponentHandler.register({\n constructor: MaterialCheckbox,\n classAsString: 'MaterialCheckbox',\n cssClass: 'mdl-js-checkbox',\n widget: true\n});\n/**\n * @license\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Class constructor for icon toggle MDL component.\n * Implements MDL component design pattern defined at:\n * https://github.com/jasonmayes/mdl-component-design-pattern\n *\n * @constructor\n * @param {HTMLElement} element The element that will be upgraded.\n */\nvar MaterialIconToggle = function MaterialIconToggle(element) {\n this.element_ = element;\n // Initialize instance.\n this.init();\n};\nwindow['MaterialIconToggle'] = MaterialIconToggle;\n/**\n * Store constants in one place so they can be updated easily.\n *\n * @enum {string | number}\n * @private\n */\nMaterialIconToggle.prototype.Constant_ = { TINY_TIMEOUT: 0.001 };\n/**\n * Store strings for class names defined by this component that are used in\n * JavaScript. This allows us to simply change it in one place should we\n * decide to modify at a later date.\n *\n * @enum {string}\n * @private\n */\nMaterialIconToggle.prototype.CssClasses_ = {\n INPUT: 'mdl-icon-toggle__input',\n JS_RIPPLE_EFFECT: 'mdl-js-ripple-effect',\n RIPPLE_IGNORE_EVENTS: 'mdl-js-ripple-effect--ignore-events',\n RIPPLE_CONTAINER: 'mdl-icon-toggle__ripple-container',\n RIPPLE_CENTER: 'mdl-ripple--center',\n RIPPLE: 'mdl-ripple',\n IS_FOCUSED: 'is-focused',\n IS_DISABLED: 'is-disabled',\n IS_CHECKED: 'is-checked'\n};\n/**\n * Handle change of state.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialIconToggle.prototype.onChange_ = function (event) {\n this.updateClasses_();\n};\n/**\n * Handle focus of element.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialIconToggle.prototype.onFocus_ = function (event) {\n this.element_.classList.add(this.CssClasses_.IS_FOCUSED);\n};\n/**\n * Handle lost focus of element.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialIconToggle.prototype.onBlur_ = function (event) {\n this.element_.classList.remove(this.CssClasses_.IS_FOCUSED);\n};\n/**\n * Handle mouseup.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialIconToggle.prototype.onMouseUp_ = function (event) {\n this.blur_();\n};\n/**\n * Handle class updates.\n *\n * @private\n */\nMaterialIconToggle.prototype.updateClasses_ = function () {\n this.checkDisabled();\n this.checkToggleState();\n};\n/**\n * Add blur.\n *\n * @private\n */\nMaterialIconToggle.prototype.blur_ = function () {\n // TODO: figure out why there's a focus event being fired after our blur,\n // so that we can avoid this hack.\n window.setTimeout(function () {\n this.inputElement_.blur();\n }.bind(this), this.Constant_.TINY_TIMEOUT);\n};\n// Public methods.\n/**\n * Check the inputs toggle state and update display.\n *\n * @public\n */\nMaterialIconToggle.prototype.checkToggleState = function () {\n if (this.inputElement_.checked) {\n this.element_.classList.add(this.CssClasses_.IS_CHECKED);\n } else {\n this.element_.classList.remove(this.CssClasses_.IS_CHECKED);\n }\n};\nMaterialIconToggle.prototype['checkToggleState'] = MaterialIconToggle.prototype.checkToggleState;\n/**\n * Check the inputs disabled state and update display.\n *\n * @public\n */\nMaterialIconToggle.prototype.checkDisabled = function () {\n if (this.inputElement_.disabled) {\n this.element_.classList.add(this.CssClasses_.IS_DISABLED);\n } else {\n this.element_.classList.remove(this.CssClasses_.IS_DISABLED);\n }\n};\nMaterialIconToggle.prototype['checkDisabled'] = MaterialIconToggle.prototype.checkDisabled;\n/**\n * Disable icon toggle.\n *\n * @public\n */\nMaterialIconToggle.prototype.disable = function () {\n this.inputElement_.disabled = true;\n this.updateClasses_();\n};\nMaterialIconToggle.prototype['disable'] = MaterialIconToggle.prototype.disable;\n/**\n * Enable icon toggle.\n *\n * @public\n */\nMaterialIconToggle.prototype.enable = function () {\n this.inputElement_.disabled = false;\n this.updateClasses_();\n};\nMaterialIconToggle.prototype['enable'] = MaterialIconToggle.prototype.enable;\n/**\n * Check icon toggle.\n *\n * @public\n */\nMaterialIconToggle.prototype.check = function () {\n this.inputElement_.checked = true;\n this.updateClasses_();\n};\nMaterialIconToggle.prototype['check'] = MaterialIconToggle.prototype.check;\n/**\n * Uncheck icon toggle.\n *\n * @public\n */\nMaterialIconToggle.prototype.uncheck = function () {\n this.inputElement_.checked = false;\n this.updateClasses_();\n};\nMaterialIconToggle.prototype['uncheck'] = MaterialIconToggle.prototype.uncheck;\n/**\n * Initialize element.\n */\nMaterialIconToggle.prototype.init = function () {\n if (this.element_) {\n this.inputElement_ = this.element_.querySelector('.' + this.CssClasses_.INPUT);\n if (this.element_.classList.contains(this.CssClasses_.JS_RIPPLE_EFFECT)) {\n this.element_.classList.add(this.CssClasses_.RIPPLE_IGNORE_EVENTS);\n this.rippleContainerElement_ = document.createElement('span');\n this.rippleContainerElement_.classList.add(this.CssClasses_.RIPPLE_CONTAINER);\n this.rippleContainerElement_.classList.add(this.CssClasses_.JS_RIPPLE_EFFECT);\n this.rippleContainerElement_.classList.add(this.CssClasses_.RIPPLE_CENTER);\n this.boundRippleMouseUp = this.onMouseUp_.bind(this);\n this.rippleContainerElement_.addEventListener('mouseup', this.boundRippleMouseUp);\n var ripple = document.createElement('span');\n ripple.classList.add(this.CssClasses_.RIPPLE);\n this.rippleContainerElement_.appendChild(ripple);\n this.element_.appendChild(this.rippleContainerElement_);\n }\n this.boundInputOnChange = this.onChange_.bind(this);\n this.boundInputOnFocus = this.onFocus_.bind(this);\n this.boundInputOnBlur = this.onBlur_.bind(this);\n this.boundElementOnMouseUp = this.onMouseUp_.bind(this);\n this.inputElement_.addEventListener('change', this.boundInputOnChange);\n this.inputElement_.addEventListener('focus', this.boundInputOnFocus);\n this.inputElement_.addEventListener('blur', this.boundInputOnBlur);\n this.element_.addEventListener('mouseup', this.boundElementOnMouseUp);\n this.updateClasses_();\n this.element_.classList.add('is-upgraded');\n }\n};\n/**\n * Downgrade the component\n *\n * @private\n */\nMaterialIconToggle.prototype.mdlDowngrade_ = function () {\n if (this.rippleContainerElement_) {\n this.rippleContainerElement_.removeEventListener('mouseup', this.boundRippleMouseUp);\n }\n this.inputElement_.removeEventListener('change', this.boundInputOnChange);\n this.inputElement_.removeEventListener('focus', this.boundInputOnFocus);\n this.inputElement_.removeEventListener('blur', this.boundInputOnBlur);\n this.element_.removeEventListener('mouseup', this.boundElementOnMouseUp);\n};\n/**\n * Public alias for the downgrade method.\n *\n * @public\n */\nMaterialIconToggle.prototype.mdlDowngrade = MaterialIconToggle.prototype.mdlDowngrade_;\nMaterialIconToggle.prototype['mdlDowngrade'] = MaterialIconToggle.prototype.mdlDowngrade;\n// The component registers itself. It can assume componentHandler is available\n// in the global scope.\ncomponentHandler.register({\n constructor: MaterialIconToggle,\n classAsString: 'MaterialIconToggle',\n cssClass: 'mdl-js-icon-toggle',\n widget: true\n});\n/**\n * @license\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Class constructor for dropdown MDL component.\n * Implements MDL component design pattern defined at:\n * https://github.com/jasonmayes/mdl-component-design-pattern\n *\n * @constructor\n * @param {HTMLElement} element The element that will be upgraded.\n */\nvar MaterialMenu = function MaterialMenu(element) {\n this.element_ = element;\n // Initialize instance.\n this.init();\n};\nwindow['MaterialMenu'] = MaterialMenu;\n/**\n * Store constants in one place so they can be updated easily.\n *\n * @enum {string | number}\n * @private\n */\nMaterialMenu.prototype.Constant_ = {\n // Total duration of the menu animation.\n TRANSITION_DURATION_SECONDS: 0.3,\n // The fraction of the total duration we want to use for menu item animations.\n TRANSITION_DURATION_FRACTION: 0.8,\n // How long the menu stays open after choosing an option (so the user can see\n // the ripple).\n CLOSE_TIMEOUT: 150\n};\n/**\n * Keycodes, for code readability.\n *\n * @enum {number}\n * @private\n */\nMaterialMenu.prototype.Keycodes_ = {\n ENTER: 13,\n ESCAPE: 27,\n SPACE: 32,\n UP_ARROW: 38,\n DOWN_ARROW: 40\n};\n/**\n * Store strings for class names defined by this component that are used in\n * JavaScript. This allows us to simply change it in one place should we\n * decide to modify at a later date.\n *\n * @enum {string}\n * @private\n */\nMaterialMenu.prototype.CssClasses_ = {\n CONTAINER: 'mdl-menu__container',\n OUTLINE: 'mdl-menu__outline',\n ITEM: 'mdl-menu__item',\n ITEM_RIPPLE_CONTAINER: 'mdl-menu__item-ripple-container',\n RIPPLE_EFFECT: 'mdl-js-ripple-effect',\n RIPPLE_IGNORE_EVENTS: 'mdl-js-ripple-effect--ignore-events',\n RIPPLE: 'mdl-ripple',\n // Statuses\n IS_UPGRADED: 'is-upgraded',\n IS_VISIBLE: 'is-visible',\n IS_ANIMATING: 'is-animating',\n // Alignment options\n BOTTOM_LEFT: 'mdl-menu--bottom-left',\n // This is the default.\n BOTTOM_RIGHT: 'mdl-menu--bottom-right',\n TOP_LEFT: 'mdl-menu--top-left',\n TOP_RIGHT: 'mdl-menu--top-right',\n UNALIGNED: 'mdl-menu--unaligned'\n};\n/**\n * Initialize element.\n */\nMaterialMenu.prototype.init = function () {\n if (this.element_) {\n // Create container for the menu.\n var container = document.createElement('div');\n container.classList.add(this.CssClasses_.CONTAINER);\n this.element_.parentElement.insertBefore(container, this.element_);\n this.element_.parentElement.removeChild(this.element_);\n container.appendChild(this.element_);\n this.container_ = container;\n // Create outline for the menu (shadow and background).\n var outline = document.createElement('div');\n outline.classList.add(this.CssClasses_.OUTLINE);\n this.outline_ = outline;\n container.insertBefore(outline, this.element_);\n // Find the \"for\" element and bind events to it.\n var forElId = this.element_.getAttribute('for');\n var forEl = null;\n if (forElId) {\n forEl = document.getElementById(forElId);\n if (forEl) {\n this.forElement_ = forEl;\n forEl.addEventListener('click', this.handleForClick_.bind(this));\n forEl.addEventListener('keydown', this.handleForKeyboardEvent_.bind(this));\n }\n }\n var items = this.element_.querySelectorAll('.' + this.CssClasses_.ITEM);\n this.boundItemKeydown_ = this.handleItemKeyboardEvent_.bind(this);\n this.boundItemClick_ = this.handleItemClick_.bind(this);\n for (var i = 0; i < items.length; i++) {\n // Add a listener to each menu item.\n items[i].addEventListener('click', this.boundItemClick_);\n // Add a tab index to each menu item.\n items[i].tabIndex = '-1';\n // Add a keyboard listener to each menu item.\n items[i].addEventListener('keydown', this.boundItemKeydown_);\n }\n // Add ripple classes to each item, if the user has enabled ripples.\n if (this.element_.classList.contains(this.CssClasses_.RIPPLE_EFFECT)) {\n this.element_.classList.add(this.CssClasses_.RIPPLE_IGNORE_EVENTS);\n for (i = 0; i < items.length; i++) {\n var item = items[i];\n var rippleContainer = document.createElement('span');\n rippleContainer.classList.add(this.CssClasses_.ITEM_RIPPLE_CONTAINER);\n var ripple = document.createElement('span');\n ripple.classList.add(this.CssClasses_.RIPPLE);\n rippleContainer.appendChild(ripple);\n item.appendChild(rippleContainer);\n item.classList.add(this.CssClasses_.RIPPLE_EFFECT);\n }\n }\n // Copy alignment classes to the container, so the outline can use them.\n if (this.element_.classList.contains(this.CssClasses_.BOTTOM_LEFT)) {\n this.outline_.classList.add(this.CssClasses_.BOTTOM_LEFT);\n }\n if (this.element_.classList.contains(this.CssClasses_.BOTTOM_RIGHT)) {\n this.outline_.classList.add(this.CssClasses_.BOTTOM_RIGHT);\n }\n if (this.element_.classList.contains(this.CssClasses_.TOP_LEFT)) {\n this.outline_.classList.add(this.CssClasses_.TOP_LEFT);\n }\n if (this.element_.classList.contains(this.CssClasses_.TOP_RIGHT)) {\n this.outline_.classList.add(this.CssClasses_.TOP_RIGHT);\n }\n if (this.element_.classList.contains(this.CssClasses_.UNALIGNED)) {\n this.outline_.classList.add(this.CssClasses_.UNALIGNED);\n }\n container.classList.add(this.CssClasses_.IS_UPGRADED);\n }\n};\n/**\n * Handles a click on the \"for\" element, by positioning the menu and then\n * toggling it.\n *\n * @param {Event} evt The event that fired.\n * @private\n */\nMaterialMenu.prototype.handleForClick_ = function (evt) {\n if (this.element_ && this.forElement_) {\n var rect = this.forElement_.getBoundingClientRect();\n var forRect = this.forElement_.parentElement.getBoundingClientRect();\n if (this.element_.classList.contains(this.CssClasses_.UNALIGNED)) {\n } else if (this.element_.classList.contains(this.CssClasses_.BOTTOM_RIGHT)) {\n // Position below the \"for\" element, aligned to its right.\n this.container_.style.right = forRect.right - rect.right + 'px';\n this.container_.style.top = this.forElement_.offsetTop + this.forElement_.offsetHeight + 'px';\n } else if (this.element_.classList.contains(this.CssClasses_.TOP_LEFT)) {\n // Position above the \"for\" element, aligned to its left.\n this.container_.style.left = this.forElement_.offsetLeft + 'px';\n this.container_.style.bottom = forRect.bottom - rect.top + 'px';\n } else if (this.element_.classList.contains(this.CssClasses_.TOP_RIGHT)) {\n // Position above the \"for\" element, aligned to its right.\n this.container_.style.right = forRect.right - rect.right + 'px';\n this.container_.style.bottom = forRect.bottom - rect.top + 'px';\n } else {\n // Default: position below the \"for\" element, aligned to its left.\n this.container_.style.left = this.forElement_.offsetLeft + 'px';\n this.container_.style.top = this.forElement_.offsetTop + this.forElement_.offsetHeight + 'px';\n }\n }\n this.toggle(evt);\n};\n/**\n * Handles a keyboard event on the \"for\" element.\n *\n * @param {Event} evt The event that fired.\n * @private\n */\nMaterialMenu.prototype.handleForKeyboardEvent_ = function (evt) {\n if (this.element_ && this.container_ && this.forElement_) {\n var items = this.element_.querySelectorAll('.' + this.CssClasses_.ITEM + ':not([disabled])');\n if (items && items.length > 0 && this.container_.classList.contains(this.CssClasses_.IS_VISIBLE)) {\n if (evt.keyCode === this.Keycodes_.UP_ARROW) {\n evt.preventDefault();\n items[items.length - 1].focus();\n } else if (evt.keyCode === this.Keycodes_.DOWN_ARROW) {\n evt.preventDefault();\n items[0].focus();\n }\n }\n }\n};\n/**\n * Handles a keyboard event on an item.\n *\n * @param {Event} evt The event that fired.\n * @private\n */\nMaterialMenu.prototype.handleItemKeyboardEvent_ = function (evt) {\n if (this.element_ && this.container_) {\n var items = this.element_.querySelectorAll('.' + this.CssClasses_.ITEM + ':not([disabled])');\n if (items && items.length > 0 && this.container_.classList.contains(this.CssClasses_.IS_VISIBLE)) {\n var currentIndex = Array.prototype.slice.call(items).indexOf(evt.target);\n if (evt.keyCode === this.Keycodes_.UP_ARROW) {\n evt.preventDefault();\n if (currentIndex > 0) {\n items[currentIndex - 1].focus();\n } else {\n items[items.length - 1].focus();\n }\n } else if (evt.keyCode === this.Keycodes_.DOWN_ARROW) {\n evt.preventDefault();\n if (items.length > currentIndex + 1) {\n items[currentIndex + 1].focus();\n } else {\n items[0].focus();\n }\n } else if (evt.keyCode === this.Keycodes_.SPACE || evt.keyCode === this.Keycodes_.ENTER) {\n evt.preventDefault();\n // Send mousedown and mouseup to trigger ripple.\n var e = new MouseEvent('mousedown');\n evt.target.dispatchEvent(e);\n e = new MouseEvent('mouseup');\n evt.target.dispatchEvent(e);\n // Send click.\n evt.target.click();\n } else if (evt.keyCode === this.Keycodes_.ESCAPE) {\n evt.preventDefault();\n this.hide();\n }\n }\n }\n};\n/**\n * Handles a click event on an item.\n *\n * @param {Event} evt The event that fired.\n * @private\n */\nMaterialMenu.prototype.handleItemClick_ = function (evt) {\n if (evt.target.hasAttribute('disabled')) {\n evt.stopPropagation();\n } else {\n // Wait some time before closing menu, so the user can see the ripple.\n this.closing_ = true;\n window.setTimeout(function (evt) {\n this.hide();\n this.closing_ = false;\n }.bind(this), this.Constant_.CLOSE_TIMEOUT);\n }\n};\n/**\n * Calculates the initial clip (for opening the menu) or final clip (for closing\n * it), and applies it. This allows us to animate from or to the correct point,\n * that is, the point it's aligned to in the \"for\" element.\n *\n * @param {number} height Height of the clip rectangle\n * @param {number} width Width of the clip rectangle\n * @private\n */\nMaterialMenu.prototype.applyClip_ = function (height, width) {\n if (this.element_.classList.contains(this.CssClasses_.UNALIGNED)) {\n // Do not clip.\n this.element_.style.clip = '';\n } else if (this.element_.classList.contains(this.CssClasses_.BOTTOM_RIGHT)) {\n // Clip to the top right corner of the menu.\n this.element_.style.clip = 'rect(0 ' + width + 'px ' + '0 ' + width + 'px)';\n } else if (this.element_.classList.contains(this.CssClasses_.TOP_LEFT)) {\n // Clip to the bottom left corner of the menu.\n this.element_.style.clip = 'rect(' + height + 'px 0 ' + height + 'px 0)';\n } else if (this.element_.classList.contains(this.CssClasses_.TOP_RIGHT)) {\n // Clip to the bottom right corner of the menu.\n this.element_.style.clip = 'rect(' + height + 'px ' + width + 'px ' + height + 'px ' + width + 'px)';\n } else {\n // Default: do not clip (same as clipping to the top left corner).\n this.element_.style.clip = '';\n }\n};\n/**\n * Adds an event listener to clean up after the animation ends.\n *\n * @private\n */\nMaterialMenu.prototype.addAnimationEndListener_ = function () {\n var cleanup = function () {\n this.element_.removeEventListener('transitionend', cleanup);\n this.element_.removeEventListener('webkitTransitionEnd', cleanup);\n this.element_.classList.remove(this.CssClasses_.IS_ANIMATING);\n }.bind(this);\n // Remove animation class once the transition is done.\n this.element_.addEventListener('transitionend', cleanup);\n this.element_.addEventListener('webkitTransitionEnd', cleanup);\n};\n/**\n * Displays the menu.\n *\n * @public\n */\nMaterialMenu.prototype.show = function (evt) {\n if (this.element_ && this.container_ && this.outline_) {\n // Measure the inner element.\n var height = this.element_.getBoundingClientRect().height;\n var width = this.element_.getBoundingClientRect().width;\n // Apply the inner element's size to the container and outline.\n this.container_.style.width = width + 'px';\n this.container_.style.height = height + 'px';\n this.outline_.style.width = width + 'px';\n this.outline_.style.height = height + 'px';\n var transitionDuration = this.Constant_.TRANSITION_DURATION_SECONDS * this.Constant_.TRANSITION_DURATION_FRACTION;\n // Calculate transition delays for individual menu items, so that they fade\n // in one at a time.\n var items = this.element_.querySelectorAll('.' + this.CssClasses_.ITEM);\n for (var i = 0; i < items.length; i++) {\n var itemDelay = null;\n if (this.element_.classList.contains(this.CssClasses_.TOP_LEFT) || this.element_.classList.contains(this.CssClasses_.TOP_RIGHT)) {\n itemDelay = (height - items[i].offsetTop - items[i].offsetHeight) / height * transitionDuration + 's';\n } else {\n itemDelay = items[i].offsetTop / height * transitionDuration + 's';\n }\n items[i].style.transitionDelay = itemDelay;\n }\n // Apply the initial clip to the text before we start animating.\n this.applyClip_(height, width);\n // Wait for the next frame, turn on animation, and apply the final clip.\n // Also make it visible. This triggers the transitions.\n window.requestAnimationFrame(function () {\n this.element_.classList.add(this.CssClasses_.IS_ANIMATING);\n this.element_.style.clip = 'rect(0 ' + width + 'px ' + height + 'px 0)';\n this.container_.classList.add(this.CssClasses_.IS_VISIBLE);\n }.bind(this));\n // Clean up after the animation is complete.\n this.addAnimationEndListener_();\n // Add a click listener to the document, to close the menu.\n var callback = function (e) {\n // Check to see if the document is processing the same event that\n // displayed the menu in the first place. If so, do nothing.\n // Also check to see if the menu is in the process of closing itself, and\n // do nothing in that case.\n // Also check if the clicked element is a menu item\n // if so, do nothing.\n if (e !== evt && !this.closing_ && e.target.parentNode !== this.element_) {\n document.removeEventListener('click', callback);\n this.hide();\n }\n }.bind(this);\n document.addEventListener('click', callback);\n }\n};\nMaterialMenu.prototype['show'] = MaterialMenu.prototype.show;\n/**\n * Hides the menu.\n *\n * @public\n */\nMaterialMenu.prototype.hide = function () {\n if (this.element_ && this.container_ && this.outline_) {\n var items = this.element_.querySelectorAll('.' + this.CssClasses_.ITEM);\n // Remove all transition delays; menu items fade out concurrently.\n for (var i = 0; i < items.length; i++) {\n items[i].style.transitionDelay = null;\n }\n // Measure the inner element.\n var rect = this.element_.getBoundingClientRect();\n var height = rect.height;\n var width = rect.width;\n // Turn on animation, and apply the final clip. Also make invisible.\n // This triggers the transitions.\n this.element_.classList.add(this.CssClasses_.IS_ANIMATING);\n this.applyClip_(height, width);\n this.container_.classList.remove(this.CssClasses_.IS_VISIBLE);\n // Clean up after the animation is complete.\n this.addAnimationEndListener_();\n }\n};\nMaterialMenu.prototype['hide'] = MaterialMenu.prototype.hide;\n/**\n * Displays or hides the menu, depending on current state.\n *\n * @public\n */\nMaterialMenu.prototype.toggle = function (evt) {\n if (this.container_.classList.contains(this.CssClasses_.IS_VISIBLE)) {\n this.hide();\n } else {\n this.show(evt);\n }\n};\nMaterialMenu.prototype['toggle'] = MaterialMenu.prototype.toggle;\n/**\n * Downgrade the component.\n *\n * @private\n */\nMaterialMenu.prototype.mdlDowngrade_ = function () {\n var items = this.element_.querySelectorAll('.' + this.CssClasses_.ITEM);\n for (var i = 0; i < items.length; i++) {\n items[i].removeEventListener('click', this.boundItemClick_);\n items[i].removeEventListener('keydown', this.boundItemKeydown_);\n }\n};\n/**\n * Public alias for the downgrade method.\n *\n * @public\n */\nMaterialMenu.prototype.mdlDowngrade = MaterialMenu.prototype.mdlDowngrade_;\nMaterialMenu.prototype['mdlDowngrade'] = MaterialMenu.prototype.mdlDowngrade;\n// The component registers itself. It can assume componentHandler is available\n// in the global scope.\ncomponentHandler.register({\n constructor: MaterialMenu,\n classAsString: 'MaterialMenu',\n cssClass: 'mdl-js-menu',\n widget: true\n});\n/**\n * @license\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Class constructor for Progress MDL component.\n * Implements MDL component design pattern defined at:\n * https://github.com/jasonmayes/mdl-component-design-pattern\n *\n * @constructor\n * @param {HTMLElement} element The element that will be upgraded.\n */\nvar MaterialProgress = function MaterialProgress(element) {\n this.element_ = element;\n // Initialize instance.\n this.init();\n};\nwindow['MaterialProgress'] = MaterialProgress;\n/**\n * Store constants in one place so they can be updated easily.\n *\n * @enum {string | number}\n * @private\n */\nMaterialProgress.prototype.Constant_ = {};\n/**\n * Store strings for class names defined by this component that are used in\n * JavaScript. This allows us to simply change it in one place should we\n * decide to modify at a later date.\n *\n * @enum {string}\n * @private\n */\nMaterialProgress.prototype.CssClasses_ = { INDETERMINATE_CLASS: 'mdl-progress__indeterminate' };\n/**\n * Set the current progress of the progressbar.\n *\n * @param {number} p Percentage of the progress (0-100)\n * @public\n */\nMaterialProgress.prototype.setProgress = function (p) {\n if (this.element_.classList.contains(this.CssClasses_.INDETERMINATE_CLASS)) {\n return;\n }\n this.progressbar_.style.width = p + '%';\n};\nMaterialProgress.prototype['setProgress'] = MaterialProgress.prototype.setProgress;\n/**\n * Set the current progress of the buffer.\n *\n * @param {number} p Percentage of the buffer (0-100)\n * @public\n */\nMaterialProgress.prototype.setBuffer = function (p) {\n this.bufferbar_.style.width = p + '%';\n this.auxbar_.style.width = 100 - p + '%';\n};\nMaterialProgress.prototype['setBuffer'] = MaterialProgress.prototype.setBuffer;\n/**\n * Initialize element.\n */\nMaterialProgress.prototype.init = function () {\n if (this.element_) {\n var el = document.createElement('div');\n el.className = 'progressbar bar bar1';\n this.element_.appendChild(el);\n this.progressbar_ = el;\n el = document.createElement('div');\n el.className = 'bufferbar bar bar2';\n this.element_.appendChild(el);\n this.bufferbar_ = el;\n el = document.createElement('div');\n el.className = 'auxbar bar bar3';\n this.element_.appendChild(el);\n this.auxbar_ = el;\n this.progressbar_.style.width = '0%';\n this.bufferbar_.style.width = '100%';\n this.auxbar_.style.width = '0%';\n this.element_.classList.add('is-upgraded');\n }\n};\n/**\n * Downgrade the component\n *\n * @private\n */\nMaterialProgress.prototype.mdlDowngrade_ = function () {\n while (this.element_.firstChild) {\n this.element_.removeChild(this.element_.firstChild);\n }\n};\n/**\n * Public alias for the downgrade method.\n *\n * @public\n */\nMaterialProgress.prototype.mdlDowngrade = MaterialProgress.prototype.mdlDowngrade_;\nMaterialProgress.prototype['mdlDowngrade'] = MaterialProgress.prototype.mdlDowngrade;\n// The component registers itself. It can assume componentHandler is available\n// in the global scope.\ncomponentHandler.register({\n constructor: MaterialProgress,\n classAsString: 'MaterialProgress',\n cssClass: 'mdl-js-progress',\n widget: true\n});\n/**\n * @license\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Class constructor for Radio MDL component.\n * Implements MDL component design pattern defined at:\n * https://github.com/jasonmayes/mdl-component-design-pattern\n *\n * @constructor\n * @param {HTMLElement} element The element that will be upgraded.\n */\nvar MaterialRadio = function MaterialRadio(element) {\n this.element_ = element;\n // Initialize instance.\n this.init();\n};\nwindow['MaterialRadio'] = MaterialRadio;\n/**\n * Store constants in one place so they can be updated easily.\n *\n * @enum {string | number}\n * @private\n */\nMaterialRadio.prototype.Constant_ = { TINY_TIMEOUT: 0.001 };\n/**\n * Store strings for class names defined by this component that are used in\n * JavaScript. This allows us to simply change it in one place should we\n * decide to modify at a later date.\n *\n * @enum {string}\n * @private\n */\nMaterialRadio.prototype.CssClasses_ = {\n IS_FOCUSED: 'is-focused',\n IS_DISABLED: 'is-disabled',\n IS_CHECKED: 'is-checked',\n IS_UPGRADED: 'is-upgraded',\n JS_RADIO: 'mdl-js-radio',\n RADIO_BTN: 'mdl-radio__button',\n RADIO_OUTER_CIRCLE: 'mdl-radio__outer-circle',\n RADIO_INNER_CIRCLE: 'mdl-radio__inner-circle',\n RIPPLE_EFFECT: 'mdl-js-ripple-effect',\n RIPPLE_IGNORE_EVENTS: 'mdl-js-ripple-effect--ignore-events',\n RIPPLE_CONTAINER: 'mdl-radio__ripple-container',\n RIPPLE_CENTER: 'mdl-ripple--center',\n RIPPLE: 'mdl-ripple'\n};\n/**\n * Handle change of state.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialRadio.prototype.onChange_ = function (event) {\n // Since other radio buttons don't get change events, we need to look for\n // them to update their classes.\n var radios = document.getElementsByClassName(this.CssClasses_.JS_RADIO);\n for (var i = 0; i < radios.length; i++) {\n var button = radios[i].querySelector('.' + this.CssClasses_.RADIO_BTN);\n // Different name == different group, so no point updating those.\n if (button.getAttribute('name') === this.btnElement_.getAttribute('name')) {\n radios[i]['MaterialRadio'].updateClasses_();\n }\n }\n};\n/**\n * Handle focus.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialRadio.prototype.onFocus_ = function (event) {\n this.element_.classList.add(this.CssClasses_.IS_FOCUSED);\n};\n/**\n * Handle lost focus.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialRadio.prototype.onBlur_ = function (event) {\n this.element_.classList.remove(this.CssClasses_.IS_FOCUSED);\n};\n/**\n * Handle mouseup.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialRadio.prototype.onMouseup_ = function (event) {\n this.blur_();\n};\n/**\n * Update classes.\n *\n * @private\n */\nMaterialRadio.prototype.updateClasses_ = function () {\n this.checkDisabled();\n this.checkToggleState();\n};\n/**\n * Add blur.\n *\n * @private\n */\nMaterialRadio.prototype.blur_ = function () {\n // TODO: figure out why there's a focus event being fired after our blur,\n // so that we can avoid this hack.\n window.setTimeout(function () {\n this.btnElement_.blur();\n }.bind(this), this.Constant_.TINY_TIMEOUT);\n};\n// Public methods.\n/**\n * Check the components disabled state.\n *\n * @public\n */\nMaterialRadio.prototype.checkDisabled = function () {\n if (this.btnElement_.disabled) {\n this.element_.classList.add(this.CssClasses_.IS_DISABLED);\n } else {\n this.element_.classList.remove(this.CssClasses_.IS_DISABLED);\n }\n};\nMaterialRadio.prototype['checkDisabled'] = MaterialRadio.prototype.checkDisabled;\n/**\n * Check the components toggled state.\n *\n * @public\n */\nMaterialRadio.prototype.checkToggleState = function () {\n if (this.btnElement_.checked) {\n this.element_.classList.add(this.CssClasses_.IS_CHECKED);\n } else {\n this.element_.classList.remove(this.CssClasses_.IS_CHECKED);\n }\n};\nMaterialRadio.prototype['checkToggleState'] = MaterialRadio.prototype.checkToggleState;\n/**\n * Disable radio.\n *\n * @public\n */\nMaterialRadio.prototype.disable = function () {\n this.btnElement_.disabled = true;\n this.updateClasses_();\n};\nMaterialRadio.prototype['disable'] = MaterialRadio.prototype.disable;\n/**\n * Enable radio.\n *\n * @public\n */\nMaterialRadio.prototype.enable = function () {\n this.btnElement_.disabled = false;\n this.updateClasses_();\n};\nMaterialRadio.prototype['enable'] = MaterialRadio.prototype.enable;\n/**\n * Check radio.\n *\n * @public\n */\nMaterialRadio.prototype.check = function () {\n this.btnElement_.checked = true;\n this.updateClasses_();\n};\nMaterialRadio.prototype['check'] = MaterialRadio.prototype.check;\n/**\n * Uncheck radio.\n *\n * @public\n */\nMaterialRadio.prototype.uncheck = function () {\n this.btnElement_.checked = false;\n this.updateClasses_();\n};\nMaterialRadio.prototype['uncheck'] = MaterialRadio.prototype.uncheck;\n/**\n * Initialize element.\n */\nMaterialRadio.prototype.init = function () {\n if (this.element_) {\n this.btnElement_ = this.element_.querySelector('.' + this.CssClasses_.RADIO_BTN);\n this.boundChangeHandler_ = this.onChange_.bind(this);\n this.boundFocusHandler_ = this.onChange_.bind(this);\n this.boundBlurHandler_ = this.onBlur_.bind(this);\n this.boundMouseUpHandler_ = this.onMouseup_.bind(this);\n var outerCircle = document.createElement('span');\n outerCircle.classList.add(this.CssClasses_.RADIO_OUTER_CIRCLE);\n var innerCircle = document.createElement('span');\n innerCircle.classList.add(this.CssClasses_.RADIO_INNER_CIRCLE);\n this.element_.appendChild(outerCircle);\n this.element_.appendChild(innerCircle);\n var rippleContainer;\n if (this.element_.classList.contains(this.CssClasses_.RIPPLE_EFFECT)) {\n this.element_.classList.add(this.CssClasses_.RIPPLE_IGNORE_EVENTS);\n rippleContainer = document.createElement('span');\n rippleContainer.classList.add(this.CssClasses_.RIPPLE_CONTAINER);\n rippleContainer.classList.add(this.CssClasses_.RIPPLE_EFFECT);\n rippleContainer.classList.add(this.CssClasses_.RIPPLE_CENTER);\n rippleContainer.addEventListener('mouseup', this.boundMouseUpHandler_);\n var ripple = document.createElement('span');\n ripple.classList.add(this.CssClasses_.RIPPLE);\n rippleContainer.appendChild(ripple);\n this.element_.appendChild(rippleContainer);\n }\n this.btnElement_.addEventListener('change', this.boundChangeHandler_);\n this.btnElement_.addEventListener('focus', this.boundFocusHandler_);\n this.btnElement_.addEventListener('blur', this.boundBlurHandler_);\n this.element_.addEventListener('mouseup', this.boundMouseUpHandler_);\n this.updateClasses_();\n this.element_.classList.add(this.CssClasses_.IS_UPGRADED);\n }\n};\n/**\n * Downgrade the element.\n *\n * @private\n */\nMaterialRadio.prototype.mdlDowngrade_ = function () {\n var rippleContainer = this.element_.querySelector('.' + this.CssClasses_.RIPPLE_CONTAINER);\n this.btnElement_.removeEventListener('change', this.boundChangeHandler_);\n this.btnElement_.removeEventListener('focus', this.boundFocusHandler_);\n this.btnElement_.removeEventListener('blur', this.boundBlurHandler_);\n this.element_.removeEventListener('mouseup', this.boundMouseUpHandler_);\n if (rippleContainer) {\n rippleContainer.removeEventListener('mouseup', this.boundMouseUpHandler_);\n this.element_.removeChild(rippleContainer);\n }\n};\n/**\n * Public alias for the downgrade method.\n *\n * @public\n */\nMaterialRadio.prototype.mdlDowngrade = MaterialRadio.prototype.mdlDowngrade_;\nMaterialRadio.prototype['mdlDowngrade'] = MaterialRadio.prototype.mdlDowngrade;\n// The component registers itself. It can assume componentHandler is available\n// in the global scope.\ncomponentHandler.register({\n constructor: MaterialRadio,\n classAsString: 'MaterialRadio',\n cssClass: 'mdl-js-radio',\n widget: true\n});\n/**\n * @license\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Class constructor for Slider MDL component.\n * Implements MDL component design pattern defined at:\n * https://github.com/jasonmayes/mdl-component-design-pattern\n *\n * @constructor\n * @param {HTMLElement} element The element that will be upgraded.\n */\nvar MaterialSlider = function MaterialSlider(element) {\n this.element_ = element;\n // Browser feature detection.\n this.isIE_ = window.navigator.msPointerEnabled;\n // Initialize instance.\n this.init();\n};\nwindow['MaterialSlider'] = MaterialSlider;\n/**\n * Store constants in one place so they can be updated easily.\n *\n * @enum {string | number}\n * @private\n */\nMaterialSlider.prototype.Constant_ = {};\n/**\n * Store strings for class names defined by this component that are used in\n * JavaScript. This allows us to simply change it in one place should we\n * decide to modify at a later date.\n *\n * @enum {string}\n * @private\n */\nMaterialSlider.prototype.CssClasses_ = {\n IE_CONTAINER: 'mdl-slider__ie-container',\n SLIDER_CONTAINER: 'mdl-slider__container',\n BACKGROUND_FLEX: 'mdl-slider__background-flex',\n BACKGROUND_LOWER: 'mdl-slider__background-lower',\n BACKGROUND_UPPER: 'mdl-slider__background-upper',\n IS_LOWEST_VALUE: 'is-lowest-value',\n IS_UPGRADED: 'is-upgraded'\n};\n/**\n * Handle input on element.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialSlider.prototype.onInput_ = function (event) {\n this.updateValueStyles_();\n};\n/**\n * Handle change on element.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialSlider.prototype.onChange_ = function (event) {\n this.updateValueStyles_();\n};\n/**\n * Handle mouseup on element.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialSlider.prototype.onMouseUp_ = function (event) {\n event.target.blur();\n};\n/**\n * Handle mousedown on container element.\n * This handler is purpose is to not require the use to click\n * exactly on the 2px slider element, as FireFox seems to be very\n * strict about this.\n *\n * @param {Event} event The event that fired.\n * @private\n * @suppress {missingProperties}\n */\nMaterialSlider.prototype.onContainerMouseDown_ = function (event) {\n // If this click is not on the parent element (but rather some child)\n // ignore. It may still bubble up.\n if (event.target !== this.element_.parentElement) {\n return;\n }\n // Discard the original event and create a new event that\n // is on the slider element.\n event.preventDefault();\n var newEvent = new MouseEvent('mousedown', {\n target: event.target,\n buttons: event.buttons,\n clientX: event.clientX,\n clientY: this.element_.getBoundingClientRect().y\n });\n this.element_.dispatchEvent(newEvent);\n};\n/**\n * Handle updating of values.\n *\n * @private\n */\nMaterialSlider.prototype.updateValueStyles_ = function () {\n // Calculate and apply percentages to div structure behind slider.\n var fraction = (this.element_.value - this.element_.min) / (this.element_.max - this.element_.min);\n if (fraction === 0) {\n this.element_.classList.add(this.CssClasses_.IS_LOWEST_VALUE);\n } else {\n this.element_.classList.remove(this.CssClasses_.IS_LOWEST_VALUE);\n }\n if (!this.isIE_) {\n this.backgroundLower_.style.flex = fraction;\n this.backgroundLower_.style.webkitFlex = fraction;\n this.backgroundUpper_.style.flex = 1 - fraction;\n this.backgroundUpper_.style.webkitFlex = 1 - fraction;\n }\n};\n// Public methods.\n/**\n * Disable slider.\n *\n * @public\n */\nMaterialSlider.prototype.disable = function () {\n this.element_.disabled = true;\n};\nMaterialSlider.prototype['disable'] = MaterialSlider.prototype.disable;\n/**\n * Enable slider.\n *\n * @public\n */\nMaterialSlider.prototype.enable = function () {\n this.element_.disabled = false;\n};\nMaterialSlider.prototype['enable'] = MaterialSlider.prototype.enable;\n/**\n * Update slider value.\n *\n * @param {number} value The value to which to set the control (optional).\n * @public\n */\nMaterialSlider.prototype.change = function (value) {\n if (typeof value !== 'undefined') {\n this.element_.value = value;\n }\n this.updateValueStyles_();\n};\nMaterialSlider.prototype['change'] = MaterialSlider.prototype.change;\n/**\n * Initialize element.\n */\nMaterialSlider.prototype.init = function () {\n if (this.element_) {\n if (this.isIE_) {\n // Since we need to specify a very large height in IE due to\n // implementation limitations, we add a parent here that trims it down to\n // a reasonable size.\n var containerIE = document.createElement('div');\n containerIE.classList.add(this.CssClasses_.IE_CONTAINER);\n this.element_.parentElement.insertBefore(containerIE, this.element_);\n this.element_.parentElement.removeChild(this.element_);\n containerIE.appendChild(this.element_);\n } else {\n // For non-IE browsers, we need a div structure that sits behind the\n // slider and allows us to style the left and right sides of it with\n // different colors.\n var container = document.createElement('div');\n container.classList.add(this.CssClasses_.SLIDER_CONTAINER);\n this.element_.parentElement.insertBefore(container, this.element_);\n this.element_.parentElement.removeChild(this.element_);\n container.appendChild(this.element_);\n var backgroundFlex = document.createElement('div');\n backgroundFlex.classList.add(this.CssClasses_.BACKGROUND_FLEX);\n container.appendChild(backgroundFlex);\n this.backgroundLower_ = document.createElement('div');\n this.backgroundLower_.classList.add(this.CssClasses_.BACKGROUND_LOWER);\n backgroundFlex.appendChild(this.backgroundLower_);\n this.backgroundUpper_ = document.createElement('div');\n this.backgroundUpper_.classList.add(this.CssClasses_.BACKGROUND_UPPER);\n backgroundFlex.appendChild(this.backgroundUpper_);\n }\n this.boundInputHandler = this.onInput_.bind(this);\n this.boundChangeHandler = this.onChange_.bind(this);\n this.boundMouseUpHandler = this.onMouseUp_.bind(this);\n this.boundContainerMouseDownHandler = this.onContainerMouseDown_.bind(this);\n this.element_.addEventListener('input', this.boundInputHandler);\n this.element_.addEventListener('change', this.boundChangeHandler);\n this.element_.addEventListener('mouseup', this.boundMouseUpHandler);\n this.element_.parentElement.addEventListener('mousedown', this.boundContainerMouseDownHandler);\n this.updateValueStyles_();\n this.element_.classList.add(this.CssClasses_.IS_UPGRADED);\n }\n};\n/**\n * Downgrade the component\n *\n * @private\n */\nMaterialSlider.prototype.mdlDowngrade_ = function () {\n this.element_.removeEventListener('input', this.boundInputHandler);\n this.element_.removeEventListener('change', this.boundChangeHandler);\n this.element_.removeEventListener('mouseup', this.boundMouseUpHandler);\n this.element_.parentElement.removeEventListener('mousedown', this.boundContainerMouseDownHandler);\n};\n/**\n * Public alias for the downgrade method.\n *\n * @public\n */\nMaterialSlider.prototype.mdlDowngrade = MaterialSlider.prototype.mdlDowngrade_;\nMaterialSlider.prototype['mdlDowngrade'] = MaterialSlider.prototype.mdlDowngrade;\n// The component registers itself. It can assume componentHandler is available\n// in the global scope.\ncomponentHandler.register({\n constructor: MaterialSlider,\n classAsString: 'MaterialSlider',\n cssClass: 'mdl-js-slider',\n widget: true\n});\n/**\n * @license\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Class constructor for Spinner MDL component.\n * Implements MDL component design pattern defined at:\n * https://github.com/jasonmayes/mdl-component-design-pattern\n *\n * @param {HTMLElement} element The element that will be upgraded.\n * @constructor\n */\nvar MaterialSpinner = function MaterialSpinner(element) {\n this.element_ = element;\n // Initialize instance.\n this.init();\n};\nwindow['MaterialSpinner'] = MaterialSpinner;\n/**\n * Store constants in one place so they can be updated easily.\n *\n * @enum {string | number}\n * @private\n */\nMaterialSpinner.prototype.Constant_ = { MDL_SPINNER_LAYER_COUNT: 4 };\n/**\n * Store strings for class names defined by this component that are used in\n * JavaScript. This allows us to simply change it in one place should we\n * decide to modify at a later date.\n *\n * @enum {string}\n * @private\n */\nMaterialSpinner.prototype.CssClasses_ = {\n MDL_SPINNER_LAYER: 'mdl-spinner__layer',\n MDL_SPINNER_CIRCLE_CLIPPER: 'mdl-spinner__circle-clipper',\n MDL_SPINNER_CIRCLE: 'mdl-spinner__circle',\n MDL_SPINNER_GAP_PATCH: 'mdl-spinner__gap-patch',\n MDL_SPINNER_LEFT: 'mdl-spinner__left',\n MDL_SPINNER_RIGHT: 'mdl-spinner__right'\n};\n/**\n * Auxiliary method to create a spinner layer.\n *\n * @param {number} index Index of the layer to be created.\n * @public\n */\nMaterialSpinner.prototype.createLayer = function (index) {\n var layer = document.createElement('div');\n layer.classList.add(this.CssClasses_.MDL_SPINNER_LAYER);\n layer.classList.add(this.CssClasses_.MDL_SPINNER_LAYER + '-' + index);\n var leftClipper = document.createElement('div');\n leftClipper.classList.add(this.CssClasses_.MDL_SPINNER_CIRCLE_CLIPPER);\n leftClipper.classList.add(this.CssClasses_.MDL_SPINNER_LEFT);\n var gapPatch = document.createElement('div');\n gapPatch.classList.add(this.CssClasses_.MDL_SPINNER_GAP_PATCH);\n var rightClipper = document.createElement('div');\n rightClipper.classList.add(this.CssClasses_.MDL_SPINNER_CIRCLE_CLIPPER);\n rightClipper.classList.add(this.CssClasses_.MDL_SPINNER_RIGHT);\n var circleOwners = [\n leftClipper,\n gapPatch,\n rightClipper\n ];\n for (var i = 0; i < circleOwners.length; i++) {\n var circle = document.createElement('div');\n circle.classList.add(this.CssClasses_.MDL_SPINNER_CIRCLE);\n circleOwners[i].appendChild(circle);\n }\n layer.appendChild(leftClipper);\n layer.appendChild(gapPatch);\n layer.appendChild(rightClipper);\n this.element_.appendChild(layer);\n};\nMaterialSpinner.prototype['createLayer'] = MaterialSpinner.prototype.createLayer;\n/**\n * Stops the spinner animation.\n * Public method for users who need to stop the spinner for any reason.\n *\n * @public\n */\nMaterialSpinner.prototype.stop = function () {\n this.element_.classList.remove('is-active');\n};\nMaterialSpinner.prototype['stop'] = MaterialSpinner.prototype.stop;\n/**\n * Starts the spinner animation.\n * Public method for users who need to manually start the spinner for any reason\n * (instead of just adding the 'is-active' class to their markup).\n *\n * @public\n */\nMaterialSpinner.prototype.start = function () {\n this.element_.classList.add('is-active');\n};\nMaterialSpinner.prototype['start'] = MaterialSpinner.prototype.start;\n/**\n * Initialize element.\n */\nMaterialSpinner.prototype.init = function () {\n if (this.element_) {\n for (var i = 1; i <= this.Constant_.MDL_SPINNER_LAYER_COUNT; i++) {\n this.createLayer(i);\n }\n this.element_.classList.add('is-upgraded');\n }\n};\n// The component registers itself. It can assume componentHandler is available\n// in the global scope.\ncomponentHandler.register({\n constructor: MaterialSpinner,\n classAsString: 'MaterialSpinner',\n cssClass: 'mdl-js-spinner',\n widget: true\n});\n/**\n * @license\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Class constructor for Checkbox MDL component.\n * Implements MDL component design pattern defined at:\n * https://github.com/jasonmayes/mdl-component-design-pattern\n *\n * @constructor\n * @param {HTMLElement} element The element that will be upgraded.\n */\nvar MaterialSwitch = function MaterialSwitch(element) {\n this.element_ = element;\n // Initialize instance.\n this.init();\n};\nwindow['MaterialSwitch'] = MaterialSwitch;\n/**\n * Store constants in one place so they can be updated easily.\n *\n * @enum {string | number}\n * @private\n */\nMaterialSwitch.prototype.Constant_ = { TINY_TIMEOUT: 0.001 };\n/**\n * Store strings for class names defined by this component that are used in\n * JavaScript. This allows us to simply change it in one place should we\n * decide to modify at a later date.\n *\n * @enum {string}\n * @private\n */\nMaterialSwitch.prototype.CssClasses_ = {\n INPUT: 'mdl-switch__input',\n TRACK: 'mdl-switch__track',\n THUMB: 'mdl-switch__thumb',\n FOCUS_HELPER: 'mdl-switch__focus-helper',\n RIPPLE_EFFECT: 'mdl-js-ripple-effect',\n RIPPLE_IGNORE_EVENTS: 'mdl-js-ripple-effect--ignore-events',\n RIPPLE_CONTAINER: 'mdl-switch__ripple-container',\n RIPPLE_CENTER: 'mdl-ripple--center',\n RIPPLE: 'mdl-ripple',\n IS_FOCUSED: 'is-focused',\n IS_DISABLED: 'is-disabled',\n IS_CHECKED: 'is-checked'\n};\n/**\n * Handle change of state.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialSwitch.prototype.onChange_ = function (event) {\n this.updateClasses_();\n};\n/**\n * Handle focus of element.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialSwitch.prototype.onFocus_ = function (event) {\n this.element_.classList.add(this.CssClasses_.IS_FOCUSED);\n};\n/**\n * Handle lost focus of element.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialSwitch.prototype.onBlur_ = function (event) {\n this.element_.classList.remove(this.CssClasses_.IS_FOCUSED);\n};\n/**\n * Handle mouseup.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialSwitch.prototype.onMouseUp_ = function (event) {\n this.blur_();\n};\n/**\n * Handle class updates.\n *\n * @private\n */\nMaterialSwitch.prototype.updateClasses_ = function () {\n this.checkDisabled();\n this.checkToggleState();\n};\n/**\n * Add blur.\n *\n * @private\n */\nMaterialSwitch.prototype.blur_ = function () {\n // TODO: figure out why there's a focus event being fired after our blur,\n // so that we can avoid this hack.\n window.setTimeout(function () {\n this.inputElement_.blur();\n }.bind(this), this.Constant_.TINY_TIMEOUT);\n};\n// Public methods.\n/**\n * Check the components disabled state.\n *\n * @public\n */\nMaterialSwitch.prototype.checkDisabled = function () {\n if (this.inputElement_.disabled) {\n this.element_.classList.add(this.CssClasses_.IS_DISABLED);\n } else {\n this.element_.classList.remove(this.CssClasses_.IS_DISABLED);\n }\n};\nMaterialSwitch.prototype['checkDisabled'] = MaterialSwitch.prototype.checkDisabled;\n/**\n * Check the components toggled state.\n *\n * @public\n */\nMaterialSwitch.prototype.checkToggleState = function () {\n if (this.inputElement_.checked) {\n this.element_.classList.add(this.CssClasses_.IS_CHECKED);\n } else {\n this.element_.classList.remove(this.CssClasses_.IS_CHECKED);\n }\n};\nMaterialSwitch.prototype['checkToggleState'] = MaterialSwitch.prototype.checkToggleState;\n/**\n * Disable switch.\n *\n * @public\n */\nMaterialSwitch.prototype.disable = function () {\n this.inputElement_.disabled = true;\n this.updateClasses_();\n};\nMaterialSwitch.prototype['disable'] = MaterialSwitch.prototype.disable;\n/**\n * Enable switch.\n *\n * @public\n */\nMaterialSwitch.prototype.enable = function () {\n this.inputElement_.disabled = false;\n this.updateClasses_();\n};\nMaterialSwitch.prototype['enable'] = MaterialSwitch.prototype.enable;\n/**\n * Activate switch.\n *\n * @public\n */\nMaterialSwitch.prototype.on = function () {\n this.inputElement_.checked = true;\n this.updateClasses_();\n};\nMaterialSwitch.prototype['on'] = MaterialSwitch.prototype.on;\n/**\n * Deactivate switch.\n *\n * @public\n */\nMaterialSwitch.prototype.off = function () {\n this.inputElement_.checked = false;\n this.updateClasses_();\n};\nMaterialSwitch.prototype['off'] = MaterialSwitch.prototype.off;\n/**\n * Initialize element.\n */\nMaterialSwitch.prototype.init = function () {\n if (this.element_) {\n this.inputElement_ = this.element_.querySelector('.' + this.CssClasses_.INPUT);\n var track = document.createElement('div');\n track.classList.add(this.CssClasses_.TRACK);\n var thumb = document.createElement('div');\n thumb.classList.add(this.CssClasses_.THUMB);\n var focusHelper = document.createElement('span');\n focusHelper.classList.add(this.CssClasses_.FOCUS_HELPER);\n thumb.appendChild(focusHelper);\n this.element_.appendChild(track);\n this.element_.appendChild(thumb);\n this.boundMouseUpHandler = this.onMouseUp_.bind(this);\n if (this.element_.classList.contains(this.CssClasses_.RIPPLE_EFFECT)) {\n this.element_.classList.add(this.CssClasses_.RIPPLE_IGNORE_EVENTS);\n this.rippleContainerElement_ = document.createElement('span');\n this.rippleContainerElement_.classList.add(this.CssClasses_.RIPPLE_CONTAINER);\n this.rippleContainerElement_.classList.add(this.CssClasses_.RIPPLE_EFFECT);\n this.rippleContainerElement_.classList.add(this.CssClasses_.RIPPLE_CENTER);\n this.rippleContainerElement_.addEventListener('mouseup', this.boundMouseUpHandler);\n var ripple = document.createElement('span');\n ripple.classList.add(this.CssClasses_.RIPPLE);\n this.rippleContainerElement_.appendChild(ripple);\n this.element_.appendChild(this.rippleContainerElement_);\n }\n this.boundChangeHandler = this.onChange_.bind(this);\n this.boundFocusHandler = this.onFocus_.bind(this);\n this.boundBlurHandler = this.onBlur_.bind(this);\n this.inputElement_.addEventListener('change', this.boundChangeHandler);\n this.inputElement_.addEventListener('focus', this.boundFocusHandler);\n this.inputElement_.addEventListener('blur', this.boundBlurHandler);\n this.element_.addEventListener('mouseup', this.boundMouseUpHandler);\n this.updateClasses_();\n this.element_.classList.add('is-upgraded');\n }\n};\n/**\n * Downgrade the component.\n *\n * @private\n */\nMaterialSwitch.prototype.mdlDowngrade_ = function () {\n if (this.rippleContainerElement_) {\n this.rippleContainerElement_.removeEventListener('mouseup', this.boundMouseUpHandler);\n }\n this.inputElement_.removeEventListener('change', this.boundChangeHandler);\n this.inputElement_.removeEventListener('focus', this.boundFocusHandler);\n this.inputElement_.removeEventListener('blur', this.boundBlurHandler);\n this.element_.removeEventListener('mouseup', this.boundMouseUpHandler);\n};\n/**\n * Public alias for the downgrade method.\n *\n * @public\n */\nMaterialSwitch.prototype.mdlDowngrade = MaterialSwitch.prototype.mdlDowngrade_;\nMaterialSwitch.prototype['mdlDowngrade'] = MaterialSwitch.prototype.mdlDowngrade;\n// The component registers itself. It can assume componentHandler is available\n// in the global scope.\ncomponentHandler.register({\n constructor: MaterialSwitch,\n classAsString: 'MaterialSwitch',\n cssClass: 'mdl-js-switch',\n widget: true\n});\n/**\n * @license\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Class constructor for Tabs MDL component.\n * Implements MDL component design pattern defined at:\n * https://github.com/jasonmayes/mdl-component-design-pattern\n *\n * @constructor\n * @param {HTMLElement} element The element that will be upgraded.\n */\nvar MaterialTabs = function MaterialTabs(element) {\n // Stores the HTML element.\n this.element_ = element;\n // Initialize instance.\n this.init();\n};\nwindow['MaterialTabs'] = MaterialTabs;\n/**\n * Store constants in one place so they can be updated easily.\n *\n * @enum {string}\n * @private\n */\nMaterialTabs.prototype.Constant_ = {};\n/**\n * Store strings for class names defined by this component that are used in\n * JavaScript. This allows us to simply change it in one place should we\n * decide to modify at a later date.\n *\n * @enum {string}\n * @private\n */\nMaterialTabs.prototype.CssClasses_ = {\n TAB_CLASS: 'mdl-tabs__tab',\n PANEL_CLASS: 'mdl-tabs__panel',\n ACTIVE_CLASS: 'is-active',\n UPGRADED_CLASS: 'is-upgraded',\n MDL_JS_RIPPLE_EFFECT: 'mdl-js-ripple-effect',\n MDL_RIPPLE_CONTAINER: 'mdl-tabs__ripple-container',\n MDL_RIPPLE: 'mdl-ripple',\n MDL_JS_RIPPLE_EFFECT_IGNORE_EVENTS: 'mdl-js-ripple-effect--ignore-events'\n};\n/**\n * Handle clicks to a tabs component\n *\n * @private\n */\nMaterialTabs.prototype.initTabs_ = function () {\n if (this.element_.classList.contains(this.CssClasses_.MDL_JS_RIPPLE_EFFECT)) {\n this.element_.classList.add(this.CssClasses_.MDL_JS_RIPPLE_EFFECT_IGNORE_EVENTS);\n }\n // Select element tabs, document panels\n this.tabs_ = this.element_.querySelectorAll('.' + this.CssClasses_.TAB_CLASS);\n this.panels_ = this.element_.querySelectorAll('.' + this.CssClasses_.PANEL_CLASS);\n // Create new tabs for each tab element\n for (var i = 0; i < this.tabs_.length; i++) {\n new MaterialTab(this.tabs_[i], this);\n }\n this.element_.classList.add(this.CssClasses_.UPGRADED_CLASS);\n};\n/**\n * Reset tab state, dropping active classes\n *\n * @private\n */\nMaterialTabs.prototype.resetTabState_ = function () {\n for (var k = 0; k < this.tabs_.length; k++) {\n this.tabs_[k].classList.remove(this.CssClasses_.ACTIVE_CLASS);\n }\n};\n/**\n * Reset panel state, droping active classes\n *\n * @private\n */\nMaterialTabs.prototype.resetPanelState_ = function () {\n for (var j = 0; j < this.panels_.length; j++) {\n this.panels_[j].classList.remove(this.CssClasses_.ACTIVE_CLASS);\n }\n};\n/**\n * Initialize element.\n */\nMaterialTabs.prototype.init = function () {\n if (this.element_) {\n this.initTabs_();\n }\n};\n/**\n * Constructor for an individual tab.\n *\n * @constructor\n * @param {HTMLElement} tab The HTML element for the tab.\n * @param {MaterialTabs} ctx The MaterialTabs object that owns the tab.\n */\nfunction MaterialTab(tab, ctx) {\n if (tab) {\n if (ctx.element_.classList.contains(ctx.CssClasses_.MDL_JS_RIPPLE_EFFECT)) {\n var rippleContainer = document.createElement('span');\n rippleContainer.classList.add(ctx.CssClasses_.MDL_RIPPLE_CONTAINER);\n rippleContainer.classList.add(ctx.CssClasses_.MDL_JS_RIPPLE_EFFECT);\n var ripple = document.createElement('span');\n ripple.classList.add(ctx.CssClasses_.MDL_RIPPLE);\n rippleContainer.appendChild(ripple);\n tab.appendChild(rippleContainer);\n }\n tab.addEventListener('click', function (e) {\n e.preventDefault();\n var href = tab.href.split('#')[1];\n var panel = ctx.element_.querySelector('#' + href);\n ctx.resetTabState_();\n ctx.resetPanelState_();\n tab.classList.add(ctx.CssClasses_.ACTIVE_CLASS);\n panel.classList.add(ctx.CssClasses_.ACTIVE_CLASS);\n });\n }\n}\n// The component registers itself. It can assume componentHandler is available\n// in the global scope.\ncomponentHandler.register({\n constructor: MaterialTabs,\n classAsString: 'MaterialTabs',\n cssClass: 'mdl-js-tabs'\n});\n/**\n * @license\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Class constructor for Textfield MDL component.\n * Implements MDL component design pattern defined at:\n * https://github.com/jasonmayes/mdl-component-design-pattern\n *\n * @constructor\n * @param {HTMLElement} element The element that will be upgraded.\n */\nvar MaterialTextfield = function MaterialTextfield(element) {\n this.element_ = element;\n this.maxRows = this.Constant_.NO_MAX_ROWS;\n // Initialize instance.\n this.init();\n};\nwindow['MaterialTextfield'] = MaterialTextfield;\n/**\n * Store constants in one place so they can be updated easily.\n *\n * @enum {string | number}\n * @private\n */\nMaterialTextfield.prototype.Constant_ = {\n NO_MAX_ROWS: -1,\n MAX_ROWS_ATTRIBUTE: 'maxrows'\n};\n/**\n * Store strings for class names defined by this component that are used in\n * JavaScript. This allows us to simply change it in one place should we\n * decide to modify at a later date.\n *\n * @enum {string}\n * @private\n */\nMaterialTextfield.prototype.CssClasses_ = {\n LABEL: 'mdl-textfield__label',\n INPUT: 'mdl-textfield__input',\n IS_DIRTY: 'is-dirty',\n IS_FOCUSED: 'is-focused',\n IS_DISABLED: 'is-disabled',\n IS_INVALID: 'is-invalid',\n IS_UPGRADED: 'is-upgraded'\n};\n/**\n * Handle input being entered.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialTextfield.prototype.onKeyDown_ = function (event) {\n var currentRowCount = event.target.value.split('\\n').length;\n if (event.keyCode === 13) {\n if (currentRowCount >= this.maxRows) {\n event.preventDefault();\n }\n }\n};\n/**\n * Handle focus.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialTextfield.prototype.onFocus_ = function (event) {\n this.element_.classList.add(this.CssClasses_.IS_FOCUSED);\n};\n/**\n * Handle lost focus.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialTextfield.prototype.onBlur_ = function (event) {\n this.element_.classList.remove(this.CssClasses_.IS_FOCUSED);\n};\n/**\n * Handle class updates.\n *\n * @private\n */\nMaterialTextfield.prototype.updateClasses_ = function () {\n this.checkDisabled();\n this.checkValidity();\n this.checkDirty();\n};\n// Public methods.\n/**\n * Check the disabled state and update field accordingly.\n *\n * @public\n */\nMaterialTextfield.prototype.checkDisabled = function () {\n if (this.input_.disabled) {\n this.element_.classList.add(this.CssClasses_.IS_DISABLED);\n } else {\n this.element_.classList.remove(this.CssClasses_.IS_DISABLED);\n }\n};\nMaterialTextfield.prototype['checkDisabled'] = MaterialTextfield.prototype.checkDisabled;\n/**\n * Check the validity state and update field accordingly.\n *\n * @public\n */\nMaterialTextfield.prototype.checkValidity = function () {\n if (this.input_.validity) {\n if (this.input_.validity.valid) {\n this.element_.classList.remove(this.CssClasses_.IS_INVALID);\n } else {\n this.element_.classList.add(this.CssClasses_.IS_INVALID);\n }\n }\n};\nMaterialTextfield.prototype['checkValidity'] = MaterialTextfield.prototype.checkValidity;\n/**\n * Check the dirty state and update field accordingly.\n *\n * @public\n */\nMaterialTextfield.prototype.checkDirty = function () {\n if (this.input_.value && this.input_.value.length > 0) {\n this.element_.classList.add(this.CssClasses_.IS_DIRTY);\n } else {\n this.element_.classList.remove(this.CssClasses_.IS_DIRTY);\n }\n};\nMaterialTextfield.prototype['checkDirty'] = MaterialTextfield.prototype.checkDirty;\n/**\n * Disable text field.\n *\n * @public\n */\nMaterialTextfield.prototype.disable = function () {\n this.input_.disabled = true;\n this.updateClasses_();\n};\nMaterialTextfield.prototype['disable'] = MaterialTextfield.prototype.disable;\n/**\n * Enable text field.\n *\n * @public\n */\nMaterialTextfield.prototype.enable = function () {\n this.input_.disabled = false;\n this.updateClasses_();\n};\nMaterialTextfield.prototype['enable'] = MaterialTextfield.prototype.enable;\n/**\n * Update text field value.\n *\n * @param {string} value The value to which to set the control (optional).\n * @public\n */\nMaterialTextfield.prototype.change = function (value) {\n this.input_.value = value || '';\n this.updateClasses_();\n};\nMaterialTextfield.prototype['change'] = MaterialTextfield.prototype.change;\n/**\n * Initialize element.\n */\nMaterialTextfield.prototype.init = function () {\n if (this.element_) {\n this.label_ = this.element_.querySelector('.' + this.CssClasses_.LABEL);\n this.input_ = this.element_.querySelector('.' + this.CssClasses_.INPUT);\n if (this.input_) {\n if (this.input_.hasAttribute(this.Constant_.MAX_ROWS_ATTRIBUTE)) {\n this.maxRows = parseInt(this.input_.getAttribute(this.Constant_.MAX_ROWS_ATTRIBUTE), 10);\n if (isNaN(this.maxRows)) {\n this.maxRows = this.Constant_.NO_MAX_ROWS;\n }\n }\n this.boundUpdateClassesHandler = this.updateClasses_.bind(this);\n this.boundFocusHandler = this.onFocus_.bind(this);\n this.boundBlurHandler = this.onBlur_.bind(this);\n this.input_.addEventListener('input', this.boundUpdateClassesHandler);\n this.input_.addEventListener('focus', this.boundFocusHandler);\n this.input_.addEventListener('blur', this.boundBlurHandler);\n if (this.maxRows !== this.Constant_.NO_MAX_ROWS) {\n // TODO: This should handle pasting multi line text.\n // Currently doesn't.\n this.boundKeyDownHandler = this.onKeyDown_.bind(this);\n this.input_.addEventListener('keydown', this.boundKeyDownHandler);\n }\n var invalid = this.element_.classList.contains(this.CssClasses_.IS_INVALID);\n this.updateClasses_();\n this.element_.classList.add(this.CssClasses_.IS_UPGRADED);\n if (invalid) {\n this.element_.classList.add(this.CssClasses_.IS_INVALID);\n }\n }\n }\n};\n/**\n * Downgrade the component\n *\n * @private\n */\nMaterialTextfield.prototype.mdlDowngrade_ = function () {\n this.input_.removeEventListener('input', this.boundUpdateClassesHandler);\n this.input_.removeEventListener('focus', this.boundFocusHandler);\n this.input_.removeEventListener('blur', this.boundBlurHandler);\n if (this.boundKeyDownHandler) {\n this.input_.removeEventListener('keydown', this.boundKeyDownHandler);\n }\n};\n/**\n * Public alias for the downgrade method.\n *\n * @public\n */\nMaterialTextfield.prototype.mdlDowngrade = MaterialTextfield.prototype.mdlDowngrade_;\nMaterialTextfield.prototype['mdlDowngrade'] = MaterialTextfield.prototype.mdlDowngrade;\n// The component registers itself. It can assume componentHandler is available\n// in the global scope.\ncomponentHandler.register({\n constructor: MaterialTextfield,\n classAsString: 'MaterialTextfield',\n cssClass: 'mdl-js-textfield',\n widget: true\n});\n/**\n * @license\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Class constructor for Tooltip MDL component.\n * Implements MDL component design pattern defined at:\n * https://github.com/jasonmayes/mdl-component-design-pattern\n *\n * @constructor\n * @param {HTMLElement} element The element that will be upgraded.\n */\nvar MaterialTooltip = function MaterialTooltip(element) {\n this.element_ = element;\n // Initialize instance.\n this.init();\n};\nwindow['MaterialTooltip'] = MaterialTooltip;\n/**\n * Store constants in one place so they can be updated easily.\n *\n * @enum {string | number}\n * @private\n */\nMaterialTooltip.prototype.Constant_ = {};\n/**\n * Store strings for class names defined by this component that are used in\n * JavaScript. This allows us to simply change it in one place should we\n * decide to modify at a later date.\n *\n * @enum {string}\n * @private\n */\nMaterialTooltip.prototype.CssClasses_ = { IS_ACTIVE: 'is-active' };\n/**\n * Handle mouseenter for tooltip.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialTooltip.prototype.handleMouseEnter_ = function (event) {\n event.stopPropagation();\n var props = event.target.getBoundingClientRect();\n var left = props.left + props.width / 2;\n var marginLeft = -1 * (this.element_.offsetWidth / 2);\n if (left + marginLeft < 0) {\n this.element_.style.left = 0;\n this.element_.style.marginLeft = 0;\n } else {\n this.element_.style.left = left + 'px';\n this.element_.style.marginLeft = marginLeft + 'px';\n }\n this.element_.style.top = props.top + props.height + 10 + 'px';\n this.element_.classList.add(this.CssClasses_.IS_ACTIVE);\n window.addEventListener('scroll', this.boundMouseLeaveHandler, false);\n window.addEventListener('touchmove', this.boundMouseLeaveHandler, false);\n};\n/**\n * Handle mouseleave for tooltip.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialTooltip.prototype.handleMouseLeave_ = function (event) {\n event.stopPropagation();\n this.element_.classList.remove(this.CssClasses_.IS_ACTIVE);\n window.removeEventListener('scroll', this.boundMouseLeaveHandler);\n window.removeEventListener('touchmove', this.boundMouseLeaveHandler, false);\n};\n/**\n * Initialize element.\n */\nMaterialTooltip.prototype.init = function () {\n if (this.element_) {\n var forElId = this.element_.getAttribute('for');\n if (forElId) {\n this.forElement_ = document.getElementById(forElId);\n }\n if (this.forElement_) {\n // Tabindex needs to be set for `blur` events to be emitted\n if (!this.forElement_.hasAttribute('tabindex')) {\n this.forElement_.setAttribute('tabindex', '0');\n }\n this.boundMouseEnterHandler = this.handleMouseEnter_.bind(this);\n this.boundMouseLeaveHandler = this.handleMouseLeave_.bind(this);\n this.forElement_.addEventListener('mouseenter', this.boundMouseEnterHandler, false);\n this.forElement_.addEventListener('click', this.boundMouseEnterHandler, false);\n this.forElement_.addEventListener('blur', this.boundMouseLeaveHandler);\n this.forElement_.addEventListener('touchstart', this.boundMouseEnterHandler, false);\n this.forElement_.addEventListener('mouseleave', this.boundMouseLeaveHandler);\n }\n }\n};\n/**\n * Downgrade the component\n *\n * @private\n */\nMaterialTooltip.prototype.mdlDowngrade_ = function () {\n if (this.forElement_) {\n this.forElement_.removeEventListener('mouseenter', this.boundMouseEnterHandler, false);\n this.forElement_.removeEventListener('click', this.boundMouseEnterHandler, false);\n this.forElement_.removeEventListener('touchstart', this.boundMouseEnterHandler, false);\n this.forElement_.removeEventListener('mouseleave', this.boundMouseLeaveHandler);\n }\n};\n/**\n * Public alias for the downgrade method.\n *\n * @public\n */\nMaterialTooltip.prototype.mdlDowngrade = MaterialTooltip.prototype.mdlDowngrade_;\nMaterialTooltip.prototype['mdlDowngrade'] = MaterialTooltip.prototype.mdlDowngrade;\n// The component registers itself. It can assume componentHandler is available\n// in the global scope.\ncomponentHandler.register({\n constructor: MaterialTooltip,\n classAsString: 'MaterialTooltip',\n cssClass: 'mdl-tooltip'\n});\n/**\n * @license\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Class constructor for Layout MDL component.\n * Implements MDL component design pattern defined at:\n * https://github.com/jasonmayes/mdl-component-design-pattern\n *\n * @constructor\n * @param {HTMLElement} element The element that will be upgraded.\n */\nvar MaterialLayout = function MaterialLayout(element) {\n this.element_ = element;\n // Initialize instance.\n this.init();\n};\nwindow['MaterialLayout'] = MaterialLayout;\n/**\n * Store constants in one place so they can be updated easily.\n *\n * @enum {string | number}\n * @private\n */\nMaterialLayout.prototype.Constant_ = {\n MAX_WIDTH: '(max-width: 1024px)',\n TAB_SCROLL_PIXELS: 100,\n MENU_ICON: 'menu',\n CHEVRON_LEFT: 'chevron_left',\n CHEVRON_RIGHT: 'chevron_right'\n};\n/**\n * Modes.\n *\n * @enum {number}\n * @private\n */\nMaterialLayout.prototype.Mode_ = {\n STANDARD: 0,\n SEAMED: 1,\n WATERFALL: 2,\n SCROLL: 3\n};\n/**\n * Store strings for class names defined by this component that are used in\n * JavaScript. This allows us to simply change it in one place should we\n * decide to modify at a later date.\n *\n * @enum {string}\n * @private\n */\nMaterialLayout.prototype.CssClasses_ = {\n CONTAINER: 'mdl-layout__container',\n HEADER: 'mdl-layout__header',\n DRAWER: 'mdl-layout__drawer',\n CONTENT: 'mdl-layout__content',\n DRAWER_BTN: 'mdl-layout__drawer-button',\n ICON: 'material-icons',\n JS_RIPPLE_EFFECT: 'mdl-js-ripple-effect',\n RIPPLE_CONTAINER: 'mdl-layout__tab-ripple-container',\n RIPPLE: 'mdl-ripple',\n RIPPLE_IGNORE_EVENTS: 'mdl-js-ripple-effect--ignore-events',\n HEADER_SEAMED: 'mdl-layout__header--seamed',\n HEADER_WATERFALL: 'mdl-layout__header--waterfall',\n HEADER_SCROLL: 'mdl-layout__header--scroll',\n FIXED_HEADER: 'mdl-layout--fixed-header',\n OBFUSCATOR: 'mdl-layout__obfuscator',\n TAB_BAR: 'mdl-layout__tab-bar',\n TAB_CONTAINER: 'mdl-layout__tab-bar-container',\n TAB: 'mdl-layout__tab',\n TAB_BAR_BUTTON: 'mdl-layout__tab-bar-button',\n TAB_BAR_LEFT_BUTTON: 'mdl-layout__tab-bar-left-button',\n TAB_BAR_RIGHT_BUTTON: 'mdl-layout__tab-bar-right-button',\n PANEL: 'mdl-layout__tab-panel',\n HAS_DRAWER: 'has-drawer',\n HAS_TABS: 'has-tabs',\n HAS_SCROLLING_HEADER: 'has-scrolling-header',\n CASTING_SHADOW: 'is-casting-shadow',\n IS_COMPACT: 'is-compact',\n IS_SMALL_SCREEN: 'is-small-screen',\n IS_DRAWER_OPEN: 'is-visible',\n IS_ACTIVE: 'is-active',\n IS_UPGRADED: 'is-upgraded',\n IS_ANIMATING: 'is-animating',\n ON_LARGE_SCREEN: 'mdl-layout--large-screen-only',\n ON_SMALL_SCREEN: 'mdl-layout--small-screen-only'\n};\n/**\n * Handles scrolling on the content.\n *\n * @private\n */\nMaterialLayout.prototype.contentScrollHandler_ = function () {\n if (this.header_.classList.contains(this.CssClasses_.IS_ANIMATING)) {\n return;\n }\n if (this.content_.scrollTop > 0 && !this.header_.classList.contains(this.CssClasses_.IS_COMPACT)) {\n this.header_.classList.add(this.CssClasses_.CASTING_SHADOW);\n this.header_.classList.add(this.CssClasses_.IS_COMPACT);\n this.header_.classList.add(this.CssClasses_.IS_ANIMATING);\n } else if (this.content_.scrollTop <= 0 && this.header_.classList.contains(this.CssClasses_.IS_COMPACT)) {\n this.header_.classList.remove(this.CssClasses_.CASTING_SHADOW);\n this.header_.classList.remove(this.CssClasses_.IS_COMPACT);\n this.header_.classList.add(this.CssClasses_.IS_ANIMATING);\n }\n};\n/**\n * Handles changes in screen size.\n *\n * @private\n */\nMaterialLayout.prototype.screenSizeHandler_ = function () {\n if (this.screenSizeMediaQuery_.matches) {\n this.element_.classList.add(this.CssClasses_.IS_SMALL_SCREEN);\n } else {\n this.element_.classList.remove(this.CssClasses_.IS_SMALL_SCREEN);\n // Collapse drawer (if any) when moving to a large screen size.\n if (this.drawer_) {\n this.drawer_.classList.remove(this.CssClasses_.IS_DRAWER_OPEN);\n this.obfuscator_.classList.remove(this.CssClasses_.IS_DRAWER_OPEN);\n }\n }\n};\n/**\n * Handles toggling of the drawer.\n *\n * @private\n */\nMaterialLayout.prototype.drawerToggleHandler_ = function () {\n this.drawer_.classList.toggle(this.CssClasses_.IS_DRAWER_OPEN);\n this.obfuscator_.classList.toggle(this.CssClasses_.IS_DRAWER_OPEN);\n};\n/**\n * Handles (un)setting the `is-animating` class\n *\n * @private\n */\nMaterialLayout.prototype.headerTransitionEndHandler_ = function () {\n this.header_.classList.remove(this.CssClasses_.IS_ANIMATING);\n};\n/**\n * Handles expanding the header on click\n *\n * @private\n */\nMaterialLayout.prototype.headerClickHandler_ = function () {\n if (this.header_.classList.contains(this.CssClasses_.IS_COMPACT)) {\n this.header_.classList.remove(this.CssClasses_.IS_COMPACT);\n this.header_.classList.add(this.CssClasses_.IS_ANIMATING);\n }\n};\n/**\n * Reset tab state, dropping active classes\n *\n * @private\n */\nMaterialLayout.prototype.resetTabState_ = function (tabBar) {\n for (var k = 0; k < tabBar.length; k++) {\n tabBar[k].classList.remove(this.CssClasses_.IS_ACTIVE);\n }\n};\n/**\n * Reset panel state, droping active classes\n *\n * @private\n */\nMaterialLayout.prototype.resetPanelState_ = function (panels) {\n for (var j = 0; j < panels.length; j++) {\n panels[j].classList.remove(this.CssClasses_.IS_ACTIVE);\n }\n};\n/**\n * Initialize element.\n */\nMaterialLayout.prototype.init = function () {\n if (this.element_) {\n var container = document.createElement('div');\n container.classList.add(this.CssClasses_.CONTAINER);\n this.element_.parentElement.insertBefore(container, this.element_);\n this.element_.parentElement.removeChild(this.element_);\n container.appendChild(this.element_);\n var directChildren = this.element_.childNodes;\n var numChildren = directChildren.length;\n for (var c = 0; c < numChildren; c++) {\n var child = directChildren[c];\n if (child.classList && child.classList.contains(this.CssClasses_.HEADER)) {\n this.header_ = child;\n }\n if (child.classList && child.classList.contains(this.CssClasses_.DRAWER)) {\n this.drawer_ = child;\n }\n if (child.classList && child.classList.contains(this.CssClasses_.CONTENT)) {\n this.content_ = child;\n }\n }\n if (this.header_) {\n this.tabBar_ = this.header_.querySelector('.' + this.CssClasses_.TAB_BAR);\n }\n var mode = this.Mode_.STANDARD;\n if (this.header_) {\n if (this.header_.classList.contains(this.CssClasses_.HEADER_SEAMED)) {\n mode = this.Mode_.SEAMED;\n } else if (this.header_.classList.contains(this.CssClasses_.HEADER_WATERFALL)) {\n mode = this.Mode_.WATERFALL;\n this.header_.addEventListener('transitionend', this.headerTransitionEndHandler_.bind(this));\n this.header_.addEventListener('click', this.headerClickHandler_.bind(this));\n } else if (this.header_.classList.contains(this.CssClasses_.HEADER_SCROLL)) {\n mode = this.Mode_.SCROLL;\n container.classList.add(this.CssClasses_.HAS_SCROLLING_HEADER);\n }\n if (mode === this.Mode_.STANDARD) {\n this.header_.classList.add(this.CssClasses_.CASTING_SHADOW);\n if (this.tabBar_) {\n this.tabBar_.classList.add(this.CssClasses_.CASTING_SHADOW);\n }\n } else if (mode === this.Mode_.SEAMED || mode === this.Mode_.SCROLL) {\n this.header_.classList.remove(this.CssClasses_.CASTING_SHADOW);\n if (this.tabBar_) {\n this.tabBar_.classList.remove(this.CssClasses_.CASTING_SHADOW);\n }\n } else if (mode === this.Mode_.WATERFALL) {\n // Add and remove shadows depending on scroll position.\n // Also add/remove auxiliary class for styling of the compact version of\n // the header.\n this.content_.addEventListener('scroll', this.contentScrollHandler_.bind(this));\n this.contentScrollHandler_();\n }\n }\n // Add drawer toggling button to our layout, if we have an openable drawer.\n if (this.drawer_) {\n var drawerButton = this.element_.querySelector('.' + this.CssClasses_.DRAWER_BTN);\n if (!drawerButton) {\n drawerButton = document.createElement('div');\n drawerButton.classList.add(this.CssClasses_.DRAWER_BTN);\n var drawerButtonIcon = document.createElement('i');\n drawerButtonIcon.classList.add(this.CssClasses_.ICON);\n drawerButtonIcon.textContent = this.Constant_.MENU_ICON;\n drawerButton.appendChild(drawerButtonIcon);\n }\n if (this.drawer_.classList.contains(this.CssClasses_.ON_LARGE_SCREEN)) {\n //If drawer has ON_LARGE_SCREEN class then add it to the drawer toggle button as well.\n drawerButton.classList.add(this.CssClasses_.ON_LARGE_SCREEN);\n } else if (this.drawer_.classList.contains(this.CssClasses_.ON_SMALL_SCREEN)) {\n //If drawer has ON_SMALL_SCREEN class then add it to the drawer toggle button as well.\n drawerButton.classList.add(this.CssClasses_.ON_SMALL_SCREEN);\n }\n drawerButton.addEventListener('click', this.drawerToggleHandler_.bind(this));\n // Add a class if the layout has a drawer, for altering the left padding.\n // Adds the HAS_DRAWER to the elements since this.header_ may or may\n // not be present.\n this.element_.classList.add(this.CssClasses_.HAS_DRAWER);\n // If we have a fixed header, add the button to the header rather than\n // the layout.\n if (this.element_.classList.contains(this.CssClasses_.FIXED_HEADER)) {\n this.header_.insertBefore(drawerButton, this.header_.firstChild);\n } else {\n this.element_.insertBefore(drawerButton, this.content_);\n }\n var obfuscator = document.createElement('div');\n obfuscator.classList.add(this.CssClasses_.OBFUSCATOR);\n this.element_.appendChild(obfuscator);\n obfuscator.addEventListener('click', this.drawerToggleHandler_.bind(this));\n this.obfuscator_ = obfuscator;\n }\n // Keep an eye on screen size, and add/remove auxiliary class for styling\n // of small screens.\n this.screenSizeMediaQuery_ = window.matchMedia(this.Constant_.MAX_WIDTH);\n this.screenSizeMediaQuery_.addListener(this.screenSizeHandler_.bind(this));\n this.screenSizeHandler_();\n // Initialize tabs, if any.\n if (this.header_ && this.tabBar_) {\n this.element_.classList.add(this.CssClasses_.HAS_TABS);\n var tabContainer = document.createElement('div');\n tabContainer.classList.add(this.CssClasses_.TAB_CONTAINER);\n this.header_.insertBefore(tabContainer, this.tabBar_);\n this.header_.removeChild(this.tabBar_);\n var leftButton = document.createElement('div');\n leftButton.classList.add(this.CssClasses_.TAB_BAR_BUTTON);\n leftButton.classList.add(this.CssClasses_.TAB_BAR_LEFT_BUTTON);\n var leftButtonIcon = document.createElement('i');\n leftButtonIcon.classList.add(this.CssClasses_.ICON);\n leftButtonIcon.textContent = this.Constant_.CHEVRON_LEFT;\n leftButton.appendChild(leftButtonIcon);\n leftButton.addEventListener('click', function () {\n this.tabBar_.scrollLeft -= this.Constant_.TAB_SCROLL_PIXELS;\n }.bind(this));\n var rightButton = document.createElement('div');\n rightButton.classList.add(this.CssClasses_.TAB_BAR_BUTTON);\n rightButton.classList.add(this.CssClasses_.TAB_BAR_RIGHT_BUTTON);\n var rightButtonIcon = document.createElement('i');\n rightButtonIcon.classList.add(this.CssClasses_.ICON);\n rightButtonIcon.textContent = this.Constant_.CHEVRON_RIGHT;\n rightButton.appendChild(rightButtonIcon);\n rightButton.addEventListener('click', function () {\n this.tabBar_.scrollLeft += this.Constant_.TAB_SCROLL_PIXELS;\n }.bind(this));\n tabContainer.appendChild(leftButton);\n tabContainer.appendChild(this.tabBar_);\n tabContainer.appendChild(rightButton);\n // Add and remove buttons depending on scroll position.\n var tabScrollHandler = function () {\n if (this.tabBar_.scrollLeft > 0) {\n leftButton.classList.add(this.CssClasses_.IS_ACTIVE);\n } else {\n leftButton.classList.remove(this.CssClasses_.IS_ACTIVE);\n }\n if (this.tabBar_.scrollLeft < this.tabBar_.scrollWidth - this.tabBar_.offsetWidth) {\n rightButton.classList.add(this.CssClasses_.IS_ACTIVE);\n } else {\n rightButton.classList.remove(this.CssClasses_.IS_ACTIVE);\n }\n }.bind(this);\n this.tabBar_.addEventListener('scroll', tabScrollHandler);\n tabScrollHandler();\n if (this.tabBar_.classList.contains(this.CssClasses_.JS_RIPPLE_EFFECT)) {\n this.tabBar_.classList.add(this.CssClasses_.RIPPLE_IGNORE_EVENTS);\n }\n // Select element tabs, document panels\n var tabs = this.tabBar_.querySelectorAll('.' + this.CssClasses_.TAB);\n var panels = this.content_.querySelectorAll('.' + this.CssClasses_.PANEL);\n // Create new tabs for each tab element\n for (var i = 0; i < tabs.length; i++) {\n new MaterialLayoutTab(tabs[i], tabs, panels, this);\n }\n }\n this.element_.classList.add(this.CssClasses_.IS_UPGRADED);\n }\n};\n/**\n * Constructor for an individual tab.\n *\n * @constructor\n * @param {HTMLElement} tab The HTML element for the tab.\n * @param {!Array} tabs Array with HTML elements for all tabs.\n * @param {!Array} panels Array with HTML elements for all panels.\n * @param {MaterialLayout} layout The MaterialLayout object that owns the tab.\n */\nfunction MaterialLayoutTab(tab, tabs, panels, layout) {\n if (layout.tabBar_.classList.contains(layout.CssClasses_.JS_RIPPLE_EFFECT)) {\n var rippleContainer = document.createElement('span');\n rippleContainer.classList.add(layout.CssClasses_.RIPPLE_CONTAINER);\n rippleContainer.classList.add(layout.CssClasses_.JS_RIPPLE_EFFECT);\n var ripple = document.createElement('span');\n ripple.classList.add(layout.CssClasses_.RIPPLE);\n rippleContainer.appendChild(ripple);\n tab.appendChild(rippleContainer);\n }\n tab.addEventListener('click', function (e) {\n e.preventDefault();\n var href = tab.href.split('#')[1];\n var panel = layout.content_.querySelector('#' + href);\n layout.resetTabState_(tabs);\n layout.resetPanelState_(panels);\n tab.classList.add(layout.CssClasses_.IS_ACTIVE);\n panel.classList.add(layout.CssClasses_.IS_ACTIVE);\n });\n}\n// The component registers itself. It can assume componentHandler is available\n// in the global scope.\ncomponentHandler.register({\n constructor: MaterialLayout,\n classAsString: 'MaterialLayout',\n cssClass: 'mdl-js-layout'\n});\n/**\n * @license\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Class constructor for Data Table Card MDL component.\n * Implements MDL component design pattern defined at:\n * https://github.com/jasonmayes/mdl-component-design-pattern\n *\n * @constructor\n * @param {HTMLElement} element The element that will be upgraded.\n */\nvar MaterialDataTable = function MaterialDataTable(element) {\n this.element_ = element;\n // Initialize instance.\n this.init();\n};\nwindow['MaterialDataTable'] = MaterialDataTable;\n/**\n * Store constants in one place so they can be updated easily.\n *\n * @enum {string | number}\n * @private\n */\nMaterialDataTable.prototype.Constant_ = {};\n/**\n * Store strings for class names defined by this component that are used in\n * JavaScript. This allows us to simply change it in one place should we\n * decide to modify at a later date.\n *\n * @enum {string}\n * @private\n */\nMaterialDataTable.prototype.CssClasses_ = {\n DATA_TABLE: 'mdl-data-table',\n SELECTABLE: 'mdl-data-table--selectable',\n SELECT_ELEMENT: 'mdl-data-table__select',\n IS_SELECTED: 'is-selected',\n IS_UPGRADED: 'is-upgraded'\n};\n/**\n * Generates and returns a function that toggles the selection state of a\n * single row (or multiple rows).\n *\n * @param {Element} checkbox Checkbox that toggles the selection state.\n * @param {HTMLElement} row Row to toggle when checkbox changes.\n * @param {(Array|NodeList)=} opt_rows Rows to toggle when checkbox changes.\n * @private\n */\nMaterialDataTable.prototype.selectRow_ = function (checkbox, row, opt_rows) {\n if (row) {\n return function () {\n if (checkbox.checked) {\n row.classList.add(this.CssClasses_.IS_SELECTED);\n } else {\n row.classList.remove(this.CssClasses_.IS_SELECTED);\n }\n }.bind(this);\n }\n if (opt_rows) {\n return function () {\n var i;\n var el;\n if (checkbox.checked) {\n for (i = 0; i < opt_rows.length; i++) {\n el = opt_rows[i].querySelector('td').querySelector('.mdl-checkbox');\n el['MaterialCheckbox'].check();\n opt_rows[i].classList.add(this.CssClasses_.IS_SELECTED);\n }\n } else {\n for (i = 0; i < opt_rows.length; i++) {\n el = opt_rows[i].querySelector('td').querySelector('.mdl-checkbox');\n el['MaterialCheckbox'].uncheck();\n opt_rows[i].classList.remove(this.CssClasses_.IS_SELECTED);\n }\n }\n }.bind(this);\n }\n};\n/**\n * Creates a checkbox for a single or or multiple rows and hooks up the\n * event handling.\n *\n * @param {HTMLElement} row Row to toggle when checkbox changes.\n * @param {(Array|NodeList)=} opt_rows Rows to toggle when checkbox changes.\n * @private\n */\nMaterialDataTable.prototype.createCheckbox_ = function (row, opt_rows) {\n var label = document.createElement('label');\n var labelClasses = [\n 'mdl-checkbox',\n 'mdl-js-checkbox',\n 'mdl-js-ripple-effect',\n this.CssClasses_.SELECT_ELEMENT\n ];\n label.className = labelClasses.join(' ');\n var checkbox = document.createElement('input');\n checkbox.type = 'checkbox';\n checkbox.classList.add('mdl-checkbox__input');\n checkbox.addEventListener('change', this.selectRow_(checkbox, row, opt_rows));\n label.appendChild(checkbox);\n componentHandler.upgradeElement(label, 'MaterialCheckbox');\n return label;\n};\n/**\n * Initialize element.\n */\nMaterialDataTable.prototype.init = function () {\n if (this.element_) {\n var firstHeader = this.element_.querySelector('th');\n var rows = this.element_.querySelector('tbody').querySelectorAll('tr');\n if (this.element_.classList.contains(this.CssClasses_.SELECTABLE)) {\n var th = document.createElement('th');\n var headerCheckbox = this.createCheckbox_(null, rows);\n th.appendChild(headerCheckbox);\n firstHeader.parentElement.insertBefore(th, firstHeader);\n for (var i = 0; i < rows.length; i++) {\n var firstCell = rows[i].querySelector('td');\n if (firstCell) {\n var td = document.createElement('td');\n var rowCheckbox = this.createCheckbox_(rows[i]);\n td.appendChild(rowCheckbox);\n rows[i].insertBefore(td, firstCell);\n }\n }\n }\n this.element_.classList.add(this.CssClasses_.IS_UPGRADED);\n }\n};\n// The component registers itself. It can assume componentHandler is available\n// in the global scope.\ncomponentHandler.register({\n constructor: MaterialDataTable,\n classAsString: 'MaterialDataTable',\n cssClass: 'mdl-js-data-table'\n});\n/**\n * @license\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Class constructor for Ripple MDL component.\n * Implements MDL component design pattern defined at:\n * https://github.com/jasonmayes/mdl-component-design-pattern\n *\n * @constructor\n * @param {HTMLElement} element The element that will be upgraded.\n */\nvar MaterialRipple = function MaterialRipple(element) {\n this.element_ = element;\n // Initialize instance.\n this.init();\n};\nwindow['MaterialRipple'] = MaterialRipple;\n/**\n * Store constants in one place so they can be updated easily.\n *\n * @enum {string | number}\n * @private\n */\nMaterialRipple.prototype.Constant_ = {\n INITIAL_SCALE: 'scale(0.0001, 0.0001)',\n INITIAL_SIZE: '1px',\n INITIAL_OPACITY: '0.4',\n FINAL_OPACITY: '0',\n FINAL_SCALE: ''\n};\n/**\n * Store strings for class names defined by this component that are used in\n * JavaScript. This allows us to simply change it in one place should we\n * decide to modify at a later date.\n *\n * @enum {string}\n * @private\n */\nMaterialRipple.prototype.CssClasses_ = {\n RIPPLE_CENTER: 'mdl-ripple--center',\n RIPPLE_EFFECT_IGNORE_EVENTS: 'mdl-js-ripple-effect--ignore-events',\n RIPPLE: 'mdl-ripple',\n IS_ANIMATING: 'is-animating',\n IS_VISIBLE: 'is-visible'\n};\n/**\n * Handle mouse / finger down on element.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialRipple.prototype.downHandler_ = function (event) {\n if (!this.rippleElement_.style.width && !this.rippleElement_.style.height) {\n var rect = this.element_.getBoundingClientRect();\n this.boundHeight = rect.height;\n this.boundWidth = rect.width;\n this.rippleSize_ = Math.sqrt(rect.width * rect.width + rect.height * rect.height) * 2 + 2;\n this.rippleElement_.style.width = this.rippleSize_ + 'px';\n this.rippleElement_.style.height = this.rippleSize_ + 'px';\n }\n this.rippleElement_.classList.add(this.CssClasses_.IS_VISIBLE);\n if (event.type === 'mousedown' && this.ignoringMouseDown_) {\n this.ignoringMouseDown_ = false;\n } else {\n if (event.type === 'touchstart') {\n this.ignoringMouseDown_ = true;\n }\n var frameCount = this.getFrameCount();\n if (frameCount > 0) {\n return;\n }\n this.setFrameCount(1);\n var bound = event.currentTarget.getBoundingClientRect();\n var x;\n var y;\n // Check if we are handling a keyboard click.\n if (event.clientX === 0 && event.clientY === 0) {\n x = Math.round(bound.width / 2);\n y = Math.round(bound.height / 2);\n } else {\n var clientX = event.clientX ? event.clientX : event.touches[0].clientX;\n var clientY = event.clientY ? event.clientY : event.touches[0].clientY;\n x = Math.round(clientX - bound.left);\n y = Math.round(clientY - bound.top);\n }\n this.setRippleXY(x, y);\n this.setRippleStyles(true);\n window.requestAnimationFrame(this.animFrameHandler.bind(this));\n }\n};\n/**\n * Handle mouse / finger up on element.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialRipple.prototype.upHandler_ = function (event) {\n // Don't fire for the artificial \"mouseup\" generated by a double-click.\n if (event && event.detail !== 2) {\n this.rippleElement_.classList.remove(this.CssClasses_.IS_VISIBLE);\n }\n // Allow a repaint to occur before removing this class, so the animation\n // shows for tap events, which seem to trigger a mouseup too soon after\n // mousedown.\n window.setTimeout(function () {\n this.rippleElement_.classList.remove(this.CssClasses_.IS_VISIBLE);\n }.bind(this), 0);\n};\n/**\n * Initialize element.\n */\nMaterialRipple.prototype.init = function () {\n if (this.element_) {\n var recentering = this.element_.classList.contains(this.CssClasses_.RIPPLE_CENTER);\n if (!this.element_.classList.contains(this.CssClasses_.RIPPLE_EFFECT_IGNORE_EVENTS)) {\n this.rippleElement_ = this.element_.querySelector('.' + this.CssClasses_.RIPPLE);\n this.frameCount_ = 0;\n this.rippleSize_ = 0;\n this.x_ = 0;\n this.y_ = 0;\n // Touch start produces a compat mouse down event, which would cause a\n // second ripples. To avoid that, we use this property to ignore the first\n // mouse down after a touch start.\n this.ignoringMouseDown_ = false;\n this.boundDownHandler = this.downHandler_.bind(this);\n this.element_.addEventListener('mousedown', this.boundDownHandler);\n this.element_.addEventListener('touchstart', this.boundDownHandler);\n this.boundUpHandler = this.upHandler_.bind(this);\n this.element_.addEventListener('mouseup', this.boundUpHandler);\n this.element_.addEventListener('mouseleave', this.boundUpHandler);\n this.element_.addEventListener('touchend', this.boundUpHandler);\n this.element_.addEventListener('blur', this.boundUpHandler);\n /**\n * Getter for frameCount_.\n * @return {number} the frame count.\n */\n this.getFrameCount = function () {\n return this.frameCount_;\n };\n /**\n * Setter for frameCount_.\n * @param {number} fC the frame count.\n */\n this.setFrameCount = function (fC) {\n this.frameCount_ = fC;\n };\n /**\n * Getter for rippleElement_.\n * @return {Element} the ripple element.\n */\n this.getRippleElement = function () {\n return this.rippleElement_;\n };\n /**\n * Sets the ripple X and Y coordinates.\n * @param {number} newX the new X coordinate\n * @param {number} newY the new Y coordinate\n */\n this.setRippleXY = function (newX, newY) {\n this.x_ = newX;\n this.y_ = newY;\n };\n /**\n * Sets the ripple styles.\n * @param {boolean} start whether or not this is the start frame.\n */\n this.setRippleStyles = function (start) {\n if (this.rippleElement_ !== null) {\n var transformString;\n var scale;\n var size;\n var offset = 'translate(' + this.x_ + 'px, ' + this.y_ + 'px)';\n if (start) {\n scale = this.Constant_.INITIAL_SCALE;\n size = this.Constant_.INITIAL_SIZE;\n } else {\n scale = this.Constant_.FINAL_SCALE;\n size = this.rippleSize_ + 'px';\n if (recentering) {\n offset = 'translate(' + this.boundWidth / 2 + 'px, ' + this.boundHeight / 2 + 'px)';\n }\n }\n transformString = 'translate(-50%, -50%) ' + offset + scale;\n this.rippleElement_.style.webkitTransform = transformString;\n this.rippleElement_.style.msTransform = transformString;\n this.rippleElement_.style.transform = transformString;\n if (start) {\n this.rippleElement_.classList.remove(this.CssClasses_.IS_ANIMATING);\n } else {\n this.rippleElement_.classList.add(this.CssClasses_.IS_ANIMATING);\n }\n }\n };\n /**\n * Handles an animation frame.\n */\n this.animFrameHandler = function () {\n if (this.frameCount_-- > 0) {\n window.requestAnimationFrame(this.animFrameHandler.bind(this));\n } else {\n this.setRippleStyles(false);\n }\n };\n }\n }\n};\n/**\n * Downgrade the component\n *\n * @private\n */\nMaterialRipple.prototype.mdlDowngrade_ = function () {\n this.element_.removeEventListener('mousedown', this.boundDownHandler);\n this.element_.removeEventListener('touchstart', this.boundDownHandler);\n this.element_.removeEventListener('mouseup', this.boundUpHandler);\n this.element_.removeEventListener('mouseleave', this.boundUpHandler);\n this.element_.removeEventListener('touchend', this.boundUpHandler);\n this.element_.removeEventListener('blur', this.boundUpHandler);\n};\n/**\n * Public alias for the downgrade method.\n *\n * @public\n */\nMaterialRipple.prototype.mdlDowngrade = MaterialRipple.prototype.mdlDowngrade_;\nMaterialRipple.prototype['mdlDowngrade'] = MaterialRipple.prototype.mdlDowngrade;\n// The component registers itself. It can assume componentHandler is available\n// in the global scope.\ncomponentHandler.register({\n constructor: MaterialRipple,\n classAsString: 'MaterialRipple',\n cssClass: 'mdl-js-ripple-effect',\n widget: false\n});\n}());\n","/**\n * @license\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * A component handler interface using the revealing module design pattern.\n * More details on this design pattern here:\n * https://github.com/jasonmayes/mdl-component-design-pattern\n *\n * @author Jason Mayes.\n */\n/* exported componentHandler */\n\n// Pre-defining the componentHandler interface, for closure documentation and\n// static verification.\nvar componentHandler = {\n /**\n * Searches existing DOM for elements of our component type and upgrades them\n * if they have not already been upgraded.\n *\n * @param {string=} optJsClass the programatic name of the element class we\n * need to create a new instance of.\n * @param {string=} optCssClass the name of the CSS class elements of this\n * type will have.\n */\n upgradeDom: function(optJsClass, optCssClass) {},\n /**\n * Upgrades a specific element rather than all in the DOM.\n *\n * @param {!Element} element The element we wish to upgrade.\n * @param {string=} optJsClass Optional name of the class we want to upgrade\n * the element to.\n */\n upgradeElement: function(element, optJsClass) {},\n /**\n * Upgrades a specific list of elements rather than all in the DOM.\n *\n * @param {!Element|!Array|!NodeList|!HTMLCollection} elements\n * The elements we wish to upgrade.\n */\n upgradeElements: function(elements) {},\n /**\n * Upgrades all registered components found in the current DOM. This is\n * automatically called on window load.\n */\n upgradeAllRegistered: function() {},\n /**\n * Allows user to be alerted to any upgrades that are performed for a given\n * component type\n *\n * @param {string} jsClass The class name of the MDL component we wish\n * to hook into for any upgrades performed.\n * @param {function(!HTMLElement)} callback The function to call upon an\n * upgrade. This function should expect 1 parameter - the HTMLElement which\n * got upgraded.\n */\n registerUpgradedCallback: function(jsClass, callback) {},\n /**\n * Registers a class for future use and attempts to upgrade existing DOM.\n *\n * @param {componentHandler.ComponentConfigPublic} config the registration configuration\n */\n register: function(config) {},\n /**\n * Downgrade either a given node, an array of nodes, or a NodeList.\n *\n * @param {!Node|!Array|!NodeList} nodes\n */\n downgradeElements: function(nodes) {}\n};\n\ncomponentHandler = (function() {\n 'use strict';\n\n /** @type {!Array} */\n var registeredComponents_ = [];\n\n /** @type {!Array} */\n var createdComponents_ = [];\n\n var downgradeMethod_ = 'mdlDowngrade';\n var componentConfigProperty_ = 'mdlComponentConfigInternal_';\n\n /**\n * Searches registered components for a class we are interested in using.\n * Optionally replaces a match with passed object if specified.\n *\n * @param {string} name The name of a class we want to use.\n * @param {componentHandler.ComponentConfig=} optReplace Optional object to replace match with.\n * @return {!Object|boolean}\n * @private\n */\n function findRegisteredClass_(name, optReplace) {\n for (var i = 0; i < registeredComponents_.length; i++) {\n if (registeredComponents_[i].className === name) {\n if (typeof optReplace !== 'undefined') {\n registeredComponents_[i] = optReplace;\n }\n return registeredComponents_[i];\n }\n }\n return false;\n }\n\n /**\n * Returns an array of the classNames of the upgraded classes on the element.\n *\n * @param {!Element} element The element to fetch data from.\n * @return {!Array}\n * @private\n */\n function getUpgradedListOfElement_(element) {\n var dataUpgraded = element.getAttribute('data-upgraded');\n // Use `['']` as default value to conform the `,name,name...` style.\n return dataUpgraded === null ? [''] : dataUpgraded.split(',');\n }\n\n /**\n * Returns true if the given element has already been upgraded for the given\n * class.\n *\n * @param {!Element} element The element we want to check.\n * @param {string} jsClass The class to check for.\n * @returns {boolean}\n * @private\n */\n function isElementUpgraded_(element, jsClass) {\n var upgradedList = getUpgradedListOfElement_(element);\n return upgradedList.indexOf(jsClass) !== -1;\n }\n\n /**\n * Searches existing DOM for elements of our component type and upgrades them\n * if they have not already been upgraded.\n *\n * @param {string=} optJsClass the programatic name of the element class we\n * need to create a new instance of.\n * @param {string=} optCssClass the name of the CSS class elements of this\n * type will have.\n */\n function upgradeDomInternal(optJsClass, optCssClass) {\n if (typeof optJsClass === 'undefined' &&\n typeof optCssClass === 'undefined') {\n for (var i = 0; i < registeredComponents_.length; i++) {\n upgradeDomInternal(registeredComponents_[i].className,\n registeredComponents_[i].cssClass);\n }\n } else {\n var jsClass = /** @type {string} */ (optJsClass);\n if (typeof optCssClass === 'undefined') {\n var registeredClass = findRegisteredClass_(jsClass);\n if (registeredClass) {\n optCssClass = registeredClass.cssClass;\n }\n }\n\n var elements = document.querySelectorAll('.' + optCssClass);\n for (var n = 0; n < elements.length; n++) {\n upgradeElementInternal(elements[n], jsClass);\n }\n }\n }\n\n /**\n * Upgrades a specific element rather than all in the DOM.\n *\n * @param {!Element} element The element we wish to upgrade.\n * @param {string=} optJsClass Optional name of the class we want to upgrade\n * the element to.\n */\n function upgradeElementInternal(element, optJsClass) {\n // Verify argument type.\n if (!(typeof element === 'object' && element instanceof Element)) {\n throw new Error('Invalid argument provided to upgrade MDL element.');\n }\n var upgradedList = getUpgradedListOfElement_(element);\n var classesToUpgrade = [];\n // If jsClass is not provided scan the registered components to find the\n // ones matching the element's CSS classList.\n if (!optJsClass) {\n var classList = element.classList;\n registeredComponents_.forEach(function(component) {\n // Match CSS & Not to be upgraded & Not upgraded.\n if (classList.contains(component.cssClass) &&\n classesToUpgrade.indexOf(component) === -1 &&\n !isElementUpgraded_(element, component.className)) {\n classesToUpgrade.push(component);\n }\n });\n } else if (!isElementUpgraded_(element, optJsClass)) {\n classesToUpgrade.push(findRegisteredClass_(optJsClass));\n }\n\n // Upgrade the element for each classes.\n for (var i = 0, n = classesToUpgrade.length, registeredClass; i < n; i++) {\n registeredClass = classesToUpgrade[i];\n if (registeredClass) {\n // Mark element as upgraded.\n upgradedList.push(registeredClass.className);\n element.setAttribute('data-upgraded', upgradedList.join(','));\n var instance = new registeredClass.classConstructor(element);\n instance[componentConfigProperty_] = registeredClass;\n createdComponents_.push(instance);\n // Call any callbacks the user has registered with this component type.\n for (var j = 0, m = registeredClass.callbacks.length; j < m; j++) {\n registeredClass.callbacks[j](element);\n }\n\n if (registeredClass.widget) {\n // Assign per element instance for control over API\n element[registeredClass.className] = instance;\n }\n } else {\n throw new Error(\n 'Unable to find a registered component for the given class.');\n }\n\n var ev = document.createEvent('Events');\n ev.initEvent('mdl-componentupgraded', true, true);\n element.dispatchEvent(ev);\n }\n }\n\n /**\n * Upgrades a specific list of elements rather than all in the DOM.\n *\n * @param {!Element|!Array|!NodeList|!HTMLCollection} elements\n * The elements we wish to upgrade.\n */\n function upgradeElementsInternal(elements) {\n if (!Array.isArray(elements)) {\n if (typeof elements.item === 'function') {\n elements = Array.prototype.slice.call(/** @type {Array} */ (elements));\n } else {\n elements = [elements];\n }\n }\n for (var i = 0, n = elements.length, element; i < n; i++) {\n element = elements[i];\n if (element instanceof HTMLElement) {\n upgradeElementInternal(element);\n if (element.children.length > 0) {\n upgradeElementsInternal(element.children);\n }\n }\n }\n }\n\n /**\n * Registers a class for future use and attempts to upgrade existing DOM.\n *\n * @param {componentHandler.ComponentConfigPublic} config\n */\n function registerInternal(config) {\n // In order to support both Closure-compiled and uncompiled code accessing\n // this method, we need to allow for both the dot and array syntax for\n // property access. You'll therefore see the `foo.bar || foo['bar']`\n // pattern repeated across this method.\n var widgetMissing = (typeof config.widget === 'undefined' &&\n typeof config['widget'] === 'undefined');\n var widget = true;\n\n if (!widgetMissing) {\n widget = config.widget || config['widget'];\n }\n\n var newConfig = /** @type {componentHandler.ComponentConfig} */ ({\n classConstructor: config.constructor || config['constructor'],\n className: config.classAsString || config['classAsString'],\n cssClass: config.cssClass || config['cssClass'],\n widget: widget,\n callbacks: []\n });\n\n registeredComponents_.forEach(function(item) {\n if (item.cssClass === newConfig.cssClass) {\n throw new Error('The provided cssClass has already been registered: ' + item.cssClass);\n }\n if (item.className === newConfig.className) {\n throw new Error('The provided className has already been registered');\n }\n });\n\n if (config.constructor.prototype\n .hasOwnProperty(componentConfigProperty_)) {\n throw new Error(\n 'MDL component classes must not have ' + componentConfigProperty_ +\n ' defined as a property.');\n }\n\n var found = findRegisteredClass_(config.classAsString, newConfig);\n\n if (!found) {\n registeredComponents_.push(newConfig);\n }\n }\n\n /**\n * Allows user to be alerted to any upgrades that are performed for a given\n * component type\n *\n * @param {string} jsClass The class name of the MDL component we wish\n * to hook into for any upgrades performed.\n * @param {function(!HTMLElement)} callback The function to call upon an\n * upgrade. This function should expect 1 parameter - the HTMLElement which\n * got upgraded.\n */\n function registerUpgradedCallbackInternal(jsClass, callback) {\n var regClass = findRegisteredClass_(jsClass);\n if (regClass) {\n regClass.callbacks.push(callback);\n }\n }\n\n /**\n * Upgrades all registered components found in the current DOM. This is\n * automatically called on window load.\n */\n function upgradeAllRegisteredInternal() {\n for (var n = 0; n < registeredComponents_.length; n++) {\n upgradeDomInternal(registeredComponents_[n].className);\n }\n }\n\n /**\n * Finds a created component by a given DOM node.\n *\n * @param {!Node} node\n * @return {*}\n */\n function findCreatedComponentByNodeInternal(node) {\n for (var n = 0; n < createdComponents_.length; n++) {\n var component = createdComponents_[n];\n if (component.element_ === node) {\n return component;\n }\n }\n }\n\n /**\n * Check the component for the downgrade method.\n * Execute if found.\n * Remove component from createdComponents list.\n *\n * @param {*} component\n */\n function deconstructComponentInternal(component) {\n if (component &&\n component[componentConfigProperty_]\n .classConstructor.prototype\n .hasOwnProperty(downgradeMethod_)) {\n component[downgradeMethod_]();\n var componentIndex = createdComponents_.indexOf(component);\n createdComponents_.splice(componentIndex, 1);\n\n var upgrades = component.element_.getAttribute('data-upgraded').split(',');\n var componentPlace = upgrades.indexOf(\n component[componentConfigProperty_].classAsString);\n upgrades.splice(componentPlace, 1);\n component.element_.setAttribute('data-upgraded', upgrades.join(','));\n\n var ev = document.createEvent('Events');\n ev.initEvent('mdl-componentdowngraded', true, true);\n component.element_.dispatchEvent(ev);\n }\n }\n\n /**\n * Downgrade either a given node, an array of nodes, or a NodeList.\n *\n * @param {!Node|!Array|!NodeList} nodes\n */\n function downgradeNodesInternal(nodes) {\n /**\n * Auxiliary function to downgrade a single node.\n * @param {!Node} node the node to be downgraded\n */\n var downgradeNode = function(node) {\n deconstructComponentInternal(findCreatedComponentByNodeInternal(node));\n };\n if (nodes instanceof Array || nodes instanceof NodeList) {\n for (var n = 0; n < nodes.length; n++) {\n downgradeNode(nodes[n]);\n }\n } else if (nodes instanceof Node) {\n downgradeNode(nodes);\n } else {\n throw new Error('Invalid argument provided to downgrade MDL nodes.');\n }\n }\n\n // Now return the functions that should be made public with their publicly\n // facing names...\n return {\n upgradeDom: upgradeDomInternal,\n upgradeElement: upgradeElementInternal,\n upgradeElements: upgradeElementsInternal,\n upgradeAllRegistered: upgradeAllRegisteredInternal,\n registerUpgradedCallback: registerUpgradedCallbackInternal,\n register: registerInternal,\n downgradeElements: downgradeNodesInternal\n };\n})();\n\n/**\n * Describes the type of a registered component type managed by\n * componentHandler. Provided for benefit of the Closure compiler.\n *\n * @typedef {{\n * constructor: Function,\n * classAsString: string,\n * cssClass: string,\n * widget: (string|boolean|undefined)\n * }}\n */\ncomponentHandler.ComponentConfigPublic; // jshint ignore:line\n\n/**\n * Describes the type of a registered component type managed by\n * componentHandler. Provided for benefit of the Closure compiler.\n *\n * @typedef {{\n * constructor: !Function,\n * className: string,\n * cssClass: string,\n * widget: (string|boolean),\n * callbacks: !Array\n * }}\n */\ncomponentHandler.ComponentConfig; // jshint ignore:line\n\n/**\n * Created component (i.e., upgraded element) type as managed by\n * componentHandler. Provided for benefit of the Closure compiler.\n *\n * @typedef {{\n * element_: !HTMLElement,\n * className: string,\n * classAsString: string,\n * cssClass: string,\n * widget: string\n * }}\n */\ncomponentHandler.Component; // jshint ignore:line\n\n// Export all symbols, for the benefit of Closure compiler.\n// No effect on uncompiled code.\ncomponentHandler['upgradeDom'] = componentHandler.upgradeDom;\ncomponentHandler['upgradeElement'] = componentHandler.upgradeElement;\ncomponentHandler['upgradeElements'] = componentHandler.upgradeElements;\ncomponentHandler['upgradeAllRegistered'] =\n componentHandler.upgradeAllRegistered;\ncomponentHandler['registerUpgradedCallback'] =\n componentHandler.registerUpgradedCallback;\ncomponentHandler['register'] = componentHandler.register;\ncomponentHandler['downgradeElements'] = componentHandler.downgradeElements;\nwindow.componentHandler = componentHandler;\nwindow['componentHandler'] = componentHandler;\n\nwindow.addEventListener('load', function() {\n 'use strict';\n\n /**\n * Performs a \"Cutting the mustard\" test. If the browser supports the features\n * tested, adds a mdl-js class to the element. It then upgrades all MDL\n * components requiring JavaScript.\n */\n if ('classList' in document.createElement('div') &&\n 'querySelector' in document &&\n 'addEventListener' in window && Array.prototype.forEach) {\n document.documentElement.classList.add('mdl-js');\n componentHandler.upgradeAllRegistered();\n } else {\n /**\n * Dummy function to avoid JS errors.\n */\n componentHandler.upgradeElement = function() {};\n /**\n * Dummy function to avoid JS errors.\n */\n componentHandler.register = function() {};\n }\n});\n","/**\n * @license\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Class constructor for Tabs MDL component.\n * Implements MDL component design pattern defined at:\n * https://github.com/jasonmayes/mdl-component-design-pattern\n *\n * @constructor\n * @param {HTMLElement} element The element that will be upgraded.\n */\nvar MaterialTabs = function MaterialTabs(element) {\n // Stores the HTML element.\n this.element_ = element;\n // Initialize instance.\n this.init();\n};\nwindow['MaterialTabs'] = MaterialTabs;\n/**\n * Store constants in one place so they can be updated easily.\n *\n * @enum {string}\n * @private\n */\nMaterialTabs.prototype.Constant_ = {};\n/**\n * Store strings for class names defined by this component that are used in\n * JavaScript. This allows us to simply change it in one place should we\n * decide to modify at a later date.\n *\n * @enum {string}\n * @private\n */\nMaterialTabs.prototype.CssClasses_ = {\n TAB_CLASS: 'mdl-tabs__tab',\n PANEL_CLASS: 'mdl-tabs__panel',\n ACTIVE_CLASS: 'is-active',\n UPGRADED_CLASS: 'is-upgraded',\n MDL_JS_RIPPLE_EFFECT: 'mdl-js-ripple-effect',\n MDL_RIPPLE_CONTAINER: 'mdl-tabs__ripple-container',\n MDL_RIPPLE: 'mdl-ripple',\n MDL_JS_RIPPLE_EFFECT_IGNORE_EVENTS: 'mdl-js-ripple-effect--ignore-events'\n};\n/**\n * Handle clicks to a tabs component\n *\n * @private\n */\nMaterialTabs.prototype.initTabs_ = function () {\n if (this.element_.classList.contains(this.CssClasses_.MDL_JS_RIPPLE_EFFECT)) {\n this.element_.classList.add(this.CssClasses_.MDL_JS_RIPPLE_EFFECT_IGNORE_EVENTS);\n }\n // Select element tabs, document panels\n this.tabs_ = this.element_.querySelectorAll('.' + this.CssClasses_.TAB_CLASS);\n this.panels_ = this.element_.querySelectorAll('.' + this.CssClasses_.PANEL_CLASS);\n // Create new tabs for each tab element\n for (var i = 0; i < this.tabs_.length; i++) {\n new MaterialTab(this.tabs_[i], this);\n }\n this.element_.classList.add(this.CssClasses_.UPGRADED_CLASS);\n};\n/**\n * Reset tab state, dropping active classes\n *\n * @private\n */\nMaterialTabs.prototype.resetTabState_ = function () {\n for (var k = 0; k < this.tabs_.length; k++) {\n this.tabs_[k].classList.remove(this.CssClasses_.ACTIVE_CLASS);\n }\n};\n/**\n * Reset panel state, droping active classes\n *\n * @private\n */\nMaterialTabs.prototype.resetPanelState_ = function () {\n for (var j = 0; j < this.panels_.length; j++) {\n this.panels_[j].classList.remove(this.CssClasses_.ACTIVE_CLASS);\n }\n};\n/**\n * Initialize element.\n */\nMaterialTabs.prototype.init = function () {\n if (this.element_) {\n this.initTabs_();\n }\n};\n/**\n * Constructor for an individual tab.\n *\n * @constructor\n * @param {HTMLElement} tab The HTML element for the tab.\n * @param {MaterialTabs} ctx The MaterialTabs object that owns the tab.\n */\nfunction MaterialTab(tab, ctx) {\n if (tab) {\n if (ctx.element_.classList.contains(ctx.CssClasses_.MDL_JS_RIPPLE_EFFECT)) {\n var rippleContainer = document.createElement('span');\n rippleContainer.classList.add(ctx.CssClasses_.MDL_RIPPLE_CONTAINER);\n rippleContainer.classList.add(ctx.CssClasses_.MDL_JS_RIPPLE_EFFECT);\n var ripple = document.createElement('span');\n ripple.classList.add(ctx.CssClasses_.MDL_RIPPLE);\n rippleContainer.appendChild(ripple);\n tab.appendChild(rippleContainer);\n }\n tab.addEventListener('click', function (e) {\n e.preventDefault();\n var href = tab.href.split('#')[1];\n var panel = ctx.element_.querySelector('#' + href);\n ctx.resetTabState_();\n ctx.resetPanelState_();\n tab.classList.add(ctx.CssClasses_.ACTIVE_CLASS);\n panel.classList.add(ctx.CssClasses_.ACTIVE_CLASS);\n });\n }\n}\n// The component registers itself. It can assume componentHandler is available\n// in the global scope.\ncomponentHandler.register({\n constructor: MaterialTabs,\n classAsString: 'MaterialTabs',\n cssClass: 'mdl-js-tabs'\n});","/**\n * @license\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Class constructor for Layout MDL component.\n * Implements MDL component design pattern defined at:\n * https://github.com/jasonmayes/mdl-component-design-pattern\n *\n * @constructor\n * @param {HTMLElement} element The element that will be upgraded.\n */\nvar MaterialLayout = function MaterialLayout(element) {\n this.element_ = element;\n // Initialize instance.\n this.init();\n};\nwindow['MaterialLayout'] = MaterialLayout;\n/**\n * Store constants in one place so they can be updated easily.\n *\n * @enum {string | number}\n * @private\n */\nMaterialLayout.prototype.Constant_ = {\n MAX_WIDTH: '(max-width: 1024px)',\n TAB_SCROLL_PIXELS: 100,\n MENU_ICON: 'menu',\n CHEVRON_LEFT: 'chevron_left',\n CHEVRON_RIGHT: 'chevron_right'\n};\n/**\n * Modes.\n *\n * @enum {number}\n * @private\n */\nMaterialLayout.prototype.Mode_ = {\n STANDARD: 0,\n SEAMED: 1,\n WATERFALL: 2,\n SCROLL: 3\n};\n/**\n * Store strings for class names defined by this component that are used in\n * JavaScript. This allows us to simply change it in one place should we\n * decide to modify at a later date.\n *\n * @enum {string}\n * @private\n */\nMaterialLayout.prototype.CssClasses_ = {\n CONTAINER: 'mdl-layout__container',\n HEADER: 'mdl-layout__header',\n DRAWER: 'mdl-layout__drawer',\n CONTENT: 'mdl-layout__content',\n DRAWER_BTN: 'mdl-layout__drawer-button',\n ICON: 'material-icons',\n JS_RIPPLE_EFFECT: 'mdl-js-ripple-effect',\n RIPPLE_CONTAINER: 'mdl-layout__tab-ripple-container',\n RIPPLE: 'mdl-ripple',\n RIPPLE_IGNORE_EVENTS: 'mdl-js-ripple-effect--ignore-events',\n HEADER_SEAMED: 'mdl-layout__header--seamed',\n HEADER_WATERFALL: 'mdl-layout__header--waterfall',\n HEADER_SCROLL: 'mdl-layout__header--scroll',\n FIXED_HEADER: 'mdl-layout--fixed-header',\n OBFUSCATOR: 'mdl-layout__obfuscator',\n TAB_BAR: 'mdl-layout__tab-bar',\n TAB_CONTAINER: 'mdl-layout__tab-bar-container',\n TAB: 'mdl-layout__tab',\n TAB_BAR_BUTTON: 'mdl-layout__tab-bar-button',\n TAB_BAR_LEFT_BUTTON: 'mdl-layout__tab-bar-left-button',\n TAB_BAR_RIGHT_BUTTON: 'mdl-layout__tab-bar-right-button',\n PANEL: 'mdl-layout__tab-panel',\n HAS_DRAWER: 'has-drawer',\n HAS_TABS: 'has-tabs',\n HAS_SCROLLING_HEADER: 'has-scrolling-header',\n CASTING_SHADOW: 'is-casting-shadow',\n IS_COMPACT: 'is-compact',\n IS_SMALL_SCREEN: 'is-small-screen',\n IS_DRAWER_OPEN: 'is-visible',\n IS_ACTIVE: 'is-active',\n IS_UPGRADED: 'is-upgraded',\n IS_ANIMATING: 'is-animating',\n ON_LARGE_SCREEN: 'mdl-layout--large-screen-only',\n ON_SMALL_SCREEN: 'mdl-layout--small-screen-only'\n};\n/**\n * Handles scrolling on the content.\n *\n * @private\n */\nMaterialLayout.prototype.contentScrollHandler_ = function () {\n if (this.header_.classList.contains(this.CssClasses_.IS_ANIMATING)) {\n return;\n }\n if (this.content_.scrollTop > 0 && !this.header_.classList.contains(this.CssClasses_.IS_COMPACT)) {\n this.header_.classList.add(this.CssClasses_.CASTING_SHADOW);\n this.header_.classList.add(this.CssClasses_.IS_COMPACT);\n this.header_.classList.add(this.CssClasses_.IS_ANIMATING);\n } else if (this.content_.scrollTop <= 0 && this.header_.classList.contains(this.CssClasses_.IS_COMPACT)) {\n this.header_.classList.remove(this.CssClasses_.CASTING_SHADOW);\n this.header_.classList.remove(this.CssClasses_.IS_COMPACT);\n this.header_.classList.add(this.CssClasses_.IS_ANIMATING);\n }\n};\n/**\n * Handles changes in screen size.\n *\n * @private\n */\nMaterialLayout.prototype.screenSizeHandler_ = function () {\n if (this.screenSizeMediaQuery_.matches) {\n this.element_.classList.add(this.CssClasses_.IS_SMALL_SCREEN);\n } else {\n this.element_.classList.remove(this.CssClasses_.IS_SMALL_SCREEN);\n // Collapse drawer (if any) when moving to a large screen size.\n if (this.drawer_) {\n this.drawer_.classList.remove(this.CssClasses_.IS_DRAWER_OPEN);\n this.obfuscator_.classList.remove(this.CssClasses_.IS_DRAWER_OPEN);\n }\n }\n};\n/**\n * Handles toggling of the drawer.\n *\n * @private\n */\nMaterialLayout.prototype.drawerToggleHandler_ = function () {\n this.drawer_.classList.toggle(this.CssClasses_.IS_DRAWER_OPEN);\n this.obfuscator_.classList.toggle(this.CssClasses_.IS_DRAWER_OPEN);\n};\n/**\n * Handles (un)setting the `is-animating` class\n *\n * @private\n */\nMaterialLayout.prototype.headerTransitionEndHandler_ = function () {\n this.header_.classList.remove(this.CssClasses_.IS_ANIMATING);\n};\n/**\n * Handles expanding the header on click\n *\n * @private\n */\nMaterialLayout.prototype.headerClickHandler_ = function () {\n if (this.header_.classList.contains(this.CssClasses_.IS_COMPACT)) {\n this.header_.classList.remove(this.CssClasses_.IS_COMPACT);\n this.header_.classList.add(this.CssClasses_.IS_ANIMATING);\n }\n};\n/**\n * Reset tab state, dropping active classes\n *\n * @private\n */\nMaterialLayout.prototype.resetTabState_ = function (tabBar) {\n for (var k = 0; k < tabBar.length; k++) {\n tabBar[k].classList.remove(this.CssClasses_.IS_ACTIVE);\n }\n};\n/**\n * Reset panel state, droping active classes\n *\n * @private\n */\nMaterialLayout.prototype.resetPanelState_ = function (panels) {\n for (var j = 0; j < panels.length; j++) {\n panels[j].classList.remove(this.CssClasses_.IS_ACTIVE);\n }\n};\n/**\n * Initialize element.\n */\nMaterialLayout.prototype.init = function () {\n if (this.element_) {\n var container = document.createElement('div');\n container.classList.add(this.CssClasses_.CONTAINER);\n this.element_.parentElement.insertBefore(container, this.element_);\n this.element_.parentElement.removeChild(this.element_);\n container.appendChild(this.element_);\n var directChildren = this.element_.childNodes;\n var numChildren = directChildren.length;\n for (var c = 0; c < numChildren; c++) {\n var child = directChildren[c];\n if (child.classList && child.classList.contains(this.CssClasses_.HEADER)) {\n this.header_ = child;\n }\n if (child.classList && child.classList.contains(this.CssClasses_.DRAWER)) {\n this.drawer_ = child;\n }\n if (child.classList && child.classList.contains(this.CssClasses_.CONTENT)) {\n this.content_ = child;\n }\n }\n if (this.header_) {\n this.tabBar_ = this.header_.querySelector('.' + this.CssClasses_.TAB_BAR);\n }\n var mode = this.Mode_.STANDARD;\n if (this.header_) {\n if (this.header_.classList.contains(this.CssClasses_.HEADER_SEAMED)) {\n mode = this.Mode_.SEAMED;\n } else if (this.header_.classList.contains(this.CssClasses_.HEADER_WATERFALL)) {\n mode = this.Mode_.WATERFALL;\n this.header_.addEventListener('transitionend', this.headerTransitionEndHandler_.bind(this));\n this.header_.addEventListener('click', this.headerClickHandler_.bind(this));\n } else if (this.header_.classList.contains(this.CssClasses_.HEADER_SCROLL)) {\n mode = this.Mode_.SCROLL;\n container.classList.add(this.CssClasses_.HAS_SCROLLING_HEADER);\n }\n if (mode === this.Mode_.STANDARD) {\n this.header_.classList.add(this.CssClasses_.CASTING_SHADOW);\n if (this.tabBar_) {\n this.tabBar_.classList.add(this.CssClasses_.CASTING_SHADOW);\n }\n } else if (mode === this.Mode_.SEAMED || mode === this.Mode_.SCROLL) {\n this.header_.classList.remove(this.CssClasses_.CASTING_SHADOW);\n if (this.tabBar_) {\n this.tabBar_.classList.remove(this.CssClasses_.CASTING_SHADOW);\n }\n } else if (mode === this.Mode_.WATERFALL) {\n // Add and remove shadows depending on scroll position.\n // Also add/remove auxiliary class for styling of the compact version of\n // the header.\n this.content_.addEventListener('scroll', this.contentScrollHandler_.bind(this));\n this.contentScrollHandler_();\n }\n }\n // Add drawer toggling button to our layout, if we have an openable drawer.\n if (this.drawer_) {\n var drawerButton = this.element_.querySelector('.' + this.CssClasses_.DRAWER_BTN);\n if (!drawerButton) {\n drawerButton = document.createElement('div');\n drawerButton.classList.add(this.CssClasses_.DRAWER_BTN);\n var drawerButtonIcon = document.createElement('i');\n drawerButtonIcon.classList.add(this.CssClasses_.ICON);\n drawerButtonIcon.textContent = this.Constant_.MENU_ICON;\n drawerButton.appendChild(drawerButtonIcon);\n }\n if (this.drawer_.classList.contains(this.CssClasses_.ON_LARGE_SCREEN)) {\n //If drawer has ON_LARGE_SCREEN class then add it to the drawer toggle button as well.\n drawerButton.classList.add(this.CssClasses_.ON_LARGE_SCREEN);\n } else if (this.drawer_.classList.contains(this.CssClasses_.ON_SMALL_SCREEN)) {\n //If drawer has ON_SMALL_SCREEN class then add it to the drawer toggle button as well.\n drawerButton.classList.add(this.CssClasses_.ON_SMALL_SCREEN);\n }\n drawerButton.addEventListener('click', this.drawerToggleHandler_.bind(this));\n // Add a class if the layout has a drawer, for altering the left padding.\n // Adds the HAS_DRAWER to the elements since this.header_ may or may\n // not be present.\n this.element_.classList.add(this.CssClasses_.HAS_DRAWER);\n // If we have a fixed header, add the button to the header rather than\n // the layout.\n if (this.element_.classList.contains(this.CssClasses_.FIXED_HEADER)) {\n this.header_.insertBefore(drawerButton, this.header_.firstChild);\n } else {\n this.element_.insertBefore(drawerButton, this.content_);\n }\n var obfuscator = document.createElement('div');\n obfuscator.classList.add(this.CssClasses_.OBFUSCATOR);\n this.element_.appendChild(obfuscator);\n obfuscator.addEventListener('click', this.drawerToggleHandler_.bind(this));\n this.obfuscator_ = obfuscator;\n }\n // Keep an eye on screen size, and add/remove auxiliary class for styling\n // of small screens.\n this.screenSizeMediaQuery_ = window.matchMedia(this.Constant_.MAX_WIDTH);\n this.screenSizeMediaQuery_.addListener(this.screenSizeHandler_.bind(this));\n this.screenSizeHandler_();\n // Initialize tabs, if any.\n if (this.header_ && this.tabBar_) {\n this.element_.classList.add(this.CssClasses_.HAS_TABS);\n var tabContainer = document.createElement('div');\n tabContainer.classList.add(this.CssClasses_.TAB_CONTAINER);\n this.header_.insertBefore(tabContainer, this.tabBar_);\n this.header_.removeChild(this.tabBar_);\n var leftButton = document.createElement('div');\n leftButton.classList.add(this.CssClasses_.TAB_BAR_BUTTON);\n leftButton.classList.add(this.CssClasses_.TAB_BAR_LEFT_BUTTON);\n var leftButtonIcon = document.createElement('i');\n leftButtonIcon.classList.add(this.CssClasses_.ICON);\n leftButtonIcon.textContent = this.Constant_.CHEVRON_LEFT;\n leftButton.appendChild(leftButtonIcon);\n leftButton.addEventListener('click', function () {\n this.tabBar_.scrollLeft -= this.Constant_.TAB_SCROLL_PIXELS;\n }.bind(this));\n var rightButton = document.createElement('div');\n rightButton.classList.add(this.CssClasses_.TAB_BAR_BUTTON);\n rightButton.classList.add(this.CssClasses_.TAB_BAR_RIGHT_BUTTON);\n var rightButtonIcon = document.createElement('i');\n rightButtonIcon.classList.add(this.CssClasses_.ICON);\n rightButtonIcon.textContent = this.Constant_.CHEVRON_RIGHT;\n rightButton.appendChild(rightButtonIcon);\n rightButton.addEventListener('click', function () {\n this.tabBar_.scrollLeft += this.Constant_.TAB_SCROLL_PIXELS;\n }.bind(this));\n tabContainer.appendChild(leftButton);\n tabContainer.appendChild(this.tabBar_);\n tabContainer.appendChild(rightButton);\n // Add and remove buttons depending on scroll position.\n var tabScrollHandler = function () {\n if (this.tabBar_.scrollLeft > 0) {\n leftButton.classList.add(this.CssClasses_.IS_ACTIVE);\n } else {\n leftButton.classList.remove(this.CssClasses_.IS_ACTIVE);\n }\n if (this.tabBar_.scrollLeft < this.tabBar_.scrollWidth - this.tabBar_.offsetWidth) {\n rightButton.classList.add(this.CssClasses_.IS_ACTIVE);\n } else {\n rightButton.classList.remove(this.CssClasses_.IS_ACTIVE);\n }\n }.bind(this);\n this.tabBar_.addEventListener('scroll', tabScrollHandler);\n tabScrollHandler();\n if (this.tabBar_.classList.contains(this.CssClasses_.JS_RIPPLE_EFFECT)) {\n this.tabBar_.classList.add(this.CssClasses_.RIPPLE_IGNORE_EVENTS);\n }\n // Select element tabs, document panels\n var tabs = this.tabBar_.querySelectorAll('.' + this.CssClasses_.TAB);\n var panels = this.content_.querySelectorAll('.' + this.CssClasses_.PANEL);\n // Create new tabs for each tab element\n for (var i = 0; i < tabs.length; i++) {\n new MaterialLayoutTab(tabs[i], tabs, panels, this);\n }\n }\n this.element_.classList.add(this.CssClasses_.IS_UPGRADED);\n }\n};\n/**\n * Constructor for an individual tab.\n *\n * @constructor\n * @param {HTMLElement} tab The HTML element for the tab.\n * @param {!Array} tabs Array with HTML elements for all tabs.\n * @param {!Array} panels Array with HTML elements for all panels.\n * @param {MaterialLayout} layout The MaterialLayout object that owns the tab.\n */\nfunction MaterialLayoutTab(tab, tabs, panels, layout) {\n if (layout.tabBar_.classList.contains(layout.CssClasses_.JS_RIPPLE_EFFECT)) {\n var rippleContainer = document.createElement('span');\n rippleContainer.classList.add(layout.CssClasses_.RIPPLE_CONTAINER);\n rippleContainer.classList.add(layout.CssClasses_.JS_RIPPLE_EFFECT);\n var ripple = document.createElement('span');\n ripple.classList.add(layout.CssClasses_.RIPPLE);\n rippleContainer.appendChild(ripple);\n tab.appendChild(rippleContainer);\n }\n tab.addEventListener('click', function (e) {\n e.preventDefault();\n var href = tab.href.split('#')[1];\n var panel = layout.content_.querySelector('#' + href);\n layout.resetTabState_(tabs);\n layout.resetPanelState_(panels);\n tab.classList.add(layout.CssClasses_.IS_ACTIVE);\n panel.classList.add(layout.CssClasses_.IS_ACTIVE);\n });\n}\n// The component registers itself. It can assume componentHandler is available\n// in the global scope.\ncomponentHandler.register({\n constructor: MaterialLayout,\n classAsString: 'MaterialLayout',\n cssClass: 'mdl-js-layout'\n});","// Source: https://github.com/darius/requestAnimationFrame/blob/master/requestAnimationFrame.js\n// Adapted from https://gist.github.com/paulirish/1579671 which derived from\n// http://paulirish.com/2011/requestanimationframe-for-smart-animating/\n// http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating\n// requestAnimationFrame polyfill by Erik Möller.\n// Fixes from Paul Irish, Tino Zijdel, Andrew Mao, Klemen Slavič, Darius Bacon\n// MIT license\nif (!Date.now) {\n /**\n * Date.now polyfill.\n * @return {number} the current Date\n */\n Date.now = function () {\n return new Date().getTime();\n };\n Date['now'] = Date.now;\n}\nvar vendors = [\n 'webkit',\n 'moz'\n];\nfor (var i = 0; i < vendors.length && !window.requestAnimationFrame; ++i) {\n var vp = vendors[i];\n window.requestAnimationFrame = window[vp + 'RequestAnimationFrame'];\n window.cancelAnimationFrame = window[vp + 'CancelAnimationFrame'] || window[vp + 'CancelRequestAnimationFrame'];\n window['requestAnimationFrame'] = window.requestAnimationFrame;\n window['cancelAnimationFrame'] = window.cancelAnimationFrame;\n}\nif (/iP(ad|hone|od).*OS 6/.test(window.navigator.userAgent) || !window.requestAnimationFrame || !window.cancelAnimationFrame) {\n var lastTime = 0;\n /**\n * requestAnimationFrame polyfill.\n * @param {!Function} callback the callback function.\n */\n window.requestAnimationFrame = function (callback) {\n var now = Date.now();\n var nextTime = Math.max(lastTime + 16, now);\n return setTimeout(function () {\n callback(lastTime = nextTime);\n }, nextTime - now);\n };\n window.cancelAnimationFrame = clearTimeout;\n window['requestAnimationFrame'] = window.requestAnimationFrame;\n window['cancelAnimationFrame'] = window.cancelAnimationFrame;\n}","/**\n * @license\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Class constructor for Button MDL component.\n * Implements MDL component design pattern defined at:\n * https://github.com/jasonmayes/mdl-component-design-pattern\n *\n * @param {HTMLElement} element The element that will be upgraded.\n */\nvar MaterialButton = function MaterialButton(element) {\n this.element_ = element;\n // Initialize instance.\n this.init();\n};\nwindow['MaterialButton'] = MaterialButton;\n/**\n * Store constants in one place so they can be updated easily.\n *\n * @enum {string | number}\n * @private\n */\nMaterialButton.prototype.Constant_ = {};\n/**\n * Store strings for class names defined by this component that are used in\n * JavaScript. This allows us to simply change it in one place should we\n * decide to modify at a later date.\n *\n * @enum {string}\n * @private\n */\nMaterialButton.prototype.CssClasses_ = {\n RIPPLE_EFFECT: 'mdl-js-ripple-effect',\n RIPPLE_CONTAINER: 'mdl-button__ripple-container',\n RIPPLE: 'mdl-ripple'\n};\n/**\n * Handle blur of element.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialButton.prototype.blurHandler_ = function (event) {\n if (event) {\n this.element_.blur();\n }\n};\n// Public methods.\n/**\n * Disable button.\n *\n * @public\n */\nMaterialButton.prototype.disable = function () {\n this.element_.disabled = true;\n};\nMaterialButton.prototype['disable'] = MaterialButton.prototype.disable;\n/**\n * Enable button.\n *\n * @public\n */\nMaterialButton.prototype.enable = function () {\n this.element_.disabled = false;\n};\nMaterialButton.prototype['enable'] = MaterialButton.prototype.enable;\n/**\n * Initialize element.\n */\nMaterialButton.prototype.init = function () {\n if (this.element_) {\n if (this.element_.classList.contains(this.CssClasses_.RIPPLE_EFFECT)) {\n var rippleContainer = document.createElement('span');\n rippleContainer.classList.add(this.CssClasses_.RIPPLE_CONTAINER);\n this.rippleElement_ = document.createElement('span');\n this.rippleElement_.classList.add(this.CssClasses_.RIPPLE);\n rippleContainer.appendChild(this.rippleElement_);\n this.boundRippleBlurHandler = this.blurHandler_.bind(this);\n this.rippleElement_.addEventListener('mouseup', this.boundRippleBlurHandler);\n this.element_.appendChild(rippleContainer);\n }\n this.boundButtonBlurHandler = this.blurHandler_.bind(this);\n this.element_.addEventListener('mouseup', this.boundButtonBlurHandler);\n this.element_.addEventListener('mouseleave', this.boundButtonBlurHandler);\n }\n};\n/**\n * Downgrade the element.\n *\n * @private\n */\nMaterialButton.prototype.mdlDowngrade_ = function () {\n if (this.rippleElement_) {\n this.rippleElement_.removeEventListener('mouseup', this.boundRippleBlurHandler);\n }\n this.element_.removeEventListener('mouseup', this.boundButtonBlurHandler);\n this.element_.removeEventListener('mouseleave', this.boundButtonBlurHandler);\n};\n/**\n * Public alias for the downgrade method.\n *\n * @public\n */\nMaterialButton.prototype.mdlDowngrade = MaterialButton.prototype.mdlDowngrade_;\nMaterialButton.prototype['mdlDowngrade'] = MaterialButton.prototype.mdlDowngrade;\n// The component registers itself. It can assume componentHandler is available\n// in the global scope.\ncomponentHandler.register({\n constructor: MaterialButton,\n classAsString: 'MaterialButton',\n cssClass: 'mdl-js-button',\n widget: true\n});","/**\n * @license\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Class constructor for Checkbox MDL component.\n * Implements MDL component design pattern defined at:\n * https://github.com/jasonmayes/mdl-component-design-pattern\n *\n * @constructor\n * @param {HTMLElement} element The element that will be upgraded.\n */\nvar MaterialCheckbox = function MaterialCheckbox(element) {\n this.element_ = element;\n // Initialize instance.\n this.init();\n};\nwindow['MaterialCheckbox'] = MaterialCheckbox;\n/**\n * Store constants in one place so they can be updated easily.\n *\n * @enum {string | number}\n * @private\n */\nMaterialCheckbox.prototype.Constant_ = { TINY_TIMEOUT: 0.001 };\n/**\n * Store strings for class names defined by this component that are used in\n * JavaScript. This allows us to simply change it in one place should we\n * decide to modify at a later date.\n *\n * @enum {string}\n * @private\n */\nMaterialCheckbox.prototype.CssClasses_ = {\n INPUT: 'mdl-checkbox__input',\n BOX_OUTLINE: 'mdl-checkbox__box-outline',\n FOCUS_HELPER: 'mdl-checkbox__focus-helper',\n TICK_OUTLINE: 'mdl-checkbox__tick-outline',\n RIPPLE_EFFECT: 'mdl-js-ripple-effect',\n RIPPLE_IGNORE_EVENTS: 'mdl-js-ripple-effect--ignore-events',\n RIPPLE_CONTAINER: 'mdl-checkbox__ripple-container',\n RIPPLE_CENTER: 'mdl-ripple--center',\n RIPPLE: 'mdl-ripple',\n IS_FOCUSED: 'is-focused',\n IS_DISABLED: 'is-disabled',\n IS_CHECKED: 'is-checked',\n IS_UPGRADED: 'is-upgraded'\n};\n/**\n * Handle change of state.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialCheckbox.prototype.onChange_ = function (event) {\n this.updateClasses_();\n};\n/**\n * Handle focus of element.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialCheckbox.prototype.onFocus_ = function (event) {\n this.element_.classList.add(this.CssClasses_.IS_FOCUSED);\n};\n/**\n * Handle lost focus of element.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialCheckbox.prototype.onBlur_ = function (event) {\n this.element_.classList.remove(this.CssClasses_.IS_FOCUSED);\n};\n/**\n * Handle mouseup.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialCheckbox.prototype.onMouseUp_ = function (event) {\n this.blur_();\n};\n/**\n * Handle class updates.\n *\n * @private\n */\nMaterialCheckbox.prototype.updateClasses_ = function () {\n this.checkDisabled();\n this.checkToggleState();\n};\n/**\n * Add blur.\n *\n * @private\n */\nMaterialCheckbox.prototype.blur_ = function () {\n // TODO: figure out why there's a focus event being fired after our blur,\n // so that we can avoid this hack.\n window.setTimeout(function () {\n this.inputElement_.blur();\n }.bind(this), this.Constant_.TINY_TIMEOUT);\n};\n// Public methods.\n/**\n * Check the inputs toggle state and update display.\n *\n * @public\n */\nMaterialCheckbox.prototype.checkToggleState = function () {\n if (this.inputElement_.checked) {\n this.element_.classList.add(this.CssClasses_.IS_CHECKED);\n } else {\n this.element_.classList.remove(this.CssClasses_.IS_CHECKED);\n }\n};\nMaterialCheckbox.prototype['checkToggleState'] = MaterialCheckbox.prototype.checkToggleState;\n/**\n * Check the inputs disabled state and update display.\n *\n * @public\n */\nMaterialCheckbox.prototype.checkDisabled = function () {\n if (this.inputElement_.disabled) {\n this.element_.classList.add(this.CssClasses_.IS_DISABLED);\n } else {\n this.element_.classList.remove(this.CssClasses_.IS_DISABLED);\n }\n};\nMaterialCheckbox.prototype['checkDisabled'] = MaterialCheckbox.prototype.checkDisabled;\n/**\n * Disable checkbox.\n *\n * @public\n */\nMaterialCheckbox.prototype.disable = function () {\n this.inputElement_.disabled = true;\n this.updateClasses_();\n};\nMaterialCheckbox.prototype['disable'] = MaterialCheckbox.prototype.disable;\n/**\n * Enable checkbox.\n *\n * @public\n */\nMaterialCheckbox.prototype.enable = function () {\n this.inputElement_.disabled = false;\n this.updateClasses_();\n};\nMaterialCheckbox.prototype['enable'] = MaterialCheckbox.prototype.enable;\n/**\n * Check checkbox.\n *\n * @public\n */\nMaterialCheckbox.prototype.check = function () {\n this.inputElement_.checked = true;\n this.updateClasses_();\n};\nMaterialCheckbox.prototype['check'] = MaterialCheckbox.prototype.check;\n/**\n * Uncheck checkbox.\n *\n * @public\n */\nMaterialCheckbox.prototype.uncheck = function () {\n this.inputElement_.checked = false;\n this.updateClasses_();\n};\nMaterialCheckbox.prototype['uncheck'] = MaterialCheckbox.prototype.uncheck;\n/**\n * Initialize element.\n */\nMaterialCheckbox.prototype.init = function () {\n if (this.element_) {\n this.inputElement_ = this.element_.querySelector('.' + this.CssClasses_.INPUT);\n var boxOutline = document.createElement('span');\n boxOutline.classList.add(this.CssClasses_.BOX_OUTLINE);\n var tickContainer = document.createElement('span');\n tickContainer.classList.add(this.CssClasses_.FOCUS_HELPER);\n var tickOutline = document.createElement('span');\n tickOutline.classList.add(this.CssClasses_.TICK_OUTLINE);\n boxOutline.appendChild(tickOutline);\n this.element_.appendChild(tickContainer);\n this.element_.appendChild(boxOutline);\n if (this.element_.classList.contains(this.CssClasses_.RIPPLE_EFFECT)) {\n this.element_.classList.add(this.CssClasses_.RIPPLE_IGNORE_EVENTS);\n this.rippleContainerElement_ = document.createElement('span');\n this.rippleContainerElement_.classList.add(this.CssClasses_.RIPPLE_CONTAINER);\n this.rippleContainerElement_.classList.add(this.CssClasses_.RIPPLE_EFFECT);\n this.rippleContainerElement_.classList.add(this.CssClasses_.RIPPLE_CENTER);\n this.boundRippleMouseUp = this.onMouseUp_.bind(this);\n this.rippleContainerElement_.addEventListener('mouseup', this.boundRippleMouseUp);\n var ripple = document.createElement('span');\n ripple.classList.add(this.CssClasses_.RIPPLE);\n this.rippleContainerElement_.appendChild(ripple);\n this.element_.appendChild(this.rippleContainerElement_);\n }\n this.boundInputOnChange = this.onChange_.bind(this);\n this.boundInputOnFocus = this.onFocus_.bind(this);\n this.boundInputOnBlur = this.onBlur_.bind(this);\n this.boundElementMouseUp = this.onMouseUp_.bind(this);\n this.inputElement_.addEventListener('change', this.boundInputOnChange);\n this.inputElement_.addEventListener('focus', this.boundInputOnFocus);\n this.inputElement_.addEventListener('blur', this.boundInputOnBlur);\n this.element_.addEventListener('mouseup', this.boundElementMouseUp);\n this.updateClasses_();\n this.element_.classList.add(this.CssClasses_.IS_UPGRADED);\n }\n};\n/**\n * Downgrade the component.\n *\n * @private\n */\nMaterialCheckbox.prototype.mdlDowngrade_ = function () {\n if (this.rippleContainerElement_) {\n this.rippleContainerElement_.removeEventListener('mouseup', this.boundRippleMouseUp);\n }\n this.inputElement_.removeEventListener('change', this.boundInputOnChange);\n this.inputElement_.removeEventListener('focus', this.boundInputOnFocus);\n this.inputElement_.removeEventListener('blur', this.boundInputOnBlur);\n this.element_.removeEventListener('mouseup', this.boundElementMouseUp);\n};\n/**\n * Public alias for the downgrade method.\n *\n * @public\n */\nMaterialCheckbox.prototype.mdlDowngrade = MaterialCheckbox.prototype.mdlDowngrade_;\nMaterialCheckbox.prototype['mdlDowngrade'] = MaterialCheckbox.prototype.mdlDowngrade;\n// The component registers itself. It can assume componentHandler is available\n// in the global scope.\ncomponentHandler.register({\n constructor: MaterialCheckbox,\n classAsString: 'MaterialCheckbox',\n cssClass: 'mdl-js-checkbox',\n widget: true\n});","/**\n * @license\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Class constructor for icon toggle MDL component.\n * Implements MDL component design pattern defined at:\n * https://github.com/jasonmayes/mdl-component-design-pattern\n *\n * @constructor\n * @param {HTMLElement} element The element that will be upgraded.\n */\nvar MaterialIconToggle = function MaterialIconToggle(element) {\n this.element_ = element;\n // Initialize instance.\n this.init();\n};\nwindow['MaterialIconToggle'] = MaterialIconToggle;\n/**\n * Store constants in one place so they can be updated easily.\n *\n * @enum {string | number}\n * @private\n */\nMaterialIconToggle.prototype.Constant_ = { TINY_TIMEOUT: 0.001 };\n/**\n * Store strings for class names defined by this component that are used in\n * JavaScript. This allows us to simply change it in one place should we\n * decide to modify at a later date.\n *\n * @enum {string}\n * @private\n */\nMaterialIconToggle.prototype.CssClasses_ = {\n INPUT: 'mdl-icon-toggle__input',\n JS_RIPPLE_EFFECT: 'mdl-js-ripple-effect',\n RIPPLE_IGNORE_EVENTS: 'mdl-js-ripple-effect--ignore-events',\n RIPPLE_CONTAINER: 'mdl-icon-toggle__ripple-container',\n RIPPLE_CENTER: 'mdl-ripple--center',\n RIPPLE: 'mdl-ripple',\n IS_FOCUSED: 'is-focused',\n IS_DISABLED: 'is-disabled',\n IS_CHECKED: 'is-checked'\n};\n/**\n * Handle change of state.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialIconToggle.prototype.onChange_ = function (event) {\n this.updateClasses_();\n};\n/**\n * Handle focus of element.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialIconToggle.prototype.onFocus_ = function (event) {\n this.element_.classList.add(this.CssClasses_.IS_FOCUSED);\n};\n/**\n * Handle lost focus of element.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialIconToggle.prototype.onBlur_ = function (event) {\n this.element_.classList.remove(this.CssClasses_.IS_FOCUSED);\n};\n/**\n * Handle mouseup.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialIconToggle.prototype.onMouseUp_ = function (event) {\n this.blur_();\n};\n/**\n * Handle class updates.\n *\n * @private\n */\nMaterialIconToggle.prototype.updateClasses_ = function () {\n this.checkDisabled();\n this.checkToggleState();\n};\n/**\n * Add blur.\n *\n * @private\n */\nMaterialIconToggle.prototype.blur_ = function () {\n // TODO: figure out why there's a focus event being fired after our blur,\n // so that we can avoid this hack.\n window.setTimeout(function () {\n this.inputElement_.blur();\n }.bind(this), this.Constant_.TINY_TIMEOUT);\n};\n// Public methods.\n/**\n * Check the inputs toggle state and update display.\n *\n * @public\n */\nMaterialIconToggle.prototype.checkToggleState = function () {\n if (this.inputElement_.checked) {\n this.element_.classList.add(this.CssClasses_.IS_CHECKED);\n } else {\n this.element_.classList.remove(this.CssClasses_.IS_CHECKED);\n }\n};\nMaterialIconToggle.prototype['checkToggleState'] = MaterialIconToggle.prototype.checkToggleState;\n/**\n * Check the inputs disabled state and update display.\n *\n * @public\n */\nMaterialIconToggle.prototype.checkDisabled = function () {\n if (this.inputElement_.disabled) {\n this.element_.classList.add(this.CssClasses_.IS_DISABLED);\n } else {\n this.element_.classList.remove(this.CssClasses_.IS_DISABLED);\n }\n};\nMaterialIconToggle.prototype['checkDisabled'] = MaterialIconToggle.prototype.checkDisabled;\n/**\n * Disable icon toggle.\n *\n * @public\n */\nMaterialIconToggle.prototype.disable = function () {\n this.inputElement_.disabled = true;\n this.updateClasses_();\n};\nMaterialIconToggle.prototype['disable'] = MaterialIconToggle.prototype.disable;\n/**\n * Enable icon toggle.\n *\n * @public\n */\nMaterialIconToggle.prototype.enable = function () {\n this.inputElement_.disabled = false;\n this.updateClasses_();\n};\nMaterialIconToggle.prototype['enable'] = MaterialIconToggle.prototype.enable;\n/**\n * Check icon toggle.\n *\n * @public\n */\nMaterialIconToggle.prototype.check = function () {\n this.inputElement_.checked = true;\n this.updateClasses_();\n};\nMaterialIconToggle.prototype['check'] = MaterialIconToggle.prototype.check;\n/**\n * Uncheck icon toggle.\n *\n * @public\n */\nMaterialIconToggle.prototype.uncheck = function () {\n this.inputElement_.checked = false;\n this.updateClasses_();\n};\nMaterialIconToggle.prototype['uncheck'] = MaterialIconToggle.prototype.uncheck;\n/**\n * Initialize element.\n */\nMaterialIconToggle.prototype.init = function () {\n if (this.element_) {\n this.inputElement_ = this.element_.querySelector('.' + this.CssClasses_.INPUT);\n if (this.element_.classList.contains(this.CssClasses_.JS_RIPPLE_EFFECT)) {\n this.element_.classList.add(this.CssClasses_.RIPPLE_IGNORE_EVENTS);\n this.rippleContainerElement_ = document.createElement('span');\n this.rippleContainerElement_.classList.add(this.CssClasses_.RIPPLE_CONTAINER);\n this.rippleContainerElement_.classList.add(this.CssClasses_.JS_RIPPLE_EFFECT);\n this.rippleContainerElement_.classList.add(this.CssClasses_.RIPPLE_CENTER);\n this.boundRippleMouseUp = this.onMouseUp_.bind(this);\n this.rippleContainerElement_.addEventListener('mouseup', this.boundRippleMouseUp);\n var ripple = document.createElement('span');\n ripple.classList.add(this.CssClasses_.RIPPLE);\n this.rippleContainerElement_.appendChild(ripple);\n this.element_.appendChild(this.rippleContainerElement_);\n }\n this.boundInputOnChange = this.onChange_.bind(this);\n this.boundInputOnFocus = this.onFocus_.bind(this);\n this.boundInputOnBlur = this.onBlur_.bind(this);\n this.boundElementOnMouseUp = this.onMouseUp_.bind(this);\n this.inputElement_.addEventListener('change', this.boundInputOnChange);\n this.inputElement_.addEventListener('focus', this.boundInputOnFocus);\n this.inputElement_.addEventListener('blur', this.boundInputOnBlur);\n this.element_.addEventListener('mouseup', this.boundElementOnMouseUp);\n this.updateClasses_();\n this.element_.classList.add('is-upgraded');\n }\n};\n/**\n * Downgrade the component\n *\n * @private\n */\nMaterialIconToggle.prototype.mdlDowngrade_ = function () {\n if (this.rippleContainerElement_) {\n this.rippleContainerElement_.removeEventListener('mouseup', this.boundRippleMouseUp);\n }\n this.inputElement_.removeEventListener('change', this.boundInputOnChange);\n this.inputElement_.removeEventListener('focus', this.boundInputOnFocus);\n this.inputElement_.removeEventListener('blur', this.boundInputOnBlur);\n this.element_.removeEventListener('mouseup', this.boundElementOnMouseUp);\n};\n/**\n * Public alias for the downgrade method.\n *\n * @public\n */\nMaterialIconToggle.prototype.mdlDowngrade = MaterialIconToggle.prototype.mdlDowngrade_;\nMaterialIconToggle.prototype['mdlDowngrade'] = MaterialIconToggle.prototype.mdlDowngrade;\n// The component registers itself. It can assume componentHandler is available\n// in the global scope.\ncomponentHandler.register({\n constructor: MaterialIconToggle,\n classAsString: 'MaterialIconToggle',\n cssClass: 'mdl-js-icon-toggle',\n widget: true\n});","/**\n * @license\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Class constructor for dropdown MDL component.\n * Implements MDL component design pattern defined at:\n * https://github.com/jasonmayes/mdl-component-design-pattern\n *\n * @constructor\n * @param {HTMLElement} element The element that will be upgraded.\n */\nvar MaterialMenu = function MaterialMenu(element) {\n this.element_ = element;\n // Initialize instance.\n this.init();\n};\nwindow['MaterialMenu'] = MaterialMenu;\n/**\n * Store constants in one place so they can be updated easily.\n *\n * @enum {string | number}\n * @private\n */\nMaterialMenu.prototype.Constant_ = {\n // Total duration of the menu animation.\n TRANSITION_DURATION_SECONDS: 0.3,\n // The fraction of the total duration we want to use for menu item animations.\n TRANSITION_DURATION_FRACTION: 0.8,\n // How long the menu stays open after choosing an option (so the user can see\n // the ripple).\n CLOSE_TIMEOUT: 150\n};\n/**\n * Keycodes, for code readability.\n *\n * @enum {number}\n * @private\n */\nMaterialMenu.prototype.Keycodes_ = {\n ENTER: 13,\n ESCAPE: 27,\n SPACE: 32,\n UP_ARROW: 38,\n DOWN_ARROW: 40\n};\n/**\n * Store strings for class names defined by this component that are used in\n * JavaScript. This allows us to simply change it in one place should we\n * decide to modify at a later date.\n *\n * @enum {string}\n * @private\n */\nMaterialMenu.prototype.CssClasses_ = {\n CONTAINER: 'mdl-menu__container',\n OUTLINE: 'mdl-menu__outline',\n ITEM: 'mdl-menu__item',\n ITEM_RIPPLE_CONTAINER: 'mdl-menu__item-ripple-container',\n RIPPLE_EFFECT: 'mdl-js-ripple-effect',\n RIPPLE_IGNORE_EVENTS: 'mdl-js-ripple-effect--ignore-events',\n RIPPLE: 'mdl-ripple',\n // Statuses\n IS_UPGRADED: 'is-upgraded',\n IS_VISIBLE: 'is-visible',\n IS_ANIMATING: 'is-animating',\n // Alignment options\n BOTTOM_LEFT: 'mdl-menu--bottom-left',\n // This is the default.\n BOTTOM_RIGHT: 'mdl-menu--bottom-right',\n TOP_LEFT: 'mdl-menu--top-left',\n TOP_RIGHT: 'mdl-menu--top-right',\n UNALIGNED: 'mdl-menu--unaligned'\n};\n/**\n * Initialize element.\n */\nMaterialMenu.prototype.init = function () {\n if (this.element_) {\n // Create container for the menu.\n var container = document.createElement('div');\n container.classList.add(this.CssClasses_.CONTAINER);\n this.element_.parentElement.insertBefore(container, this.element_);\n this.element_.parentElement.removeChild(this.element_);\n container.appendChild(this.element_);\n this.container_ = container;\n // Create outline for the menu (shadow and background).\n var outline = document.createElement('div');\n outline.classList.add(this.CssClasses_.OUTLINE);\n this.outline_ = outline;\n container.insertBefore(outline, this.element_);\n // Find the \"for\" element and bind events to it.\n var forElId = this.element_.getAttribute('for');\n var forEl = null;\n if (forElId) {\n forEl = document.getElementById(forElId);\n if (forEl) {\n this.forElement_ = forEl;\n forEl.addEventListener('click', this.handleForClick_.bind(this));\n forEl.addEventListener('keydown', this.handleForKeyboardEvent_.bind(this));\n }\n }\n var items = this.element_.querySelectorAll('.' + this.CssClasses_.ITEM);\n this.boundItemKeydown_ = this.handleItemKeyboardEvent_.bind(this);\n this.boundItemClick_ = this.handleItemClick_.bind(this);\n for (var i = 0; i < items.length; i++) {\n // Add a listener to each menu item.\n items[i].addEventListener('click', this.boundItemClick_);\n // Add a tab index to each menu item.\n items[i].tabIndex = '-1';\n // Add a keyboard listener to each menu item.\n items[i].addEventListener('keydown', this.boundItemKeydown_);\n }\n // Add ripple classes to each item, if the user has enabled ripples.\n if (this.element_.classList.contains(this.CssClasses_.RIPPLE_EFFECT)) {\n this.element_.classList.add(this.CssClasses_.RIPPLE_IGNORE_EVENTS);\n for (i = 0; i < items.length; i++) {\n var item = items[i];\n var rippleContainer = document.createElement('span');\n rippleContainer.classList.add(this.CssClasses_.ITEM_RIPPLE_CONTAINER);\n var ripple = document.createElement('span');\n ripple.classList.add(this.CssClasses_.RIPPLE);\n rippleContainer.appendChild(ripple);\n item.appendChild(rippleContainer);\n item.classList.add(this.CssClasses_.RIPPLE_EFFECT);\n }\n }\n // Copy alignment classes to the container, so the outline can use them.\n if (this.element_.classList.contains(this.CssClasses_.BOTTOM_LEFT)) {\n this.outline_.classList.add(this.CssClasses_.BOTTOM_LEFT);\n }\n if (this.element_.classList.contains(this.CssClasses_.BOTTOM_RIGHT)) {\n this.outline_.classList.add(this.CssClasses_.BOTTOM_RIGHT);\n }\n if (this.element_.classList.contains(this.CssClasses_.TOP_LEFT)) {\n this.outline_.classList.add(this.CssClasses_.TOP_LEFT);\n }\n if (this.element_.classList.contains(this.CssClasses_.TOP_RIGHT)) {\n this.outline_.classList.add(this.CssClasses_.TOP_RIGHT);\n }\n if (this.element_.classList.contains(this.CssClasses_.UNALIGNED)) {\n this.outline_.classList.add(this.CssClasses_.UNALIGNED);\n }\n container.classList.add(this.CssClasses_.IS_UPGRADED);\n }\n};\n/**\n * Handles a click on the \"for\" element, by positioning the menu and then\n * toggling it.\n *\n * @param {Event} evt The event that fired.\n * @private\n */\nMaterialMenu.prototype.handleForClick_ = function (evt) {\n if (this.element_ && this.forElement_) {\n var rect = this.forElement_.getBoundingClientRect();\n var forRect = this.forElement_.parentElement.getBoundingClientRect();\n if (this.element_.classList.contains(this.CssClasses_.UNALIGNED)) {\n } else if (this.element_.classList.contains(this.CssClasses_.BOTTOM_RIGHT)) {\n // Position below the \"for\" element, aligned to its right.\n this.container_.style.right = forRect.right - rect.right + 'px';\n this.container_.style.top = this.forElement_.offsetTop + this.forElement_.offsetHeight + 'px';\n } else if (this.element_.classList.contains(this.CssClasses_.TOP_LEFT)) {\n // Position above the \"for\" element, aligned to its left.\n this.container_.style.left = this.forElement_.offsetLeft + 'px';\n this.container_.style.bottom = forRect.bottom - rect.top + 'px';\n } else if (this.element_.classList.contains(this.CssClasses_.TOP_RIGHT)) {\n // Position above the \"for\" element, aligned to its right.\n this.container_.style.right = forRect.right - rect.right + 'px';\n this.container_.style.bottom = forRect.bottom - rect.top + 'px';\n } else {\n // Default: position below the \"for\" element, aligned to its left.\n this.container_.style.left = this.forElement_.offsetLeft + 'px';\n this.container_.style.top = this.forElement_.offsetTop + this.forElement_.offsetHeight + 'px';\n }\n }\n this.toggle(evt);\n};\n/**\n * Handles a keyboard event on the \"for\" element.\n *\n * @param {Event} evt The event that fired.\n * @private\n */\nMaterialMenu.prototype.handleForKeyboardEvent_ = function (evt) {\n if (this.element_ && this.container_ && this.forElement_) {\n var items = this.element_.querySelectorAll('.' + this.CssClasses_.ITEM + ':not([disabled])');\n if (items && items.length > 0 && this.container_.classList.contains(this.CssClasses_.IS_VISIBLE)) {\n if (evt.keyCode === this.Keycodes_.UP_ARROW) {\n evt.preventDefault();\n items[items.length - 1].focus();\n } else if (evt.keyCode === this.Keycodes_.DOWN_ARROW) {\n evt.preventDefault();\n items[0].focus();\n }\n }\n }\n};\n/**\n * Handles a keyboard event on an item.\n *\n * @param {Event} evt The event that fired.\n * @private\n */\nMaterialMenu.prototype.handleItemKeyboardEvent_ = function (evt) {\n if (this.element_ && this.container_) {\n var items = this.element_.querySelectorAll('.' + this.CssClasses_.ITEM + ':not([disabled])');\n if (items && items.length > 0 && this.container_.classList.contains(this.CssClasses_.IS_VISIBLE)) {\n var currentIndex = Array.prototype.slice.call(items).indexOf(evt.target);\n if (evt.keyCode === this.Keycodes_.UP_ARROW) {\n evt.preventDefault();\n if (currentIndex > 0) {\n items[currentIndex - 1].focus();\n } else {\n items[items.length - 1].focus();\n }\n } else if (evt.keyCode === this.Keycodes_.DOWN_ARROW) {\n evt.preventDefault();\n if (items.length > currentIndex + 1) {\n items[currentIndex + 1].focus();\n } else {\n items[0].focus();\n }\n } else if (evt.keyCode === this.Keycodes_.SPACE || evt.keyCode === this.Keycodes_.ENTER) {\n evt.preventDefault();\n // Send mousedown and mouseup to trigger ripple.\n var e = new MouseEvent('mousedown');\n evt.target.dispatchEvent(e);\n e = new MouseEvent('mouseup');\n evt.target.dispatchEvent(e);\n // Send click.\n evt.target.click();\n } else if (evt.keyCode === this.Keycodes_.ESCAPE) {\n evt.preventDefault();\n this.hide();\n }\n }\n }\n};\n/**\n * Handles a click event on an item.\n *\n * @param {Event} evt The event that fired.\n * @private\n */\nMaterialMenu.prototype.handleItemClick_ = function (evt) {\n if (evt.target.hasAttribute('disabled')) {\n evt.stopPropagation();\n } else {\n // Wait some time before closing menu, so the user can see the ripple.\n this.closing_ = true;\n window.setTimeout(function (evt) {\n this.hide();\n this.closing_ = false;\n }.bind(this), this.Constant_.CLOSE_TIMEOUT);\n }\n};\n/**\n * Calculates the initial clip (for opening the menu) or final clip (for closing\n * it), and applies it. This allows us to animate from or to the correct point,\n * that is, the point it's aligned to in the \"for\" element.\n *\n * @param {number} height Height of the clip rectangle\n * @param {number} width Width of the clip rectangle\n * @private\n */\nMaterialMenu.prototype.applyClip_ = function (height, width) {\n if (this.element_.classList.contains(this.CssClasses_.UNALIGNED)) {\n // Do not clip.\n this.element_.style.clip = '';\n } else if (this.element_.classList.contains(this.CssClasses_.BOTTOM_RIGHT)) {\n // Clip to the top right corner of the menu.\n this.element_.style.clip = 'rect(0 ' + width + 'px ' + '0 ' + width + 'px)';\n } else if (this.element_.classList.contains(this.CssClasses_.TOP_LEFT)) {\n // Clip to the bottom left corner of the menu.\n this.element_.style.clip = 'rect(' + height + 'px 0 ' + height + 'px 0)';\n } else if (this.element_.classList.contains(this.CssClasses_.TOP_RIGHT)) {\n // Clip to the bottom right corner of the menu.\n this.element_.style.clip = 'rect(' + height + 'px ' + width + 'px ' + height + 'px ' + width + 'px)';\n } else {\n // Default: do not clip (same as clipping to the top left corner).\n this.element_.style.clip = '';\n }\n};\n/**\n * Adds an event listener to clean up after the animation ends.\n *\n * @private\n */\nMaterialMenu.prototype.addAnimationEndListener_ = function () {\n var cleanup = function () {\n this.element_.removeEventListener('transitionend', cleanup);\n this.element_.removeEventListener('webkitTransitionEnd', cleanup);\n this.element_.classList.remove(this.CssClasses_.IS_ANIMATING);\n }.bind(this);\n // Remove animation class once the transition is done.\n this.element_.addEventListener('transitionend', cleanup);\n this.element_.addEventListener('webkitTransitionEnd', cleanup);\n};\n/**\n * Displays the menu.\n *\n * @public\n */\nMaterialMenu.prototype.show = function (evt) {\n if (this.element_ && this.container_ && this.outline_) {\n // Measure the inner element.\n var height = this.element_.getBoundingClientRect().height;\n var width = this.element_.getBoundingClientRect().width;\n // Apply the inner element's size to the container and outline.\n this.container_.style.width = width + 'px';\n this.container_.style.height = height + 'px';\n this.outline_.style.width = width + 'px';\n this.outline_.style.height = height + 'px';\n var transitionDuration = this.Constant_.TRANSITION_DURATION_SECONDS * this.Constant_.TRANSITION_DURATION_FRACTION;\n // Calculate transition delays for individual menu items, so that they fade\n // in one at a time.\n var items = this.element_.querySelectorAll('.' + this.CssClasses_.ITEM);\n for (var i = 0; i < items.length; i++) {\n var itemDelay = null;\n if (this.element_.classList.contains(this.CssClasses_.TOP_LEFT) || this.element_.classList.contains(this.CssClasses_.TOP_RIGHT)) {\n itemDelay = (height - items[i].offsetTop - items[i].offsetHeight) / height * transitionDuration + 's';\n } else {\n itemDelay = items[i].offsetTop / height * transitionDuration + 's';\n }\n items[i].style.transitionDelay = itemDelay;\n }\n // Apply the initial clip to the text before we start animating.\n this.applyClip_(height, width);\n // Wait for the next frame, turn on animation, and apply the final clip.\n // Also make it visible. This triggers the transitions.\n window.requestAnimationFrame(function () {\n this.element_.classList.add(this.CssClasses_.IS_ANIMATING);\n this.element_.style.clip = 'rect(0 ' + width + 'px ' + height + 'px 0)';\n this.container_.classList.add(this.CssClasses_.IS_VISIBLE);\n }.bind(this));\n // Clean up after the animation is complete.\n this.addAnimationEndListener_();\n // Add a click listener to the document, to close the menu.\n var callback = function (e) {\n // Check to see if the document is processing the same event that\n // displayed the menu in the first place. If so, do nothing.\n // Also check to see if the menu is in the process of closing itself, and\n // do nothing in that case.\n // Also check if the clicked element is a menu item\n // if so, do nothing.\n if (e !== evt && !this.closing_ && e.target.parentNode !== this.element_) {\n document.removeEventListener('click', callback);\n this.hide();\n }\n }.bind(this);\n document.addEventListener('click', callback);\n }\n};\nMaterialMenu.prototype['show'] = MaterialMenu.prototype.show;\n/**\n * Hides the menu.\n *\n * @public\n */\nMaterialMenu.prototype.hide = function () {\n if (this.element_ && this.container_ && this.outline_) {\n var items = this.element_.querySelectorAll('.' + this.CssClasses_.ITEM);\n // Remove all transition delays; menu items fade out concurrently.\n for (var i = 0; i < items.length; i++) {\n items[i].style.transitionDelay = null;\n }\n // Measure the inner element.\n var rect = this.element_.getBoundingClientRect();\n var height = rect.height;\n var width = rect.width;\n // Turn on animation, and apply the final clip. Also make invisible.\n // This triggers the transitions.\n this.element_.classList.add(this.CssClasses_.IS_ANIMATING);\n this.applyClip_(height, width);\n this.container_.classList.remove(this.CssClasses_.IS_VISIBLE);\n // Clean up after the animation is complete.\n this.addAnimationEndListener_();\n }\n};\nMaterialMenu.prototype['hide'] = MaterialMenu.prototype.hide;\n/**\n * Displays or hides the menu, depending on current state.\n *\n * @public\n */\nMaterialMenu.prototype.toggle = function (evt) {\n if (this.container_.classList.contains(this.CssClasses_.IS_VISIBLE)) {\n this.hide();\n } else {\n this.show(evt);\n }\n};\nMaterialMenu.prototype['toggle'] = MaterialMenu.prototype.toggle;\n/**\n * Downgrade the component.\n *\n * @private\n */\nMaterialMenu.prototype.mdlDowngrade_ = function () {\n var items = this.element_.querySelectorAll('.' + this.CssClasses_.ITEM);\n for (var i = 0; i < items.length; i++) {\n items[i].removeEventListener('click', this.boundItemClick_);\n items[i].removeEventListener('keydown', this.boundItemKeydown_);\n }\n};\n/**\n * Public alias for the downgrade method.\n *\n * @public\n */\nMaterialMenu.prototype.mdlDowngrade = MaterialMenu.prototype.mdlDowngrade_;\nMaterialMenu.prototype['mdlDowngrade'] = MaterialMenu.prototype.mdlDowngrade;\n// The component registers itself. It can assume componentHandler is available\n// in the global scope.\ncomponentHandler.register({\n constructor: MaterialMenu,\n classAsString: 'MaterialMenu',\n cssClass: 'mdl-js-menu',\n widget: true\n});","/**\n * @license\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Class constructor for Progress MDL component.\n * Implements MDL component design pattern defined at:\n * https://github.com/jasonmayes/mdl-component-design-pattern\n *\n * @constructor\n * @param {HTMLElement} element The element that will be upgraded.\n */\nvar MaterialProgress = function MaterialProgress(element) {\n this.element_ = element;\n // Initialize instance.\n this.init();\n};\nwindow['MaterialProgress'] = MaterialProgress;\n/**\n * Store constants in one place so they can be updated easily.\n *\n * @enum {string | number}\n * @private\n */\nMaterialProgress.prototype.Constant_ = {};\n/**\n * Store strings for class names defined by this component that are used in\n * JavaScript. This allows us to simply change it in one place should we\n * decide to modify at a later date.\n *\n * @enum {string}\n * @private\n */\nMaterialProgress.prototype.CssClasses_ = { INDETERMINATE_CLASS: 'mdl-progress__indeterminate' };\n/**\n * Set the current progress of the progressbar.\n *\n * @param {number} p Percentage of the progress (0-100)\n * @public\n */\nMaterialProgress.prototype.setProgress = function (p) {\n if (this.element_.classList.contains(this.CssClasses_.INDETERMINATE_CLASS)) {\n return;\n }\n this.progressbar_.style.width = p + '%';\n};\nMaterialProgress.prototype['setProgress'] = MaterialProgress.prototype.setProgress;\n/**\n * Set the current progress of the buffer.\n *\n * @param {number} p Percentage of the buffer (0-100)\n * @public\n */\nMaterialProgress.prototype.setBuffer = function (p) {\n this.bufferbar_.style.width = p + '%';\n this.auxbar_.style.width = 100 - p + '%';\n};\nMaterialProgress.prototype['setBuffer'] = MaterialProgress.prototype.setBuffer;\n/**\n * Initialize element.\n */\nMaterialProgress.prototype.init = function () {\n if (this.element_) {\n var el = document.createElement('div');\n el.className = 'progressbar bar bar1';\n this.element_.appendChild(el);\n this.progressbar_ = el;\n el = document.createElement('div');\n el.className = 'bufferbar bar bar2';\n this.element_.appendChild(el);\n this.bufferbar_ = el;\n el = document.createElement('div');\n el.className = 'auxbar bar bar3';\n this.element_.appendChild(el);\n this.auxbar_ = el;\n this.progressbar_.style.width = '0%';\n this.bufferbar_.style.width = '100%';\n this.auxbar_.style.width = '0%';\n this.element_.classList.add('is-upgraded');\n }\n};\n/**\n * Downgrade the component\n *\n * @private\n */\nMaterialProgress.prototype.mdlDowngrade_ = function () {\n while (this.element_.firstChild) {\n this.element_.removeChild(this.element_.firstChild);\n }\n};\n/**\n * Public alias for the downgrade method.\n *\n * @public\n */\nMaterialProgress.prototype.mdlDowngrade = MaterialProgress.prototype.mdlDowngrade_;\nMaterialProgress.prototype['mdlDowngrade'] = MaterialProgress.prototype.mdlDowngrade;\n// The component registers itself. It can assume componentHandler is available\n// in the global scope.\ncomponentHandler.register({\n constructor: MaterialProgress,\n classAsString: 'MaterialProgress',\n cssClass: 'mdl-js-progress',\n widget: true\n});","/**\n * @license\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Class constructor for Radio MDL component.\n * Implements MDL component design pattern defined at:\n * https://github.com/jasonmayes/mdl-component-design-pattern\n *\n * @constructor\n * @param {HTMLElement} element The element that will be upgraded.\n */\nvar MaterialRadio = function MaterialRadio(element) {\n this.element_ = element;\n // Initialize instance.\n this.init();\n};\nwindow['MaterialRadio'] = MaterialRadio;\n/**\n * Store constants in one place so they can be updated easily.\n *\n * @enum {string | number}\n * @private\n */\nMaterialRadio.prototype.Constant_ = { TINY_TIMEOUT: 0.001 };\n/**\n * Store strings for class names defined by this component that are used in\n * JavaScript. This allows us to simply change it in one place should we\n * decide to modify at a later date.\n *\n * @enum {string}\n * @private\n */\nMaterialRadio.prototype.CssClasses_ = {\n IS_FOCUSED: 'is-focused',\n IS_DISABLED: 'is-disabled',\n IS_CHECKED: 'is-checked',\n IS_UPGRADED: 'is-upgraded',\n JS_RADIO: 'mdl-js-radio',\n RADIO_BTN: 'mdl-radio__button',\n RADIO_OUTER_CIRCLE: 'mdl-radio__outer-circle',\n RADIO_INNER_CIRCLE: 'mdl-radio__inner-circle',\n RIPPLE_EFFECT: 'mdl-js-ripple-effect',\n RIPPLE_IGNORE_EVENTS: 'mdl-js-ripple-effect--ignore-events',\n RIPPLE_CONTAINER: 'mdl-radio__ripple-container',\n RIPPLE_CENTER: 'mdl-ripple--center',\n RIPPLE: 'mdl-ripple'\n};\n/**\n * Handle change of state.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialRadio.prototype.onChange_ = function (event) {\n // Since other radio buttons don't get change events, we need to look for\n // them to update their classes.\n var radios = document.getElementsByClassName(this.CssClasses_.JS_RADIO);\n for (var i = 0; i < radios.length; i++) {\n var button = radios[i].querySelector('.' + this.CssClasses_.RADIO_BTN);\n // Different name == different group, so no point updating those.\n if (button.getAttribute('name') === this.btnElement_.getAttribute('name')) {\n radios[i]['MaterialRadio'].updateClasses_();\n }\n }\n};\n/**\n * Handle focus.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialRadio.prototype.onFocus_ = function (event) {\n this.element_.classList.add(this.CssClasses_.IS_FOCUSED);\n};\n/**\n * Handle lost focus.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialRadio.prototype.onBlur_ = function (event) {\n this.element_.classList.remove(this.CssClasses_.IS_FOCUSED);\n};\n/**\n * Handle mouseup.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialRadio.prototype.onMouseup_ = function (event) {\n this.blur_();\n};\n/**\n * Update classes.\n *\n * @private\n */\nMaterialRadio.prototype.updateClasses_ = function () {\n this.checkDisabled();\n this.checkToggleState();\n};\n/**\n * Add blur.\n *\n * @private\n */\nMaterialRadio.prototype.blur_ = function () {\n // TODO: figure out why there's a focus event being fired after our blur,\n // so that we can avoid this hack.\n window.setTimeout(function () {\n this.btnElement_.blur();\n }.bind(this), this.Constant_.TINY_TIMEOUT);\n};\n// Public methods.\n/**\n * Check the components disabled state.\n *\n * @public\n */\nMaterialRadio.prototype.checkDisabled = function () {\n if (this.btnElement_.disabled) {\n this.element_.classList.add(this.CssClasses_.IS_DISABLED);\n } else {\n this.element_.classList.remove(this.CssClasses_.IS_DISABLED);\n }\n};\nMaterialRadio.prototype['checkDisabled'] = MaterialRadio.prototype.checkDisabled;\n/**\n * Check the components toggled state.\n *\n * @public\n */\nMaterialRadio.prototype.checkToggleState = function () {\n if (this.btnElement_.checked) {\n this.element_.classList.add(this.CssClasses_.IS_CHECKED);\n } else {\n this.element_.classList.remove(this.CssClasses_.IS_CHECKED);\n }\n};\nMaterialRadio.prototype['checkToggleState'] = MaterialRadio.prototype.checkToggleState;\n/**\n * Disable radio.\n *\n * @public\n */\nMaterialRadio.prototype.disable = function () {\n this.btnElement_.disabled = true;\n this.updateClasses_();\n};\nMaterialRadio.prototype['disable'] = MaterialRadio.prototype.disable;\n/**\n * Enable radio.\n *\n * @public\n */\nMaterialRadio.prototype.enable = function () {\n this.btnElement_.disabled = false;\n this.updateClasses_();\n};\nMaterialRadio.prototype['enable'] = MaterialRadio.prototype.enable;\n/**\n * Check radio.\n *\n * @public\n */\nMaterialRadio.prototype.check = function () {\n this.btnElement_.checked = true;\n this.updateClasses_();\n};\nMaterialRadio.prototype['check'] = MaterialRadio.prototype.check;\n/**\n * Uncheck radio.\n *\n * @public\n */\nMaterialRadio.prototype.uncheck = function () {\n this.btnElement_.checked = false;\n this.updateClasses_();\n};\nMaterialRadio.prototype['uncheck'] = MaterialRadio.prototype.uncheck;\n/**\n * Initialize element.\n */\nMaterialRadio.prototype.init = function () {\n if (this.element_) {\n this.btnElement_ = this.element_.querySelector('.' + this.CssClasses_.RADIO_BTN);\n this.boundChangeHandler_ = this.onChange_.bind(this);\n this.boundFocusHandler_ = this.onChange_.bind(this);\n this.boundBlurHandler_ = this.onBlur_.bind(this);\n this.boundMouseUpHandler_ = this.onMouseup_.bind(this);\n var outerCircle = document.createElement('span');\n outerCircle.classList.add(this.CssClasses_.RADIO_OUTER_CIRCLE);\n var innerCircle = document.createElement('span');\n innerCircle.classList.add(this.CssClasses_.RADIO_INNER_CIRCLE);\n this.element_.appendChild(outerCircle);\n this.element_.appendChild(innerCircle);\n var rippleContainer;\n if (this.element_.classList.contains(this.CssClasses_.RIPPLE_EFFECT)) {\n this.element_.classList.add(this.CssClasses_.RIPPLE_IGNORE_EVENTS);\n rippleContainer = document.createElement('span');\n rippleContainer.classList.add(this.CssClasses_.RIPPLE_CONTAINER);\n rippleContainer.classList.add(this.CssClasses_.RIPPLE_EFFECT);\n rippleContainer.classList.add(this.CssClasses_.RIPPLE_CENTER);\n rippleContainer.addEventListener('mouseup', this.boundMouseUpHandler_);\n var ripple = document.createElement('span');\n ripple.classList.add(this.CssClasses_.RIPPLE);\n rippleContainer.appendChild(ripple);\n this.element_.appendChild(rippleContainer);\n }\n this.btnElement_.addEventListener('change', this.boundChangeHandler_);\n this.btnElement_.addEventListener('focus', this.boundFocusHandler_);\n this.btnElement_.addEventListener('blur', this.boundBlurHandler_);\n this.element_.addEventListener('mouseup', this.boundMouseUpHandler_);\n this.updateClasses_();\n this.element_.classList.add(this.CssClasses_.IS_UPGRADED);\n }\n};\n/**\n * Downgrade the element.\n *\n * @private\n */\nMaterialRadio.prototype.mdlDowngrade_ = function () {\n var rippleContainer = this.element_.querySelector('.' + this.CssClasses_.RIPPLE_CONTAINER);\n this.btnElement_.removeEventListener('change', this.boundChangeHandler_);\n this.btnElement_.removeEventListener('focus', this.boundFocusHandler_);\n this.btnElement_.removeEventListener('blur', this.boundBlurHandler_);\n this.element_.removeEventListener('mouseup', this.boundMouseUpHandler_);\n if (rippleContainer) {\n rippleContainer.removeEventListener('mouseup', this.boundMouseUpHandler_);\n this.element_.removeChild(rippleContainer);\n }\n};\n/**\n * Public alias for the downgrade method.\n *\n * @public\n */\nMaterialRadio.prototype.mdlDowngrade = MaterialRadio.prototype.mdlDowngrade_;\nMaterialRadio.prototype['mdlDowngrade'] = MaterialRadio.prototype.mdlDowngrade;\n// The component registers itself. It can assume componentHandler is available\n// in the global scope.\ncomponentHandler.register({\n constructor: MaterialRadio,\n classAsString: 'MaterialRadio',\n cssClass: 'mdl-js-radio',\n widget: true\n});","/**\n * @license\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Class constructor for Slider MDL component.\n * Implements MDL component design pattern defined at:\n * https://github.com/jasonmayes/mdl-component-design-pattern\n *\n * @constructor\n * @param {HTMLElement} element The element that will be upgraded.\n */\nvar MaterialSlider = function MaterialSlider(element) {\n this.element_ = element;\n // Browser feature detection.\n this.isIE_ = window.navigator.msPointerEnabled;\n // Initialize instance.\n this.init();\n};\nwindow['MaterialSlider'] = MaterialSlider;\n/**\n * Store constants in one place so they can be updated easily.\n *\n * @enum {string | number}\n * @private\n */\nMaterialSlider.prototype.Constant_ = {};\n/**\n * Store strings for class names defined by this component that are used in\n * JavaScript. This allows us to simply change it in one place should we\n * decide to modify at a later date.\n *\n * @enum {string}\n * @private\n */\nMaterialSlider.prototype.CssClasses_ = {\n IE_CONTAINER: 'mdl-slider__ie-container',\n SLIDER_CONTAINER: 'mdl-slider__container',\n BACKGROUND_FLEX: 'mdl-slider__background-flex',\n BACKGROUND_LOWER: 'mdl-slider__background-lower',\n BACKGROUND_UPPER: 'mdl-slider__background-upper',\n IS_LOWEST_VALUE: 'is-lowest-value',\n IS_UPGRADED: 'is-upgraded'\n};\n/**\n * Handle input on element.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialSlider.prototype.onInput_ = function (event) {\n this.updateValueStyles_();\n};\n/**\n * Handle change on element.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialSlider.prototype.onChange_ = function (event) {\n this.updateValueStyles_();\n};\n/**\n * Handle mouseup on element.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialSlider.prototype.onMouseUp_ = function (event) {\n event.target.blur();\n};\n/**\n * Handle mousedown on container element.\n * This handler is purpose is to not require the use to click\n * exactly on the 2px slider element, as FireFox seems to be very\n * strict about this.\n *\n * @param {Event} event The event that fired.\n * @private\n * @suppress {missingProperties}\n */\nMaterialSlider.prototype.onContainerMouseDown_ = function (event) {\n // If this click is not on the parent element (but rather some child)\n // ignore. It may still bubble up.\n if (event.target !== this.element_.parentElement) {\n return;\n }\n // Discard the original event and create a new event that\n // is on the slider element.\n event.preventDefault();\n var newEvent = new MouseEvent('mousedown', {\n target: event.target,\n buttons: event.buttons,\n clientX: event.clientX,\n clientY: this.element_.getBoundingClientRect().y\n });\n this.element_.dispatchEvent(newEvent);\n};\n/**\n * Handle updating of values.\n *\n * @private\n */\nMaterialSlider.prototype.updateValueStyles_ = function () {\n // Calculate and apply percentages to div structure behind slider.\n var fraction = (this.element_.value - this.element_.min) / (this.element_.max - this.element_.min);\n if (fraction === 0) {\n this.element_.classList.add(this.CssClasses_.IS_LOWEST_VALUE);\n } else {\n this.element_.classList.remove(this.CssClasses_.IS_LOWEST_VALUE);\n }\n if (!this.isIE_) {\n this.backgroundLower_.style.flex = fraction;\n this.backgroundLower_.style.webkitFlex = fraction;\n this.backgroundUpper_.style.flex = 1 - fraction;\n this.backgroundUpper_.style.webkitFlex = 1 - fraction;\n }\n};\n// Public methods.\n/**\n * Disable slider.\n *\n * @public\n */\nMaterialSlider.prototype.disable = function () {\n this.element_.disabled = true;\n};\nMaterialSlider.prototype['disable'] = MaterialSlider.prototype.disable;\n/**\n * Enable slider.\n *\n * @public\n */\nMaterialSlider.prototype.enable = function () {\n this.element_.disabled = false;\n};\nMaterialSlider.prototype['enable'] = MaterialSlider.prototype.enable;\n/**\n * Update slider value.\n *\n * @param {number} value The value to which to set the control (optional).\n * @public\n */\nMaterialSlider.prototype.change = function (value) {\n if (typeof value !== 'undefined') {\n this.element_.value = value;\n }\n this.updateValueStyles_();\n};\nMaterialSlider.prototype['change'] = MaterialSlider.prototype.change;\n/**\n * Initialize element.\n */\nMaterialSlider.prototype.init = function () {\n if (this.element_) {\n if (this.isIE_) {\n // Since we need to specify a very large height in IE due to\n // implementation limitations, we add a parent here that trims it down to\n // a reasonable size.\n var containerIE = document.createElement('div');\n containerIE.classList.add(this.CssClasses_.IE_CONTAINER);\n this.element_.parentElement.insertBefore(containerIE, this.element_);\n this.element_.parentElement.removeChild(this.element_);\n containerIE.appendChild(this.element_);\n } else {\n // For non-IE browsers, we need a div structure that sits behind the\n // slider and allows us to style the left and right sides of it with\n // different colors.\n var container = document.createElement('div');\n container.classList.add(this.CssClasses_.SLIDER_CONTAINER);\n this.element_.parentElement.insertBefore(container, this.element_);\n this.element_.parentElement.removeChild(this.element_);\n container.appendChild(this.element_);\n var backgroundFlex = document.createElement('div');\n backgroundFlex.classList.add(this.CssClasses_.BACKGROUND_FLEX);\n container.appendChild(backgroundFlex);\n this.backgroundLower_ = document.createElement('div');\n this.backgroundLower_.classList.add(this.CssClasses_.BACKGROUND_LOWER);\n backgroundFlex.appendChild(this.backgroundLower_);\n this.backgroundUpper_ = document.createElement('div');\n this.backgroundUpper_.classList.add(this.CssClasses_.BACKGROUND_UPPER);\n backgroundFlex.appendChild(this.backgroundUpper_);\n }\n this.boundInputHandler = this.onInput_.bind(this);\n this.boundChangeHandler = this.onChange_.bind(this);\n this.boundMouseUpHandler = this.onMouseUp_.bind(this);\n this.boundContainerMouseDownHandler = this.onContainerMouseDown_.bind(this);\n this.element_.addEventListener('input', this.boundInputHandler);\n this.element_.addEventListener('change', this.boundChangeHandler);\n this.element_.addEventListener('mouseup', this.boundMouseUpHandler);\n this.element_.parentElement.addEventListener('mousedown', this.boundContainerMouseDownHandler);\n this.updateValueStyles_();\n this.element_.classList.add(this.CssClasses_.IS_UPGRADED);\n }\n};\n/**\n * Downgrade the component\n *\n * @private\n */\nMaterialSlider.prototype.mdlDowngrade_ = function () {\n this.element_.removeEventListener('input', this.boundInputHandler);\n this.element_.removeEventListener('change', this.boundChangeHandler);\n this.element_.removeEventListener('mouseup', this.boundMouseUpHandler);\n this.element_.parentElement.removeEventListener('mousedown', this.boundContainerMouseDownHandler);\n};\n/**\n * Public alias for the downgrade method.\n *\n * @public\n */\nMaterialSlider.prototype.mdlDowngrade = MaterialSlider.prototype.mdlDowngrade_;\nMaterialSlider.prototype['mdlDowngrade'] = MaterialSlider.prototype.mdlDowngrade;\n// The component registers itself. It can assume componentHandler is available\n// in the global scope.\ncomponentHandler.register({\n constructor: MaterialSlider,\n classAsString: 'MaterialSlider',\n cssClass: 'mdl-js-slider',\n widget: true\n});","/**\n * @license\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Class constructor for Spinner MDL component.\n * Implements MDL component design pattern defined at:\n * https://github.com/jasonmayes/mdl-component-design-pattern\n *\n * @param {HTMLElement} element The element that will be upgraded.\n * @constructor\n */\nvar MaterialSpinner = function MaterialSpinner(element) {\n this.element_ = element;\n // Initialize instance.\n this.init();\n};\nwindow['MaterialSpinner'] = MaterialSpinner;\n/**\n * Store constants in one place so they can be updated easily.\n *\n * @enum {string | number}\n * @private\n */\nMaterialSpinner.prototype.Constant_ = { MDL_SPINNER_LAYER_COUNT: 4 };\n/**\n * Store strings for class names defined by this component that are used in\n * JavaScript. This allows us to simply change it in one place should we\n * decide to modify at a later date.\n *\n * @enum {string}\n * @private\n */\nMaterialSpinner.prototype.CssClasses_ = {\n MDL_SPINNER_LAYER: 'mdl-spinner__layer',\n MDL_SPINNER_CIRCLE_CLIPPER: 'mdl-spinner__circle-clipper',\n MDL_SPINNER_CIRCLE: 'mdl-spinner__circle',\n MDL_SPINNER_GAP_PATCH: 'mdl-spinner__gap-patch',\n MDL_SPINNER_LEFT: 'mdl-spinner__left',\n MDL_SPINNER_RIGHT: 'mdl-spinner__right'\n};\n/**\n * Auxiliary method to create a spinner layer.\n *\n * @param {number} index Index of the layer to be created.\n * @public\n */\nMaterialSpinner.prototype.createLayer = function (index) {\n var layer = document.createElement('div');\n layer.classList.add(this.CssClasses_.MDL_SPINNER_LAYER);\n layer.classList.add(this.CssClasses_.MDL_SPINNER_LAYER + '-' + index);\n var leftClipper = document.createElement('div');\n leftClipper.classList.add(this.CssClasses_.MDL_SPINNER_CIRCLE_CLIPPER);\n leftClipper.classList.add(this.CssClasses_.MDL_SPINNER_LEFT);\n var gapPatch = document.createElement('div');\n gapPatch.classList.add(this.CssClasses_.MDL_SPINNER_GAP_PATCH);\n var rightClipper = document.createElement('div');\n rightClipper.classList.add(this.CssClasses_.MDL_SPINNER_CIRCLE_CLIPPER);\n rightClipper.classList.add(this.CssClasses_.MDL_SPINNER_RIGHT);\n var circleOwners = [\n leftClipper,\n gapPatch,\n rightClipper\n ];\n for (var i = 0; i < circleOwners.length; i++) {\n var circle = document.createElement('div');\n circle.classList.add(this.CssClasses_.MDL_SPINNER_CIRCLE);\n circleOwners[i].appendChild(circle);\n }\n layer.appendChild(leftClipper);\n layer.appendChild(gapPatch);\n layer.appendChild(rightClipper);\n this.element_.appendChild(layer);\n};\nMaterialSpinner.prototype['createLayer'] = MaterialSpinner.prototype.createLayer;\n/**\n * Stops the spinner animation.\n * Public method for users who need to stop the spinner for any reason.\n *\n * @public\n */\nMaterialSpinner.prototype.stop = function () {\n this.element_.classList.remove('is-active');\n};\nMaterialSpinner.prototype['stop'] = MaterialSpinner.prototype.stop;\n/**\n * Starts the spinner animation.\n * Public method for users who need to manually start the spinner for any reason\n * (instead of just adding the 'is-active' class to their markup).\n *\n * @public\n */\nMaterialSpinner.prototype.start = function () {\n this.element_.classList.add('is-active');\n};\nMaterialSpinner.prototype['start'] = MaterialSpinner.prototype.start;\n/**\n * Initialize element.\n */\nMaterialSpinner.prototype.init = function () {\n if (this.element_) {\n for (var i = 1; i <= this.Constant_.MDL_SPINNER_LAYER_COUNT; i++) {\n this.createLayer(i);\n }\n this.element_.classList.add('is-upgraded');\n }\n};\n// The component registers itself. It can assume componentHandler is available\n// in the global scope.\ncomponentHandler.register({\n constructor: MaterialSpinner,\n classAsString: 'MaterialSpinner',\n cssClass: 'mdl-js-spinner',\n widget: true\n});","/**\n * @license\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Class constructor for Checkbox MDL component.\n * Implements MDL component design pattern defined at:\n * https://github.com/jasonmayes/mdl-component-design-pattern\n *\n * @constructor\n * @param {HTMLElement} element The element that will be upgraded.\n */\nvar MaterialSwitch = function MaterialSwitch(element) {\n this.element_ = element;\n // Initialize instance.\n this.init();\n};\nwindow['MaterialSwitch'] = MaterialSwitch;\n/**\n * Store constants in one place so they can be updated easily.\n *\n * @enum {string | number}\n * @private\n */\nMaterialSwitch.prototype.Constant_ = { TINY_TIMEOUT: 0.001 };\n/**\n * Store strings for class names defined by this component that are used in\n * JavaScript. This allows us to simply change it in one place should we\n * decide to modify at a later date.\n *\n * @enum {string}\n * @private\n */\nMaterialSwitch.prototype.CssClasses_ = {\n INPUT: 'mdl-switch__input',\n TRACK: 'mdl-switch__track',\n THUMB: 'mdl-switch__thumb',\n FOCUS_HELPER: 'mdl-switch__focus-helper',\n RIPPLE_EFFECT: 'mdl-js-ripple-effect',\n RIPPLE_IGNORE_EVENTS: 'mdl-js-ripple-effect--ignore-events',\n RIPPLE_CONTAINER: 'mdl-switch__ripple-container',\n RIPPLE_CENTER: 'mdl-ripple--center',\n RIPPLE: 'mdl-ripple',\n IS_FOCUSED: 'is-focused',\n IS_DISABLED: 'is-disabled',\n IS_CHECKED: 'is-checked'\n};\n/**\n * Handle change of state.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialSwitch.prototype.onChange_ = function (event) {\n this.updateClasses_();\n};\n/**\n * Handle focus of element.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialSwitch.prototype.onFocus_ = function (event) {\n this.element_.classList.add(this.CssClasses_.IS_FOCUSED);\n};\n/**\n * Handle lost focus of element.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialSwitch.prototype.onBlur_ = function (event) {\n this.element_.classList.remove(this.CssClasses_.IS_FOCUSED);\n};\n/**\n * Handle mouseup.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialSwitch.prototype.onMouseUp_ = function (event) {\n this.blur_();\n};\n/**\n * Handle class updates.\n *\n * @private\n */\nMaterialSwitch.prototype.updateClasses_ = function () {\n this.checkDisabled();\n this.checkToggleState();\n};\n/**\n * Add blur.\n *\n * @private\n */\nMaterialSwitch.prototype.blur_ = function () {\n // TODO: figure out why there's a focus event being fired after our blur,\n // so that we can avoid this hack.\n window.setTimeout(function () {\n this.inputElement_.blur();\n }.bind(this), this.Constant_.TINY_TIMEOUT);\n};\n// Public methods.\n/**\n * Check the components disabled state.\n *\n * @public\n */\nMaterialSwitch.prototype.checkDisabled = function () {\n if (this.inputElement_.disabled) {\n this.element_.classList.add(this.CssClasses_.IS_DISABLED);\n } else {\n this.element_.classList.remove(this.CssClasses_.IS_DISABLED);\n }\n};\nMaterialSwitch.prototype['checkDisabled'] = MaterialSwitch.prototype.checkDisabled;\n/**\n * Check the components toggled state.\n *\n * @public\n */\nMaterialSwitch.prototype.checkToggleState = function () {\n if (this.inputElement_.checked) {\n this.element_.classList.add(this.CssClasses_.IS_CHECKED);\n } else {\n this.element_.classList.remove(this.CssClasses_.IS_CHECKED);\n }\n};\nMaterialSwitch.prototype['checkToggleState'] = MaterialSwitch.prototype.checkToggleState;\n/**\n * Disable switch.\n *\n * @public\n */\nMaterialSwitch.prototype.disable = function () {\n this.inputElement_.disabled = true;\n this.updateClasses_();\n};\nMaterialSwitch.prototype['disable'] = MaterialSwitch.prototype.disable;\n/**\n * Enable switch.\n *\n * @public\n */\nMaterialSwitch.prototype.enable = function () {\n this.inputElement_.disabled = false;\n this.updateClasses_();\n};\nMaterialSwitch.prototype['enable'] = MaterialSwitch.prototype.enable;\n/**\n * Activate switch.\n *\n * @public\n */\nMaterialSwitch.prototype.on = function () {\n this.inputElement_.checked = true;\n this.updateClasses_();\n};\nMaterialSwitch.prototype['on'] = MaterialSwitch.prototype.on;\n/**\n * Deactivate switch.\n *\n * @public\n */\nMaterialSwitch.prototype.off = function () {\n this.inputElement_.checked = false;\n this.updateClasses_();\n};\nMaterialSwitch.prototype['off'] = MaterialSwitch.prototype.off;\n/**\n * Initialize element.\n */\nMaterialSwitch.prototype.init = function () {\n if (this.element_) {\n this.inputElement_ = this.element_.querySelector('.' + this.CssClasses_.INPUT);\n var track = document.createElement('div');\n track.classList.add(this.CssClasses_.TRACK);\n var thumb = document.createElement('div');\n thumb.classList.add(this.CssClasses_.THUMB);\n var focusHelper = document.createElement('span');\n focusHelper.classList.add(this.CssClasses_.FOCUS_HELPER);\n thumb.appendChild(focusHelper);\n this.element_.appendChild(track);\n this.element_.appendChild(thumb);\n this.boundMouseUpHandler = this.onMouseUp_.bind(this);\n if (this.element_.classList.contains(this.CssClasses_.RIPPLE_EFFECT)) {\n this.element_.classList.add(this.CssClasses_.RIPPLE_IGNORE_EVENTS);\n this.rippleContainerElement_ = document.createElement('span');\n this.rippleContainerElement_.classList.add(this.CssClasses_.RIPPLE_CONTAINER);\n this.rippleContainerElement_.classList.add(this.CssClasses_.RIPPLE_EFFECT);\n this.rippleContainerElement_.classList.add(this.CssClasses_.RIPPLE_CENTER);\n this.rippleContainerElement_.addEventListener('mouseup', this.boundMouseUpHandler);\n var ripple = document.createElement('span');\n ripple.classList.add(this.CssClasses_.RIPPLE);\n this.rippleContainerElement_.appendChild(ripple);\n this.element_.appendChild(this.rippleContainerElement_);\n }\n this.boundChangeHandler = this.onChange_.bind(this);\n this.boundFocusHandler = this.onFocus_.bind(this);\n this.boundBlurHandler = this.onBlur_.bind(this);\n this.inputElement_.addEventListener('change', this.boundChangeHandler);\n this.inputElement_.addEventListener('focus', this.boundFocusHandler);\n this.inputElement_.addEventListener('blur', this.boundBlurHandler);\n this.element_.addEventListener('mouseup', this.boundMouseUpHandler);\n this.updateClasses_();\n this.element_.classList.add('is-upgraded');\n }\n};\n/**\n * Downgrade the component.\n *\n * @private\n */\nMaterialSwitch.prototype.mdlDowngrade_ = function () {\n if (this.rippleContainerElement_) {\n this.rippleContainerElement_.removeEventListener('mouseup', this.boundMouseUpHandler);\n }\n this.inputElement_.removeEventListener('change', this.boundChangeHandler);\n this.inputElement_.removeEventListener('focus', this.boundFocusHandler);\n this.inputElement_.removeEventListener('blur', this.boundBlurHandler);\n this.element_.removeEventListener('mouseup', this.boundMouseUpHandler);\n};\n/**\n * Public alias for the downgrade method.\n *\n * @public\n */\nMaterialSwitch.prototype.mdlDowngrade = MaterialSwitch.prototype.mdlDowngrade_;\nMaterialSwitch.prototype['mdlDowngrade'] = MaterialSwitch.prototype.mdlDowngrade;\n// The component registers itself. It can assume componentHandler is available\n// in the global scope.\ncomponentHandler.register({\n constructor: MaterialSwitch,\n classAsString: 'MaterialSwitch',\n cssClass: 'mdl-js-switch',\n widget: true\n});","/**\n * @license\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Class constructor for Textfield MDL component.\n * Implements MDL component design pattern defined at:\n * https://github.com/jasonmayes/mdl-component-design-pattern\n *\n * @constructor\n * @param {HTMLElement} element The element that will be upgraded.\n */\nvar MaterialTextfield = function MaterialTextfield(element) {\n this.element_ = element;\n this.maxRows = this.Constant_.NO_MAX_ROWS;\n // Initialize instance.\n this.init();\n};\nwindow['MaterialTextfield'] = MaterialTextfield;\n/**\n * Store constants in one place so they can be updated easily.\n *\n * @enum {string | number}\n * @private\n */\nMaterialTextfield.prototype.Constant_ = {\n NO_MAX_ROWS: -1,\n MAX_ROWS_ATTRIBUTE: 'maxrows'\n};\n/**\n * Store strings for class names defined by this component that are used in\n * JavaScript. This allows us to simply change it in one place should we\n * decide to modify at a later date.\n *\n * @enum {string}\n * @private\n */\nMaterialTextfield.prototype.CssClasses_ = {\n LABEL: 'mdl-textfield__label',\n INPUT: 'mdl-textfield__input',\n IS_DIRTY: 'is-dirty',\n IS_FOCUSED: 'is-focused',\n IS_DISABLED: 'is-disabled',\n IS_INVALID: 'is-invalid',\n IS_UPGRADED: 'is-upgraded'\n};\n/**\n * Handle input being entered.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialTextfield.prototype.onKeyDown_ = function (event) {\n var currentRowCount = event.target.value.split('\\n').length;\n if (event.keyCode === 13) {\n if (currentRowCount >= this.maxRows) {\n event.preventDefault();\n }\n }\n};\n/**\n * Handle focus.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialTextfield.prototype.onFocus_ = function (event) {\n this.element_.classList.add(this.CssClasses_.IS_FOCUSED);\n};\n/**\n * Handle lost focus.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialTextfield.prototype.onBlur_ = function (event) {\n this.element_.classList.remove(this.CssClasses_.IS_FOCUSED);\n};\n/**\n * Handle class updates.\n *\n * @private\n */\nMaterialTextfield.prototype.updateClasses_ = function () {\n this.checkDisabled();\n this.checkValidity();\n this.checkDirty();\n};\n// Public methods.\n/**\n * Check the disabled state and update field accordingly.\n *\n * @public\n */\nMaterialTextfield.prototype.checkDisabled = function () {\n if (this.input_.disabled) {\n this.element_.classList.add(this.CssClasses_.IS_DISABLED);\n } else {\n this.element_.classList.remove(this.CssClasses_.IS_DISABLED);\n }\n};\nMaterialTextfield.prototype['checkDisabled'] = MaterialTextfield.prototype.checkDisabled;\n/**\n * Check the validity state and update field accordingly.\n *\n * @public\n */\nMaterialTextfield.prototype.checkValidity = function () {\n if (this.input_.validity) {\n if (this.input_.validity.valid) {\n this.element_.classList.remove(this.CssClasses_.IS_INVALID);\n } else {\n this.element_.classList.add(this.CssClasses_.IS_INVALID);\n }\n }\n};\nMaterialTextfield.prototype['checkValidity'] = MaterialTextfield.prototype.checkValidity;\n/**\n * Check the dirty state and update field accordingly.\n *\n * @public\n */\nMaterialTextfield.prototype.checkDirty = function () {\n if (this.input_.value && this.input_.value.length > 0) {\n this.element_.classList.add(this.CssClasses_.IS_DIRTY);\n } else {\n this.element_.classList.remove(this.CssClasses_.IS_DIRTY);\n }\n};\nMaterialTextfield.prototype['checkDirty'] = MaterialTextfield.prototype.checkDirty;\n/**\n * Disable text field.\n *\n * @public\n */\nMaterialTextfield.prototype.disable = function () {\n this.input_.disabled = true;\n this.updateClasses_();\n};\nMaterialTextfield.prototype['disable'] = MaterialTextfield.prototype.disable;\n/**\n * Enable text field.\n *\n * @public\n */\nMaterialTextfield.prototype.enable = function () {\n this.input_.disabled = false;\n this.updateClasses_();\n};\nMaterialTextfield.prototype['enable'] = MaterialTextfield.prototype.enable;\n/**\n * Update text field value.\n *\n * @param {string} value The value to which to set the control (optional).\n * @public\n */\nMaterialTextfield.prototype.change = function (value) {\n this.input_.value = value || '';\n this.updateClasses_();\n};\nMaterialTextfield.prototype['change'] = MaterialTextfield.prototype.change;\n/**\n * Initialize element.\n */\nMaterialTextfield.prototype.init = function () {\n if (this.element_) {\n this.label_ = this.element_.querySelector('.' + this.CssClasses_.LABEL);\n this.input_ = this.element_.querySelector('.' + this.CssClasses_.INPUT);\n if (this.input_) {\n if (this.input_.hasAttribute(this.Constant_.MAX_ROWS_ATTRIBUTE)) {\n this.maxRows = parseInt(this.input_.getAttribute(this.Constant_.MAX_ROWS_ATTRIBUTE), 10);\n if (isNaN(this.maxRows)) {\n this.maxRows = this.Constant_.NO_MAX_ROWS;\n }\n }\n this.boundUpdateClassesHandler = this.updateClasses_.bind(this);\n this.boundFocusHandler = this.onFocus_.bind(this);\n this.boundBlurHandler = this.onBlur_.bind(this);\n this.input_.addEventListener('input', this.boundUpdateClassesHandler);\n this.input_.addEventListener('focus', this.boundFocusHandler);\n this.input_.addEventListener('blur', this.boundBlurHandler);\n if (this.maxRows !== this.Constant_.NO_MAX_ROWS) {\n // TODO: This should handle pasting multi line text.\n // Currently doesn't.\n this.boundKeyDownHandler = this.onKeyDown_.bind(this);\n this.input_.addEventListener('keydown', this.boundKeyDownHandler);\n }\n var invalid = this.element_.classList.contains(this.CssClasses_.IS_INVALID);\n this.updateClasses_();\n this.element_.classList.add(this.CssClasses_.IS_UPGRADED);\n if (invalid) {\n this.element_.classList.add(this.CssClasses_.IS_INVALID);\n }\n }\n }\n};\n/**\n * Downgrade the component\n *\n * @private\n */\nMaterialTextfield.prototype.mdlDowngrade_ = function () {\n this.input_.removeEventListener('input', this.boundUpdateClassesHandler);\n this.input_.removeEventListener('focus', this.boundFocusHandler);\n this.input_.removeEventListener('blur', this.boundBlurHandler);\n if (this.boundKeyDownHandler) {\n this.input_.removeEventListener('keydown', this.boundKeyDownHandler);\n }\n};\n/**\n * Public alias for the downgrade method.\n *\n * @public\n */\nMaterialTextfield.prototype.mdlDowngrade = MaterialTextfield.prototype.mdlDowngrade_;\nMaterialTextfield.prototype['mdlDowngrade'] = MaterialTextfield.prototype.mdlDowngrade;\n// The component registers itself. It can assume componentHandler is available\n// in the global scope.\ncomponentHandler.register({\n constructor: MaterialTextfield,\n classAsString: 'MaterialTextfield',\n cssClass: 'mdl-js-textfield',\n widget: true\n});","/**\n * @license\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Class constructor for Tooltip MDL component.\n * Implements MDL component design pattern defined at:\n * https://github.com/jasonmayes/mdl-component-design-pattern\n *\n * @constructor\n * @param {HTMLElement} element The element that will be upgraded.\n */\nvar MaterialTooltip = function MaterialTooltip(element) {\n this.element_ = element;\n // Initialize instance.\n this.init();\n};\nwindow['MaterialTooltip'] = MaterialTooltip;\n/**\n * Store constants in one place so they can be updated easily.\n *\n * @enum {string | number}\n * @private\n */\nMaterialTooltip.prototype.Constant_ = {};\n/**\n * Store strings for class names defined by this component that are used in\n * JavaScript. This allows us to simply change it in one place should we\n * decide to modify at a later date.\n *\n * @enum {string}\n * @private\n */\nMaterialTooltip.prototype.CssClasses_ = { IS_ACTIVE: 'is-active' };\n/**\n * Handle mouseenter for tooltip.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialTooltip.prototype.handleMouseEnter_ = function (event) {\n event.stopPropagation();\n var props = event.target.getBoundingClientRect();\n var left = props.left + props.width / 2;\n var marginLeft = -1 * (this.element_.offsetWidth / 2);\n if (left + marginLeft < 0) {\n this.element_.style.left = 0;\n this.element_.style.marginLeft = 0;\n } else {\n this.element_.style.left = left + 'px';\n this.element_.style.marginLeft = marginLeft + 'px';\n }\n this.element_.style.top = props.top + props.height + 10 + 'px';\n this.element_.classList.add(this.CssClasses_.IS_ACTIVE);\n window.addEventListener('scroll', this.boundMouseLeaveHandler, false);\n window.addEventListener('touchmove', this.boundMouseLeaveHandler, false);\n};\n/**\n * Handle mouseleave for tooltip.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialTooltip.prototype.handleMouseLeave_ = function (event) {\n event.stopPropagation();\n this.element_.classList.remove(this.CssClasses_.IS_ACTIVE);\n window.removeEventListener('scroll', this.boundMouseLeaveHandler);\n window.removeEventListener('touchmove', this.boundMouseLeaveHandler, false);\n};\n/**\n * Initialize element.\n */\nMaterialTooltip.prototype.init = function () {\n if (this.element_) {\n var forElId = this.element_.getAttribute('for');\n if (forElId) {\n this.forElement_ = document.getElementById(forElId);\n }\n if (this.forElement_) {\n // Tabindex needs to be set for `blur` events to be emitted\n if (!this.forElement_.hasAttribute('tabindex')) {\n this.forElement_.setAttribute('tabindex', '0');\n }\n this.boundMouseEnterHandler = this.handleMouseEnter_.bind(this);\n this.boundMouseLeaveHandler = this.handleMouseLeave_.bind(this);\n this.forElement_.addEventListener('mouseenter', this.boundMouseEnterHandler, false);\n this.forElement_.addEventListener('click', this.boundMouseEnterHandler, false);\n this.forElement_.addEventListener('blur', this.boundMouseLeaveHandler);\n this.forElement_.addEventListener('touchstart', this.boundMouseEnterHandler, false);\n this.forElement_.addEventListener('mouseleave', this.boundMouseLeaveHandler);\n }\n }\n};\n/**\n * Downgrade the component\n *\n * @private\n */\nMaterialTooltip.prototype.mdlDowngrade_ = function () {\n if (this.forElement_) {\n this.forElement_.removeEventListener('mouseenter', this.boundMouseEnterHandler, false);\n this.forElement_.removeEventListener('click', this.boundMouseEnterHandler, false);\n this.forElement_.removeEventListener('touchstart', this.boundMouseEnterHandler, false);\n this.forElement_.removeEventListener('mouseleave', this.boundMouseLeaveHandler);\n }\n};\n/**\n * Public alias for the downgrade method.\n *\n * @public\n */\nMaterialTooltip.prototype.mdlDowngrade = MaterialTooltip.prototype.mdlDowngrade_;\nMaterialTooltip.prototype['mdlDowngrade'] = MaterialTooltip.prototype.mdlDowngrade;\n// The component registers itself. It can assume componentHandler is available\n// in the global scope.\ncomponentHandler.register({\n constructor: MaterialTooltip,\n classAsString: 'MaterialTooltip',\n cssClass: 'mdl-tooltip'\n});","/**\n * @license\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Class constructor for Data Table Card MDL component.\n * Implements MDL component design pattern defined at:\n * https://github.com/jasonmayes/mdl-component-design-pattern\n *\n * @constructor\n * @param {HTMLElement} element The element that will be upgraded.\n */\nvar MaterialDataTable = function MaterialDataTable(element) {\n this.element_ = element;\n // Initialize instance.\n this.init();\n};\nwindow['MaterialDataTable'] = MaterialDataTable;\n/**\n * Store constants in one place so they can be updated easily.\n *\n * @enum {string | number}\n * @private\n */\nMaterialDataTable.prototype.Constant_ = {};\n/**\n * Store strings for class names defined by this component that are used in\n * JavaScript. This allows us to simply change it in one place should we\n * decide to modify at a later date.\n *\n * @enum {string}\n * @private\n */\nMaterialDataTable.prototype.CssClasses_ = {\n DATA_TABLE: 'mdl-data-table',\n SELECTABLE: 'mdl-data-table--selectable',\n SELECT_ELEMENT: 'mdl-data-table__select',\n IS_SELECTED: 'is-selected',\n IS_UPGRADED: 'is-upgraded'\n};\n/**\n * Generates and returns a function that toggles the selection state of a\n * single row (or multiple rows).\n *\n * @param {Element} checkbox Checkbox that toggles the selection state.\n * @param {HTMLElement} row Row to toggle when checkbox changes.\n * @param {(Array|NodeList)=} opt_rows Rows to toggle when checkbox changes.\n * @private\n */\nMaterialDataTable.prototype.selectRow_ = function (checkbox, row, opt_rows) {\n if (row) {\n return function () {\n if (checkbox.checked) {\n row.classList.add(this.CssClasses_.IS_SELECTED);\n } else {\n row.classList.remove(this.CssClasses_.IS_SELECTED);\n }\n }.bind(this);\n }\n if (opt_rows) {\n return function () {\n var i;\n var el;\n if (checkbox.checked) {\n for (i = 0; i < opt_rows.length; i++) {\n el = opt_rows[i].querySelector('td').querySelector('.mdl-checkbox');\n el['MaterialCheckbox'].check();\n opt_rows[i].classList.add(this.CssClasses_.IS_SELECTED);\n }\n } else {\n for (i = 0; i < opt_rows.length; i++) {\n el = opt_rows[i].querySelector('td').querySelector('.mdl-checkbox');\n el['MaterialCheckbox'].uncheck();\n opt_rows[i].classList.remove(this.CssClasses_.IS_SELECTED);\n }\n }\n }.bind(this);\n }\n};\n/**\n * Creates a checkbox for a single or or multiple rows and hooks up the\n * event handling.\n *\n * @param {HTMLElement} row Row to toggle when checkbox changes.\n * @param {(Array|NodeList)=} opt_rows Rows to toggle when checkbox changes.\n * @private\n */\nMaterialDataTable.prototype.createCheckbox_ = function (row, opt_rows) {\n var label = document.createElement('label');\n var labelClasses = [\n 'mdl-checkbox',\n 'mdl-js-checkbox',\n 'mdl-js-ripple-effect',\n this.CssClasses_.SELECT_ELEMENT\n ];\n label.className = labelClasses.join(' ');\n var checkbox = document.createElement('input');\n checkbox.type = 'checkbox';\n checkbox.classList.add('mdl-checkbox__input');\n checkbox.addEventListener('change', this.selectRow_(checkbox, row, opt_rows));\n label.appendChild(checkbox);\n componentHandler.upgradeElement(label, 'MaterialCheckbox');\n return label;\n};\n/**\n * Initialize element.\n */\nMaterialDataTable.prototype.init = function () {\n if (this.element_) {\n var firstHeader = this.element_.querySelector('th');\n var rows = this.element_.querySelector('tbody').querySelectorAll('tr');\n if (this.element_.classList.contains(this.CssClasses_.SELECTABLE)) {\n var th = document.createElement('th');\n var headerCheckbox = this.createCheckbox_(null, rows);\n th.appendChild(headerCheckbox);\n firstHeader.parentElement.insertBefore(th, firstHeader);\n for (var i = 0; i < rows.length; i++) {\n var firstCell = rows[i].querySelector('td');\n if (firstCell) {\n var td = document.createElement('td');\n var rowCheckbox = this.createCheckbox_(rows[i]);\n td.appendChild(rowCheckbox);\n rows[i].insertBefore(td, firstCell);\n }\n }\n }\n this.element_.classList.add(this.CssClasses_.IS_UPGRADED);\n }\n};\n// The component registers itself. It can assume componentHandler is available\n// in the global scope.\ncomponentHandler.register({\n constructor: MaterialDataTable,\n classAsString: 'MaterialDataTable',\n cssClass: 'mdl-js-data-table'\n});","/**\n * @license\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Class constructor for Ripple MDL component.\n * Implements MDL component design pattern defined at:\n * https://github.com/jasonmayes/mdl-component-design-pattern\n *\n * @constructor\n * @param {HTMLElement} element The element that will be upgraded.\n */\nvar MaterialRipple = function MaterialRipple(element) {\n this.element_ = element;\n // Initialize instance.\n this.init();\n};\nwindow['MaterialRipple'] = MaterialRipple;\n/**\n * Store constants in one place so they can be updated easily.\n *\n * @enum {string | number}\n * @private\n */\nMaterialRipple.prototype.Constant_ = {\n INITIAL_SCALE: 'scale(0.0001, 0.0001)',\n INITIAL_SIZE: '1px',\n INITIAL_OPACITY: '0.4',\n FINAL_OPACITY: '0',\n FINAL_SCALE: ''\n};\n/**\n * Store strings for class names defined by this component that are used in\n * JavaScript. This allows us to simply change it in one place should we\n * decide to modify at a later date.\n *\n * @enum {string}\n * @private\n */\nMaterialRipple.prototype.CssClasses_ = {\n RIPPLE_CENTER: 'mdl-ripple--center',\n RIPPLE_EFFECT_IGNORE_EVENTS: 'mdl-js-ripple-effect--ignore-events',\n RIPPLE: 'mdl-ripple',\n IS_ANIMATING: 'is-animating',\n IS_VISIBLE: 'is-visible'\n};\n/**\n * Handle mouse / finger down on element.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialRipple.prototype.downHandler_ = function (event) {\n if (!this.rippleElement_.style.width && !this.rippleElement_.style.height) {\n var rect = this.element_.getBoundingClientRect();\n this.boundHeight = rect.height;\n this.boundWidth = rect.width;\n this.rippleSize_ = Math.sqrt(rect.width * rect.width + rect.height * rect.height) * 2 + 2;\n this.rippleElement_.style.width = this.rippleSize_ + 'px';\n this.rippleElement_.style.height = this.rippleSize_ + 'px';\n }\n this.rippleElement_.classList.add(this.CssClasses_.IS_VISIBLE);\n if (event.type === 'mousedown' && this.ignoringMouseDown_) {\n this.ignoringMouseDown_ = false;\n } else {\n if (event.type === 'touchstart') {\n this.ignoringMouseDown_ = true;\n }\n var frameCount = this.getFrameCount();\n if (frameCount > 0) {\n return;\n }\n this.setFrameCount(1);\n var bound = event.currentTarget.getBoundingClientRect();\n var x;\n var y;\n // Check if we are handling a keyboard click.\n if (event.clientX === 0 && event.clientY === 0) {\n x = Math.round(bound.width / 2);\n y = Math.round(bound.height / 2);\n } else {\n var clientX = event.clientX ? event.clientX : event.touches[0].clientX;\n var clientY = event.clientY ? event.clientY : event.touches[0].clientY;\n x = Math.round(clientX - bound.left);\n y = Math.round(clientY - bound.top);\n }\n this.setRippleXY(x, y);\n this.setRippleStyles(true);\n window.requestAnimationFrame(this.animFrameHandler.bind(this));\n }\n};\n/**\n * Handle mouse / finger up on element.\n *\n * @param {Event} event The event that fired.\n * @private\n */\nMaterialRipple.prototype.upHandler_ = function (event) {\n // Don't fire for the artificial \"mouseup\" generated by a double-click.\n if (event && event.detail !== 2) {\n this.rippleElement_.classList.remove(this.CssClasses_.IS_VISIBLE);\n }\n // Allow a repaint to occur before removing this class, so the animation\n // shows for tap events, which seem to trigger a mouseup too soon after\n // mousedown.\n window.setTimeout(function () {\n this.rippleElement_.classList.remove(this.CssClasses_.IS_VISIBLE);\n }.bind(this), 0);\n};\n/**\n * Initialize element.\n */\nMaterialRipple.prototype.init = function () {\n if (this.element_) {\n var recentering = this.element_.classList.contains(this.CssClasses_.RIPPLE_CENTER);\n if (!this.element_.classList.contains(this.CssClasses_.RIPPLE_EFFECT_IGNORE_EVENTS)) {\n this.rippleElement_ = this.element_.querySelector('.' + this.CssClasses_.RIPPLE);\n this.frameCount_ = 0;\n this.rippleSize_ = 0;\n this.x_ = 0;\n this.y_ = 0;\n // Touch start produces a compat mouse down event, which would cause a\n // second ripples. To avoid that, we use this property to ignore the first\n // mouse down after a touch start.\n this.ignoringMouseDown_ = false;\n this.boundDownHandler = this.downHandler_.bind(this);\n this.element_.addEventListener('mousedown', this.boundDownHandler);\n this.element_.addEventListener('touchstart', this.boundDownHandler);\n this.boundUpHandler = this.upHandler_.bind(this);\n this.element_.addEventListener('mouseup', this.boundUpHandler);\n this.element_.addEventListener('mouseleave', this.boundUpHandler);\n this.element_.addEventListener('touchend', this.boundUpHandler);\n this.element_.addEventListener('blur', this.boundUpHandler);\n /**\n * Getter for frameCount_.\n * @return {number} the frame count.\n */\n this.getFrameCount = function () {\n return this.frameCount_;\n };\n /**\n * Setter for frameCount_.\n * @param {number} fC the frame count.\n */\n this.setFrameCount = function (fC) {\n this.frameCount_ = fC;\n };\n /**\n * Getter for rippleElement_.\n * @return {Element} the ripple element.\n */\n this.getRippleElement = function () {\n return this.rippleElement_;\n };\n /**\n * Sets the ripple X and Y coordinates.\n * @param {number} newX the new X coordinate\n * @param {number} newY the new Y coordinate\n */\n this.setRippleXY = function (newX, newY) {\n this.x_ = newX;\n this.y_ = newY;\n };\n /**\n * Sets the ripple styles.\n * @param {boolean} start whether or not this is the start frame.\n */\n this.setRippleStyles = function (start) {\n if (this.rippleElement_ !== null) {\n var transformString;\n var scale;\n var size;\n var offset = 'translate(' + this.x_ + 'px, ' + this.y_ + 'px)';\n if (start) {\n scale = this.Constant_.INITIAL_SCALE;\n size = this.Constant_.INITIAL_SIZE;\n } else {\n scale = this.Constant_.FINAL_SCALE;\n size = this.rippleSize_ + 'px';\n if (recentering) {\n offset = 'translate(' + this.boundWidth / 2 + 'px, ' + this.boundHeight / 2 + 'px)';\n }\n }\n transformString = 'translate(-50%, -50%) ' + offset + scale;\n this.rippleElement_.style.webkitTransform = transformString;\n this.rippleElement_.style.msTransform = transformString;\n this.rippleElement_.style.transform = transformString;\n if (start) {\n this.rippleElement_.classList.remove(this.CssClasses_.IS_ANIMATING);\n } else {\n this.rippleElement_.classList.add(this.CssClasses_.IS_ANIMATING);\n }\n }\n };\n /**\n * Handles an animation frame.\n */\n this.animFrameHandler = function () {\n if (this.frameCount_-- > 0) {\n window.requestAnimationFrame(this.animFrameHandler.bind(this));\n } else {\n this.setRippleStyles(false);\n }\n };\n }\n }\n};\n/**\n * Downgrade the component\n *\n * @private\n */\nMaterialRipple.prototype.mdlDowngrade_ = function () {\n this.element_.removeEventListener('mousedown', this.boundDownHandler);\n this.element_.removeEventListener('touchstart', this.boundDownHandler);\n this.element_.removeEventListener('mouseup', this.boundUpHandler);\n this.element_.removeEventListener('mouseleave', this.boundUpHandler);\n this.element_.removeEventListener('touchend', this.boundUpHandler);\n this.element_.removeEventListener('blur', this.boundUpHandler);\n};\n/**\n * Public alias for the downgrade method.\n *\n * @public\n */\nMaterialRipple.prototype.mdlDowngrade = MaterialRipple.prototype.mdlDowngrade_;\nMaterialRipple.prototype['mdlDowngrade'] = MaterialRipple.prototype.mdlDowngrade;\n// The component registers itself. It can assume componentHandler is available\n// in the global scope.\ncomponentHandler.register({\n constructor: MaterialRipple,\n classAsString: 'MaterialRipple',\n cssClass: 'mdl-js-ripple-effect',\n widget: false\n});"],"sourceRoot":"/source/"} \ No newline at end of file diff --git a/examples/scalajs-play-core-react/server/src/main/resources/application.conf b/examples/scalajs-play-core-react/server/src/main/resources/application.conf deleted file mode 100644 index cd1bb1b..0000000 --- a/examples/scalajs-play-core-react/server/src/main/resources/application.conf +++ /dev/null @@ -1,4 +0,0 @@ -play.application.loader = "GlobalApplicationLoader" -//play.i18n.langs = [ "en" ] -play.http.parser.maxMemoryBuffer = 512k -play.http.parser.maxDiskBuffer = 1g diff --git a/examples/scalajs-play-core-react/server/src/main/scala/GlobalApplicationLoader.scala b/examples/scalajs-play-core-react/server/src/main/scala/GlobalApplicationLoader.scala deleted file mode 100644 index 6545380..0000000 --- a/examples/scalajs-play-core-react/server/src/main/scala/GlobalApplicationLoader.scala +++ /dev/null @@ -1,35 +0,0 @@ -import com.softwaremill.macwire._ -import controllers.Assets -import modules.{Controllers, Service} -import play.api.ApplicationLoader.Context -import play.api.routing.Router -import play.api.routing.sird._ -import play.api.{Application, ApplicationLoader, BuiltInComponents, BuiltInComponentsFromContext} - -/** - * Global application context - */ -class GlobalApplicationLoader extends ApplicationLoader { - override def load(context: Context): Application = (new BuiltInComponentFromContextWithPlayWorkaround(context) with ApplicationComponents).application -} - -abstract class BuiltInComponentFromContextWithPlayWorkaround(context: Context) extends BuiltInComponentsFromContext(context) { - - import play.api.inject.{Injector, NewInstanceInjector, SimpleInjector} - import play.api.libs.Files.DefaultTemporaryFileCreator - - lazy val defaultTemporaryFileCreator = new DefaultTemporaryFileCreator(applicationLifecycle) - - override lazy val injector: Injector = new SimpleInjector(NewInstanceInjector) + router + crypto + httpConfiguration + defaultTemporaryFileCreator -} - -trait ApplicationComponents extends BuiltInComponents with Controllers with Service { - lazy val assets: Assets = wire[Assets] - lazy val router: Router = Router.from { - case GET(p"/") => applicationController.index - case GET(p"/assets/$file*") => Assets.versioned(path = "/public", file = file) - case POST(p"/api/sample/$path*") => apiController.sampleApi(path) - } -} - - diff --git a/examples/scalajs-play-core-react/server/src/main/scala/controllers/ApiController.scala b/examples/scalajs-play-core-react/server/src/main/scala/controllers/ApiController.scala deleted file mode 100644 index e05d7a2..0000000 --- a/examples/scalajs-play-core-react/server/src/main/scala/controllers/ApiController.scala +++ /dev/null @@ -1,20 +0,0 @@ -package controllers - -import demo.SampleApi -import play.api.mvc.Action -import service.SampleApiImpl -import boopickle.Default._ -import scala.concurrent.ExecutionContext.Implicits.global - -/** - * API Controller - * Each request will create a SampleApiImpl instance. - * It's necessary if you want to set the user in the constructor, otherwise you can use singleton - */ -class ApiController extends ServiceController { - def sampleApi(path: String) = Action.async(parse.raw) { implicit request => - internalRoute(path, request) { - Router.route[SampleApi](new SampleApiImpl()) - } - } -} diff --git a/examples/scalajs-play-core-react/server/src/main/scala/controllers/DemoController.scala b/examples/scalajs-play-core-react/server/src/main/scala/controllers/DemoController.scala deleted file mode 100644 index 4700eaf..0000000 --- a/examples/scalajs-play-core-react/server/src/main/scala/controllers/DemoController.scala +++ /dev/null @@ -1,11 +0,0 @@ -package controllers - -import play.api.mvc._ - -import scala.concurrent.ExecutionContext.Implicits.global -import scala.concurrent.Future - -class DemoController extends Controller { - - def index = Action.async(Future(Ok(views.html.index("SPA tutorial")))) -} diff --git a/examples/scalajs-play-core-react/server/src/main/scala/controllers/ServiceController.scala b/examples/scalajs-play-core-react/server/src/main/scala/controllers/ServiceController.scala deleted file mode 100644 index b05c1ac..0000000 --- a/examples/scalajs-play-core-react/server/src/main/scala/controllers/ServiceController.scala +++ /dev/null @@ -1,38 +0,0 @@ -package controllers - -import java.nio.ByteBuffer - -import boopickle.Default._ -import boopickle.Pickler -import play.api.mvc.{Controller, RawBuffer, Request} - -import scala.concurrent.ExecutionContext.Implicits.global -/** - * Autowire router - */ -object Router extends autowire.Server[ByteBuffer, Pickler, Pickler] { - - override def read[R: Pickler](p: ByteBuffer) = Unpickle[R].fromBytes(p) - - override def write[R: Pickler](r: R) = Pickle.intoBytes(r) -} - -trait ServiceController extends Controller { - /** - * Helper for internal routing - * @param path - * @param request - * @param router - * @return - */ - protected def internalRoute(path: String, request: Request[RawBuffer])(router: => Router.Router) = { - val b = request.body.asBytes(parse.UNLIMITED).get - router( - autowire.Core.Request(path.split("/"), Unpickle[Map[String, ByteBuffer]].fromBytes(ByteBuffer.wrap(b))) - ).map(buffer => { - val data = Array.ofDim[Byte](buffer.remaining()) - buffer.get(data) - Ok(data) - }) - } -} diff --git a/examples/scalajs-play-core-react/server/src/main/scala/modules/Controllers.scala b/examples/scalajs-play-core-react/server/src/main/scala/modules/Controllers.scala deleted file mode 100644 index 59bffdb..0000000 --- a/examples/scalajs-play-core-react/server/src/main/scala/modules/Controllers.scala +++ /dev/null @@ -1,13 +0,0 @@ -package modules - -import com.softwaremill.macwire._ -import controllers.{ApiController, DemoController} -import play.api.BuiltInComponents - -/** - * Created by Janos on 12/9/2015. - */ -trait Controllers extends BuiltInComponents { - lazy val applicationController = wire[DemoController] - lazy val apiController = wire[ApiController] -} diff --git a/examples/scalajs-play-core-react/server/src/main/scala/modules/Service.scala b/examples/scalajs-play-core-react/server/src/main/scala/modules/Service.scala deleted file mode 100644 index d938327..0000000 --- a/examples/scalajs-play-core-react/server/src/main/scala/modules/Service.scala +++ /dev/null @@ -1,8 +0,0 @@ -package modules - -/** - * Created by Janos on 12/9/2015. - */ -trait Service { - -} diff --git a/examples/scalajs-play-core-react/server/src/main/scala/service/SampleApiImpl.scala b/examples/scalajs-play-core-react/server/src/main/scala/service/SampleApiImpl.scala deleted file mode 100644 index 00757f1..0000000 --- a/examples/scalajs-play-core-react/server/src/main/scala/service/SampleApiImpl.scala +++ /dev/null @@ -1,10 +0,0 @@ -package service - -import demo.SampleApi - -/** - * Created by Janos on 12/9/2015. - */ -class SampleApiImpl extends SampleApi { - override def echo(name: String): String = s"Echoed: ${name}" -} diff --git a/examples/scalajs-play-core-react/server/src/main/twirl/views/index.scala.html b/examples/scalajs-play-core-react/server/src/main/twirl/views/index.scala.html deleted file mode 100644 index 292a212..0000000 --- a/examples/scalajs-play-core-react/server/src/main/twirl/views/index.scala.html +++ /dev/null @@ -1,20 +0,0 @@ -@(title: String) - - - - - - @title - - - - - - - -
          -
          - @playscalajs.html.scripts("client") - - - \ No newline at end of file diff --git a/examples/scalajs-play-core-react/shared/.js/.gitignore b/examples/scalajs-play-core-react/shared/.js/.gitignore deleted file mode 100644 index 2a83fcb..0000000 --- a/examples/scalajs-play-core-react/shared/.js/.gitignore +++ /dev/null @@ -1,22 +0,0 @@ -/scala/ -/scala/ -/scala/ -/scala/ -/scala/ -/scala/ -/scala/ -/scala/ -/scala/ -/scala/ -/scala/ -/scala/ -/scala/ -/scala/ -/scala/ -/scala/ -/scala/ -/scala/ -/scala/ -/scala/ -/scala/ -/scala/ diff --git a/examples/scalajs-play-core-react/shared/src/main/scala/demo/SampleApi.scala b/examples/scalajs-play-core-react/shared/src/main/scala/demo/SampleApi.scala deleted file mode 100644 index 171a0d0..0000000 --- a/examples/scalajs-play-core-react/shared/src/main/scala/demo/SampleApi.scala +++ /dev/null @@ -1,5 +0,0 @@ -package demo - -trait SampleApi { - def echo(name: String): String -} \ No newline at end of file diff --git a/examples/scalajs-play-core-react/shared/src/main/scala/demo/SampleApi.sjsir b/examples/scalajs-play-core-react/shared/src/main/scala/demo/SampleApi.sjsir deleted file mode 100644 index 13dcc1e95415cd7e1e8c88673d495163c9f08da9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 323 zcmX^0&nuXL)j-co&y<1RCnYsEKR!4yw;(6gu^^LynE?hE8MsoDGxFo(Lx6~Z5hjd_ zW@OMbu&^|p&Gqg-Ju(UAO2br%AH(#$fIWZ>@Xa*am)qEhOdT^ycMaT|d z$xF;lWnc_phdO`>WQZ-4!@vl4H%Oe3mCc@Unwe;(?GLT(hc~^juY7fluV$*H)UB8t yMH^=K1ihk$AmxQ&{!Eu{x&qZ3K)eSs1B4m5z^1VPnJnBuf|Y>*L^1q_2Lb?2N String] = Option(() => "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa") + def OAuth2Token(): Option[() => String] = Option(() => "1c1d7814-c31e-40b8-bbca-f340c6909b63") def port(): Integer = 443 - def client(): Client = Connection.newClient(host, port, OAuth2Token(), true, false) - def getJavaClient() = builder().buildJavaClient(); @@ -20,8 +18,8 @@ object ClientFactory { private def builder() = { // useTest() -// useStaging() - useLocal() + useStaging() +// useLocal() } private def useLocal() = { new ClientBuilder() // diff --git a/it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/EventIntegrationHelper.scala b/it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/EventIntegrationHelper.scala index 3813207..ebf868a 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/EventIntegrationHelper.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/EventIntegrationHelper.scala @@ -21,37 +21,49 @@ class EventIntegrationHelper(generator: EventGenerator, client: Client) extends val actions = ClientActions(client) val eventType = generator.eventType - def createEventType(): EventType = { - failIfClientError(executeCall(client.createEventType(eventType))) - eventType + def createEventType(): Boolean = { + wasItsuccessfull(executeCall(client.createEventType(eventType))) } - def getEventType(eventTypeName: String = eventType.name): Option[EventType] = executeCall(client.getEventType(eventType.name)) match { + def getEventType(eventTypeName: String = eventType.name): Option[EventType] = executeCall(client.getEventType(eventTypeName)) match { case Left(clientError) => - failIfClientError(Option(clientError)) + wasItsuccessfull(Option(clientError)) None case Right(result) => result } - def deleteEventType() = failIfClientError(actions.deleteEventType(eventType.name)) + def deleteEventType() = wasItsuccessfull(actions.deleteEventType(eventType.name)) def publishEvents(nrOfEvents: Int): Seq[Event] = { val events = for { a <- 1 to nrOfEvents } yield generator.newEvent() - failIfClientError(actions.publish(eventType.name, events)) + wasItsuccessfull(actions.publish(eventType.name, events)) log.info(s"EVENTS published: $events") events } + def updateEventType(eType: EventType): Boolean = { + wasItsuccessfull(executeCall(client.updateEventType(eventType.name, eType))) + } + + def eventTypeExist(eventTypeName: String = eventType.name): Boolean = { + getEventType(eventTypeName) match { + case None => false + case Some(_) => true + } + } + //Private methods - private def failIfClientError(in: Option[ClientError]) = in match { + private def wasItsuccessfull(in: Option[ClientError]): Boolean = in match { case Some(clientError) => - fail("Failed with clientError " + clientError) - case _ => + log.error("ClientError: {}", clientError) + false + case None => + true } - def executeCall[T](call: => Future[T]): T = { + private def executeCall[T](call: => Future[T]): T = { Await.result(call, 10.second) } diff --git a/it/src/test/scala/org/zalando/nakadi/client/ClientSubscriptionTest.scala b/it/src/test/scala/org/zalando/nakadi/client/ClientSubscriptionTest.scala deleted file mode 100644 index 46ef72a..0000000 --- a/it/src/test/scala/org/zalando/nakadi/client/ClientSubscriptionTest.scala +++ /dev/null @@ -1,56 +0,0 @@ -package org.zalando.nakadi.client -import org.scalatest.{ Matchers, WordSpec } -import org.zalando.nakadi.client.scala.model._ -import com.fasterxml.jackson.core.`type`.TypeReference -import org.zalando.nakadi.client.scala.model.JacksonJsonMarshaller -import org.zalando.nakadi.client.scala.ModelFactory -import org.zalando.nakadi.client.scala.EventTypesActions -import org.zalando.nakadi.client.scala.ClientFactory -import org.zalando.nakadi.client.scala.EventActions -import org.zalando.nakadi.client.scala.StreamParameters -import org.zalando.nakadi.client.scala.Listener -import org.zalando.nakadi.client.scala.ClientError - -class ClientSubscriptionTest extends WordSpec with Matchers with ModelFactory { - import ClientFactory._ - import JacksonJsonMarshaller._ - val eventAction = new EventActions(client) - val eventTypeAction = new EventTypesActions(client) - "POST/PUT/GET/DELETE single EventType " in { - - val events = for { - a <- 0 to 4005 - } yield MyEventExample("order-" + a) - - val listener = new Listener[MyEventExample] { - def id: String = "test" - def onError(sourceUrl: String, error: Option[ClientError]): Unit = { - println("YOOOOOOOOOOOOOO ") - } - def onSubscribed(endpoint: String,cursor: Option[Cursor]):Unit = ??? - def onUnsubscribed(): Unit = ??? - def onReceive(sourceUrl: String, cursor: Cursor, event: Seq[MyEventExample]): Unit = ??? - } - val url = "/event-types/test-client-integration-event-1936085527-148383828851369665/events" - val eve = "test-client-integration-event-1936085527-148383828851369665" - val params = new StreamParameters() - client.subscribe(eve, params, listener) - // eventAction.create("test-client-integration-event-1936085527-148383828851369665", List(MyEventExample("test-1"))) - // eventAction.create("test-client-integration-event-1936085527-148383828851369665", events) - // while(true){ - // - // } - - } - - "Create" in { - val events = for { - a <- 0 to 4005 - } yield MyEventExample("order-" + a) - // eventAction.create("test-client-integration-event-1936085527-148383828851369665", List(MyEventExample("test-1"))) - eventAction.publish("test-client-integration-event-1936085527-148383828851369665", events) - } - -} - - diff --git a/it/src/test/scala/org/zalando/nakadi/client/EventTypesIntegrationTest.scala b/it/src/test/scala/org/zalando/nakadi/client/EventTypesIntegrationTest.scala deleted file mode 100644 index 640e322..0000000 --- a/it/src/test/scala/org/zalando/nakadi/client/EventTypesIntegrationTest.scala +++ /dev/null @@ -1,119 +0,0 @@ -package org.zalando.nakadi.client - -import org.scalatest.{ Matchers, WordSpec } -import org.zalando.nakadi.client.scala.model._ -import com.fasterxml.jackson.core.`type`.TypeReference -import org.zalando.nakadi.client.scala.ClientFactory -import org.zalando.nakadi.client.scala.EventTypesActions -import org.zalando.nakadi.client.scala.EventActions -import org.zalando.nakadi.client.scala.ModelFactory - -class EventTypeTest extends WordSpec with Matchers with ModelFactory{ - import ClientFactory._ -import JacksonJsonMarshaller._ - val eventAction = new EventActions(client) - val eventTypeAction = new EventTypesActions(client) - "POST/PUT/GET/DELETE single EventType " in { - val eventTypeName="test-client-integration-event-" - //Create event - val eventType = createEventType(eventTypeName) - val creationResult = eventTypeAction.create(eventType) - creationResult.isDefined shouldBe false - - //Check the created EventType - checkEventTypeExists(eventType) - - case class MyEventExample(orderNumber: String)extends Event - implicit def problemTR: TypeReference[MyEventExample] = new TypeReference[MyEventExample] {} - val events = for { - a <- 0 to 12 - } yield MyEventExample("order-"+a) -// eventAction.create("test-client-integration-event-1936085527-148383828851369665", List(MyEventExample("test-1"))) - eventAction.publish(eventTypeName, events) - - //TODO: Enable this when PUT is supported. - // Update the event - // val updatedEvent = eventType.copy(owningApplication = "laas-team-2") - // events.update(updatedEvent) - - //Check the EventType has bee updated - // checkEventTypeExists(updatedEvent) - // checkEventTypeDoesNotExist(eventType) - - //Delete the created Event - val deletedEvent = eventTypeAction.delete(eventType.name) - deletedEvent.isEmpty shouldBe true - - //Is it really deleted? - checkEventTypeDoesNotExist(eventType) - } - - "POST/GET/DELETE multiple EventTypes " in { -val eventTypeName1="test-client-integration-event-1936085527-1" -val eventTypeName2="test-client-integration-event-1936085527-2" - //Create 2 EventTypes - val eventType1 = createEventType(eventTypeName1) - val eventType2 = createEventType(eventTypeName2) - - eventTypeAction.create(eventType1) - checkEventTypeExists(eventType1) - - eventTypeAction.create(eventType2) - checkEventTypeExists(eventType2) - - //Get all EventTypes again - //TODO: Enable when Nakadi has no erranous eventType - // val Right(Some(allEvents)) = events.getAll() - // allEvents should contain(eventType1) - // allEvents should contain(eventType2) - - //Delete the 2 EventTypes - eventTypeAction.delete(eventType1.name) - eventTypeAction.delete(eventType2.name) - - //Check if the're really deleted - checkEventTypeDoesNotExist(eventType1) - checkEventTypeDoesNotExist(eventType2) - - //Get all should not contain the deleted events - val Right(Some(updatedEvents)) = eventTypeAction.getAll() - - updatedEvents shouldNot contain(eventType1) - updatedEvents shouldNot contain(eventType2) - - } - - //TODO: Enable when implemented - "UpdateEventTypes" in { - val eventTypeName1="test-client-integration-event-1936085527-3" - //Create 2 EventTypes - val eventType = createEventType(eventTypeName1) - - eventTypeAction.create(eventType) - checkEventTypeExists(eventType) - - //Update the event - val updatedEvent = eventType.copy(owningApplication = "laas-team-2") - eventTypeAction.update(updatedEvent) - - //Check the EventType has bee updated - // checkEventTypeExists(updatedEvent) - // checkEventTypeDoesNotExist(eventType) - - } - - def checkEventTypeDoesNotExist(eventType: EventType) = { - val requestedEvent = eventTypeAction.get(eventType.name) - println(requestedEvent) - requestedEvent.isRight shouldBe true - val Right(result) = requestedEvent - result shouldBe None - } - - def checkEventTypeExists(eventType: EventType) = { - val Right(Some(createdEvent)) = eventTypeAction.get(eventType.name) - createdEvent shouldBe eventType - } - -} - diff --git a/it/src/test/scala/org/zalando/nakadi/client/KlientIntegrationTest.scala b/it/src/test/scala/org/zalando/nakadi/client/KlientIntegrationTest.scala deleted file mode 100644 index be265c2..0000000 --- a/it/src/test/scala/org/zalando/nakadi/client/KlientIntegrationTest.scala +++ /dev/null @@ -1,84 +0,0 @@ -package org.zalando.nakadi.client - -//import scala.concurrent.Await -//import scala.concurrent.Future -//import scala.concurrent.duration.DurationInt -//import org.scalatest.Matchers -//import org.scalatest.WordSpec -//import org.zalando.nakadi.client._ -//import org.zalando.nakadi.client.scala.model._ -//import org.zalando.nakadi.client.scala.ClientFactory -//import org.zalando.nakadi.client.scala.ModelFactory -//import com.fasterxml.jackson.core.`type`.TypeReference -//import org.zalando.nakadi.client.scala.ClientError -//import scala.Right -//import scala.concurrent.Await -//import scala.concurrent.Future -//import scala.concurrent.Await -// -//class KlientIntegrationTest extends WordSpec with Matchers with ModelFactory { -// import ClientFactory._ -// import JacksonJsonMarshaller._ -// "Nakadi Client" should { -// "parse multiple PartitionResolutionStrategy" in { -// val Right(result) = executeCall(client.getPartitioningStrategies()) -// result.size should be > 0 -// } -// -// //TODO: Change it when this endpoint is implemented by nakadi -// "parse exsiting validationStrategies" in { -// val result = executeCall(client.getValidationStrategies()) -// result shouldBe Right(None) -// } -// //TODO: Change it when this endpoint is implemented by nakadi -// "parse exsiting enrishment-strategies" in { -// val result = executeCall(client.getEnrichmentStrategies()) -// result shouldBe Right(None) -// } -// //TODO: Change when all events are valid -// "parse existing eventTypes" in { -// val Right(result) = executeCall(client.getEventTypes()) -// result.size should be > 0 -// } -// "create a new eventType" in { -// val eventType = createUniqueEventType() -// executeCall(client.createEventType(eventType)) shouldBe None -// } -// "get EventType" in { -// val eventType = createUniqueEventType() -// executeCall(client.createEventType(eventType)) shouldBe None -// executeCall(client.getEventType(eventType.name)) shouldBe Right(Some(eventType)) -// -// } -// "delete EventType" in { -// val eventType = createUniqueEventType() -// executeCall(client.createEventType(eventType)) shouldBe None -// executeCall(client.deleteEventType(eventType.name)) shouldBe None -// } -// "Create the event itself" in { -// //Matches the one defined in the schema of -// -// case class EventExample(orderNumber: String, metadata: Option[EventMetadata]) extends Event -// implicit val eventExample: TypeReference[Seq[EventExample]] = new TypeReference[Seq[EventExample]] {} -// -// val event = new EventExample("22301982837", Some(createEventMetadata())) -// val eventType = createUniqueEventType() -// executeCall(client.createEventType(eventType)) shouldBe None -// executeCall(client.publishEvents[EventExample](eventType.name, List(event))) shouldBe None -// } -// } -// private def assertIsNotImplementedYet[T](input: Either[ClientError, Option[List[T]]]) = { -// input match { -// case Left(error) => error.status shouldBe Some(404) -// case Right(result) => -// println(" #### " + result) -// fail -// } -// } -// private def executeCall[T](call: => Future[T]): T = { -// Await.result(call, 10.second) -// } -//} -// - - diff --git a/it/src/test/scala/org/zalando/nakadi/client/scala/SimpleEventTest.scala b/it/src/test/scala/org/zalando/nakadi/client/scala/SimpleEventTest.scala index f60fc13..cc2c23e 100644 --- a/it/src/test/scala/org/zalando/nakadi/client/scala/SimpleEventTest.scala +++ b/it/src/test/scala/org/zalando/nakadi/client/scala/SimpleEventTest.scala @@ -4,9 +4,11 @@ import org.scalatest.Matchers import org.scalatest.WordSpec import org.zalando.nakadi.client.scala.model.Cursor import org.zalando.nakadi.client.scala.model.JacksonJsonMarshaller +import org.zalando.nakadi.client.scala.model.PartitionStrategyType import org.zalando.nakadi.client.scala.test.factory.EventIntegrationHelper import org.zalando.nakadi.client.scala.test.factory.events.MySimpleEvent import org.zalando.nakadi.client.scala.test.factory.events.SimpleEventListener +import org.zalando.nakadi.client.scala.model.PartitionStrategy class SimpleEventTest extends WordSpec with Matchers { @@ -50,9 +52,13 @@ class SimpleEventTest extends WordSpec with Matchers { def eventTypeId = s"SimpleEventIntegrationTest-Validate-Created-Events-$nrOfEvents" } val it = new EventIntegrationHelper(eventGenerator, client) - it.createEventType() + + it.createEventType() shouldBe true + val optionalOfCreatedEventType = it.getEventType() + optionalOfCreatedEventType.isDefined shouldBe true + val Some(eventType) = optionalOfCreatedEventType eventType.category shouldBe it.eventType.category eventType.dataKeyFields shouldBe null @@ -66,5 +72,44 @@ class SimpleEventTest extends WordSpec with Matchers { eventType.partitionKeyFields shouldBe it.eventType.partitionKeyFields } + "Update existing EventType" in { + + val eventGenerator = new DefaultMySimpleEventGenerator() { + def eventTypeId = s"SimpleEventIntegrationTest-Update-Existing-EventType" + } + val it = new EventIntegrationHelper(eventGenerator, client) + it.createEventType() shouldBe true + + //Generator with changes only in the schema + val schema = """{ "properties": { "order_number": { "type": "string" }, "id": { "type": "string" } } }""" + + val eventType2Update = new DefaultMySimpleEventGenerator() { + def eventTypeId = s"SimpleEventIntegrationTest-Update-Existing-EventType" +// override def schemaDefinition: String = schema + override def dataKeyFields = List("order_number") //Does not work +// override def partitionKeyFields = List("order_number") +// override def partitionStrategy = Some(PartitionStrategy.HASH) + }.eventType + + /* + it.updateEventType(eventType2Update) shouldBe true + val optionalOfCreatedEventType = it.getEventType() + + optionalOfCreatedEventType.isDefined shouldBe true + + val Some(eventType) = optionalOfCreatedEventType + eventType.category shouldBe eventType2Update.category + eventType.dataKeyFields shouldBe null //TODO this is not correct!! + eventType.name shouldBe it.eventType.name + eventType.owningApplication shouldBe eventType2Update.owningApplication + eventType.partitionStrategy shouldBe eventType2Update.partitionStrategy + eventType.schema shouldBe schema + eventType.statistics shouldBe eventType2Update.statistics + eventType.validationStrategies shouldBe null + eventType.enrichmentStrategies shouldBe eventType2Update.enrichmentStrategies + eventType.partitionKeyFields shouldBe eventType2Update.partitionKeyFields + */ + } + } From d596aa6574f585361d02ae244b45072e6666fb10 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Fri, 17 Jun 2016 13:24:19 +0200 Subject: [PATCH 087/183] techjira:LAAS-60 Adding apache commons-lang lib --- project/Dependencies.scala | 3 ++- project/plugins.sbt | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/project/Dependencies.scala b/project/Dependencies.scala index cac4a08..d219152 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -33,7 +33,8 @@ object Dependencies { Seq( "org.zalando.stups" % "tokens" % "0.9.9", "org.apache.httpcomponents" % "httpclient" % "4.5.2", - "org.scalatest" %% "scalatest" % "2.2.6" + "org.scalatest" %% "scalatest" % "2.2.6", + "commons-lang" % "commons-lang" % "2.6" ) } } diff --git a/project/plugins.sbt b/project/plugins.sbt index e2a661d..0b238f9 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -11,3 +11,5 @@ addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "4.0.0") addSbtPlugin("net.virtual-void" % "sbt-dependency-graph" % "0.8.2") addSbtPlugin("org.scoverage" % "sbt-scoverage" % "1.3.5") + +addSbtPlugin("de.johoop" % "findbugs4sbt" % "1.4.0") From 82d86880d95e9c6c8b7a9a1a3f20bc8a14bd94c3 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Fri, 17 Jun 2016 13:25:06 +0200 Subject: [PATCH 088/183] techjira:LAAS-60 Basis for Java Integration tests are in place --- .../zalando/nakadi/client/java/ClientTest.java | 5 ----- .../factory/{events => }/MySimpleEvent.java | 3 ++- .../nakadi/client/scala/ClientFactory.scala | 4 ++-- .../scala/test/factory/EventGenerator.scala | 2 +- .../test/factory/EventIntegrationHelper.scala | 18 +++++++++--------- .../nakadi/client/scala/SimpleEventTest.scala | 4 +++- 6 files changed, 17 insertions(+), 19 deletions(-) delete mode 100644 it/src/main/java/org/zalando/nakadi/client/java/ClientTest.java rename it/src/main/java/org/zalando/nakadi/client/java/test/factory/{events => }/MySimpleEvent.java (82%) diff --git a/it/src/main/java/org/zalando/nakadi/client/java/ClientTest.java b/it/src/main/java/org/zalando/nakadi/client/java/ClientTest.java deleted file mode 100644 index 8aa7603..0000000 --- a/it/src/main/java/org/zalando/nakadi/client/java/ClientTest.java +++ /dev/null @@ -1,5 +0,0 @@ -package org.zalando.nakadi.client.java; - -public class ClientTest { - -} diff --git a/it/src/main/java/org/zalando/nakadi/client/java/test/factory/events/MySimpleEvent.java b/it/src/main/java/org/zalando/nakadi/client/java/test/factory/MySimpleEvent.java similarity index 82% rename from it/src/main/java/org/zalando/nakadi/client/java/test/factory/events/MySimpleEvent.java rename to it/src/main/java/org/zalando/nakadi/client/java/test/factory/MySimpleEvent.java index 75d7fe4..85434c6 100644 --- a/it/src/main/java/org/zalando/nakadi/client/java/test/factory/events/MySimpleEvent.java +++ b/it/src/main/java/org/zalando/nakadi/client/java/test/factory/MySimpleEvent.java @@ -1,9 +1,10 @@ -package org.zalando.nakadi.client.java.test.factory.events; +package org.zalando.nakadi.client.java.test.factory; import org.zalando.nakadi.client.java.model.Event; public class MySimpleEvent implements Event { + private String orderNumber; public MySimpleEvent(String orderNumber) { diff --git a/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala b/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala index 3bf2129..ab5d91d 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala @@ -18,8 +18,8 @@ object ClientFactory { private def builder() = { // useTest() - useStaging() -// useLocal() +// useStaging() + useLocal() } private def useLocal() = { new ClientBuilder() // diff --git a/it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/EventGenerator.scala b/it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/EventGenerator.scala index 074d772..3deb32d 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/EventGenerator.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/EventGenerator.scala @@ -29,7 +29,7 @@ trait EventGenerator { def newId(): String /** - * Should generate an new Event. + * Should generate an new Event. */ def newEvent(): Event diff --git a/it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/EventIntegrationHelper.scala b/it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/EventIntegrationHelper.scala index ebf868a..cdbe359 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/EventIntegrationHelper.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/EventIntegrationHelper.scala @@ -16,35 +16,35 @@ import scala.concurrent.Await import scala.concurrent.duration.DurationInt import scala.concurrent.Future +import com.typesafe.scalalogging.Logger class EventIntegrationHelper(generator: EventGenerator, client: Client) extends WordSpec with Matchers { - private val log = LoggerFactory.getLogger(this.getClass) - val actions = ClientActions(client) + private val log = Logger(LoggerFactory.getLogger(this.getClass)) val eventType = generator.eventType def createEventType(): Boolean = { - wasItsuccessfull(executeCall(client.createEventType(eventType))) + wasItsuccessfull(executeCall(client.createEventType(eventType)),"createEventType") } def getEventType(eventTypeName: String = eventType.name): Option[EventType] = executeCall(client.getEventType(eventTypeName)) match { case Left(clientError) => - wasItsuccessfull(Option(clientError)) + wasItsuccessfull(Option(clientError),"getEventType") None case Right(result) => result } - def deleteEventType() = wasItsuccessfull(actions.deleteEventType(eventType.name)) + def deleteEventType() = wasItsuccessfull(executeCall(client.deleteEventType(eventType.name)),"deleteEventType") def publishEvents(nrOfEvents: Int): Seq[Event] = { val events = for { a <- 1 to nrOfEvents } yield generator.newEvent() - wasItsuccessfull(actions.publish(eventType.name, events)) + wasItsuccessfull(executeCall(client.publishEvents(eventType.name, events)),"publishEvents") log.info(s"EVENTS published: $events") events } def updateEventType(eType: EventType): Boolean = { - wasItsuccessfull(executeCall(client.updateEventType(eventType.name, eType))) + wasItsuccessfull(executeCall(client.updateEventType(eventType.name, eType)),"updateEventType") } def eventTypeExist(eventTypeName: String = eventType.name): Boolean = { @@ -55,9 +55,9 @@ class EventIntegrationHelper(generator: EventGenerator, client: Client) extends } //Private methods - private def wasItsuccessfull(in: Option[ClientError]): Boolean = in match { + private def wasItsuccessfull(in: Option[ClientError], msg: String): Boolean = in match { case Some(clientError) => - log.error("ClientError: {}", clientError) + log.error("{} => ClientError: {}", generator.eventTypeId+"-"+msg, clientError) false case None => true diff --git a/it/src/test/scala/org/zalando/nakadi/client/scala/SimpleEventTest.scala b/it/src/test/scala/org/zalando/nakadi/client/scala/SimpleEventTest.scala index cc2c23e..37435d8 100644 --- a/it/src/test/scala/org/zalando/nakadi/client/scala/SimpleEventTest.scala +++ b/it/src/test/scala/org/zalando/nakadi/client/scala/SimpleEventTest.scala @@ -26,6 +26,8 @@ class SimpleEventTest extends WordSpec with Matchers { def eventTypeId = s"SimpleEventIntegrationTest-Handle-404-Graciously" } val it = new EventIntegrationHelper(eventGenerator, client) + it.createEventType() shouldBe true + it.getEventType("non-existing-event-type-name") match { case Some(_) => fail("Should not fail, because eventType was not created yet!!") case None => @@ -38,7 +40,7 @@ class SimpleEventTest extends WordSpec with Matchers { } val it = new EventIntegrationHelper(eventGenerator, client) val cursor = Some(Cursor("0", "BEGIN")) - it.createEventType() + it.createEventType() shouldBe true val events = it.publishEvents(nrOfEvents) client.subscribe(eventGenerator.eventTypeName, StreamParameters(cursor = cursor), listener) val receivedEvents = listener.waitToReceive(nrOfEvents) From 773c55eaef1c67aa62b49d9c8e7a7b381a1f04a6 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Mon, 20 Jun 2016 11:55:49 +0200 Subject: [PATCH 089/183] techjira:LAAS-60 StreamParameter stream_limit should be respected! Fixes #67. --- .../org/zalando/nakadi/client/actor/ConsumingActor.scala | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/client/src/main/scala/org/zalando/nakadi/client/actor/ConsumingActor.scala b/client/src/main/scala/org/zalando/nakadi/client/actor/ConsumingActor.scala index 0ec1ffd..db5e02b 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/actor/ConsumingActor.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/actor/ConsumingActor.scala @@ -21,8 +21,6 @@ import akka.stream.actor._ import akka.util.ByteString import org.zalando.nakadi.client.utils.ModelConverter import org.zalando.nakadi.client.scala.EventHandler -import org.zalando.nakadi.client.scala.ScalaResult -import org.zalando.nakadi.client.scala.JavaResult import org.zalando.nakadi.client.scala.ErrorResult import org.zalando.nakadi.client.java.model.{ Event => JEvent } import SupervisingActor._ @@ -68,8 +66,9 @@ class ConsumingActor(subscription: SubscriptionKey, log.error("onError - cursor {} - {} - error {}", lastCursor, subscription, err.getMessage) context.stop(self) case OnComplete => + // When using stream_limit, the server stops the connection. log.info("onComplete - cursor {} - {}", lastCursor, subscription) - context.stop(self) + context.parent ! UnsubscribeMsg case Terminated => log.info("Received Terminated msg - subscription {} with listener-id {} ", subscription, handler.id()) case a => From 0db23d3279ec080b68025eda04537407497d6244 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Mon, 20 Jun 2016 12:04:10 +0200 Subject: [PATCH 090/183] techjira:LAAS-60 Adding equals & hashCode to StreamParameter and Cursor. Fixes #66. --- .../zalando/nakadi/client/java/Listener.java | 6 +- .../nakadi/client/java/StreamParameters.java | 70 +++++++++ .../nakadi/client/java/model/Cursor.java | 33 +++++ .../client/java/JavaClientHandler.scala | 5 +- .../nakadi/client/scala/ClientImpl.scala | 2 +- .../nakadi/client/scala/MessageHandler.scala | 135 +++++++----------- .../examples/java/EventListenerExample.java | 5 +- .../factory/{ => events}/MySimpleEvent.java | 2 +- .../examples/scala/EventListenerExample.scala | 6 +- .../nakadi/client/scala/ClientFactory.scala | 6 +- project/Dependencies.scala | 2 +- 11 files changed, 174 insertions(+), 98 deletions(-) rename it/src/main/java/org/zalando/nakadi/client/java/test/factory/{ => events}/MySimpleEvent.java (82%) diff --git a/api/src/main/java/org/zalando/nakadi/client/java/Listener.java b/api/src/main/java/org/zalando/nakadi/client/java/Listener.java index f99104e..1ea2caf 100644 --- a/api/src/main/java/org/zalando/nakadi/client/java/Listener.java +++ b/api/src/main/java/org/zalando/nakadi/client/java/Listener.java @@ -10,9 +10,9 @@ public interface Listener { String getId(); - void onReceive(String eventType, Cursor cursor, List events); + void onReceive(String endpoint, Cursor cursor, List events); - void onSubscribed(String eventType, Optional cursor); + void onSubscribed(String endpoint, Optional cursor); - void onError(String eventType, Optional error); + void onError(String endpoint, Optional error); } diff --git a/api/src/main/java/org/zalando/nakadi/client/java/StreamParameters.java b/api/src/main/java/org/zalando/nakadi/client/java/StreamParameters.java index 13be205..212a8ad 100644 --- a/api/src/main/java/org/zalando/nakadi/client/java/StreamParameters.java +++ b/api/src/main/java/org/zalando/nakadi/client/java/StreamParameters.java @@ -63,4 +63,74 @@ public Optional getFlowId() { return flowId; } + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((batchFlushTimeout == null) ? 0 : batchFlushTimeout.hashCode()); + result = prime * result + ((batchLimit == null) ? 0 : batchLimit.hashCode()); + result = prime * result + ((cursor == null) ? 0 : cursor.hashCode()); + result = prime * result + ((flowId == null) ? 0 : flowId.hashCode()); + result = prime * result + ((streamKeepAliveLimit == null) ? 0 : streamKeepAliveLimit.hashCode()); + result = prime * result + ((streamLimit == null) ? 0 : streamLimit.hashCode()); + result = prime * result + ((streamTimeout == null) ? 0 : streamTimeout.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + StreamParameters other = (StreamParameters) obj; + if (batchFlushTimeout == null) { + if (other.batchFlushTimeout != null) + return false; + } else if (!batchFlushTimeout.equals(other.batchFlushTimeout)) + return false; + if (batchLimit == null) { + if (other.batchLimit != null) + return false; + } else if (!batchLimit.equals(other.batchLimit)) + return false; + if (cursor == null) { + if (other.cursor != null) + return false; + } else if (!cursor.equals(other.cursor)) + return false; + if (flowId == null) { + if (other.flowId != null) + return false; + } else if (!flowId.equals(other.flowId)) + return false; + if (streamKeepAliveLimit == null) { + if (other.streamKeepAliveLimit != null) + return false; + } else if (!streamKeepAliveLimit.equals(other.streamKeepAliveLimit)) + return false; + if (streamLimit == null) { + if (other.streamLimit != null) + return false; + } else if (!streamLimit.equals(other.streamLimit)) + return false; + if (streamTimeout == null) { + if (other.streamTimeout != null) + return false; + } else if (!streamTimeout.equals(other.streamTimeout)) + return false; + return true; + } + + @Override + public String toString() { + return "StreamParameters [cursor=" + cursor + ", batchLimit=" + batchLimit + ", streamLimit=" + streamLimit + + ", batchFlushTimeout=" + batchFlushTimeout + ", streamTimeout=" + streamTimeout + ", streamKeepAliveLimit=" + + streamKeepAliveLimit + ", flowId=" + flowId + "]"; + } + + + } diff --git a/api/src/main/java/org/zalando/nakadi/client/java/model/Cursor.java b/api/src/main/java/org/zalando/nakadi/client/java/model/Cursor.java index 5f530a8..597fb04 100644 --- a/api/src/main/java/org/zalando/nakadi/client/java/model/Cursor.java +++ b/api/src/main/java/org/zalando/nakadi/client/java/model/Cursor.java @@ -33,4 +33,37 @@ public String getOffset() { return offset; } + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((offset == null) ? 0 : offset.hashCode()); + result = prime * result + ((partition == null) ? 0 : partition.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Cursor other = (Cursor) obj; + if (offset == null) { + if (other.offset != null) + return false; + } else if (!offset.equals(other.offset)) + return false; + if (partition == null) { + if (other.partition != null) + return false; + } else if (!partition.equals(other.partition)) + return false; + return true; + } + + + } diff --git a/client/src/main/scala/org/zalando/nakadi/client/java/JavaClientHandler.scala b/client/src/main/scala/org/zalando/nakadi/client/java/JavaClientHandler.scala index 38682e1..1903bf8 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/java/JavaClientHandler.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/java/JavaClientHandler.scala @@ -21,7 +21,6 @@ import org.zalando.nakadi.client.java.{ Listener => JListener } import org.zalando.nakadi.client.scala.Connection import org.zalando.nakadi.client.scala.EmptyScalaEvent import org.zalando.nakadi.client.scala.EventHandler -import org.zalando.nakadi.client.scala.EventHandlerImpl import org.zalando.nakadi.client.scala.HttpFactory import org.zalando.nakadi.client.scala.{ StreamParameters => ScalaStreamParameters } import org.zalando.nakadi.client.utils.FutureConversions @@ -37,6 +36,8 @@ import org.zalando.nakadi.client.handler.SubscriptionHandlerImpl import org.zalando.nakadi.client.handler.SubscriptionHandler import org.zalando.nakadi.client.utils.GeneralConversions import akka.http.scaladsl.model.StatusCodes +import org.zalando.nakadi.client.scala.ScalaEventHandlerImpl +import org.zalando.nakadi.client.scala.JavaEventHandlerImpl /** * Handler for mapping(Java<->Scala) and handling http calls and listener subscriptions for the Java API. @@ -113,7 +114,7 @@ class JavaClientHandlerImpl(val connection: Connection, subscriber: Subscription def subscribe[T <: JEvent](eventTypeName: String, endpoint: String, parameters: JStreamParameters, listener: JListener[T])(implicit des: Deserializer[JEventStreamBatch[T]]) = { import ModelConverter._ val params: Option[ScalaStreamParameters] = toScalaStreamParameters(parameters) - val eventHandler: EventHandler = new EventHandlerImpl[T, EmptyScalaEvent](Left((des, listener))) + val eventHandler: EventHandler = new JavaEventHandlerImpl(des, listener) val finalUrl = withUrl(endpoint, params) val res = subscriber.subscribe(eventTypeName, finalUrl, getCursor(params), eventHandler) toJavaClientError(res) diff --git a/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala index 5f65833..ba69076 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala @@ -117,7 +117,7 @@ class ClientImpl(connection: Connection, subscriber: SubscriptionHandler, charSe val url = URI_EVENTS_OF_EVENT_TYPE.format(eventType) logger.debug("Subscribing listener {} - cursor {} - parameters {} - eventType {} - url {}", listener.id, cursor, params, eventType, url) val finalUrl = withUrl(url, Some(params)) - val eventHandler: EventHandler = new EventHandlerImpl[EmptyJavaEvent, T](Right((des, listener))) + val eventHandler: EventHandler = new ScalaEventHandlerImpl(des, listener) subscriber.subscribe(eventTypeName, finalUrl, cursor, eventHandler) } diff --git a/client/src/main/scala/org/zalando/nakadi/client/scala/MessageHandler.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/MessageHandler.scala index d6543d9..ff05872 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/scala/MessageHandler.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/scala/MessageHandler.scala @@ -17,27 +17,14 @@ import java.util.{ List => JList } import java.util.Optional import org.slf4j.LoggerFactory import com.typesafe.scalalogging.Logger +import jdk.nashorn.internal.runtime.regexp.joni.constants.Arguments +import com.google.common.base.Preconditions._ -/** - * Internal Models for handling logic between Java and Scala - */ +case class ErrorResult(error: Throwable) /** - * Base result for Deserialization Attempt. + * EventHandlers should handle the logic of deserializing the messages and acting op on this attempt. */ -trait Result - -private case class JavaResult[J <: JEvent]( - val javaEvents: Option[java.util.List[J]], - val scalaCursor: Cursor, - val javaCursor: JCursor) extends Result - -private case class ScalaResult[S <: Event]( - scalaEvents: Option[Seq[S]] = None, - scalaCursor: Cursor) extends Result - -case class ErrorResult(error: Throwable) extends Result - trait EventHandler { def id(): String def handleOnReceive(eventTypeName: String, msg: String): Either[ErrorResult, Cursor] @@ -45,89 +32,75 @@ trait EventHandler { def handleOnError(eventTypeName: String, msg: Option[String], exception: Throwable) } -class EventHandlerImpl[J <: JEvent, S <: Event]( - eitherOfListeners: Either[(Deserializer[JEventStreamBatch[J]], JListener[J]), (Deserializer[EventStreamBatch[S]], Listener[S])]) extends EventHandler { - import EventHandler._ +class ScalaEventHandlerImpl[S <: Event](des: Deserializer[EventStreamBatch[S]], listener: Listener[S]) extends EventHandler { private val log = Logger(LoggerFactory.getLogger(this.getClass)) private def createException(msg: String) = new IllegalStateException(msg) + checkArgument(listener != null, "Listener must not be null", null) + checkArgument(des != null, "Deserializer must not be null", null) - lazy val id: String = { - - eitherOfListeners match { - //Validations - case Left((des, listener)) if (listener == null) => throw new IllegalStateException("EventHandler created without a Listener/Deserializer") - case Right((des, listener)) if (listener == null) => throw new IllegalStateException("EventHandler created without a Listener/Deserializer") - //Real handling - case Left((_, listener)) => listener.getId - case Right((_, listener)) => listener.id - } - } + def id: String = listener.id def handleOnReceive(eventTypeName: String, msg: String): Either[ErrorResult, Cursor] = { - eitherOfListeners match { - case Left((des, listener)) => // Java - transformJava(msg, des).right.flatMap { - case JavaResult(Some(events), sCursor, jCursor) => - listener.onReceive(eventTypeName, jCursor, events) - Right(sCursor) - case JavaResult(None, sCursor, jCursor) => - Right(sCursor) - case _ => - val errorMsg = s"Could not handle JAVA Transformation url [$eventTypeName] listener [${listener.getId}] msg [$msg]" - log.error(errorMsg) - listener.onError(errorMsg, Optional.empty()) - Left(ErrorResult(createException(errorMsg))) - } - case Right((des, listener)) => //Scala - transformScala(msg, des).right.flatMap { - case ScalaResult(Some(events), cursor) => - listener.onReceive(eventTypeName, cursor, events) - Right(cursor) - case ScalaResult(None, cursor) => - Right(cursor) - case _ => - val errorMsg = s"Could not handle SCALA Transformation url [$eventTypeName] listener [${listener.id}] msg [$msg]" - listener.onError(errorMsg, None) - log.error(errorMsg) - Left(ErrorResult(createException(errorMsg))) - } + Try(des.from(msg)) match { + case Success(EventStreamBatch(cursor, None)) => Right(cursor) + case Success(EventStreamBatch(cursor, Some(Nil))) => Right(cursor) + case Success(EventStreamBatch(cursor, Some(events))) => + listener.onReceive(eventTypeName, cursor, events) + Right(cursor) + case Failure(err) => + val errorMsg = s"Could not deserialize[Scala] url [$eventTypeName] listener [${listener.id}] msg [$msg] error[${err.getMessage}]" + log.error(errorMsg) + listener.onError(errorMsg, None) + Left(ErrorResult(createException(errorMsg))) + } + } def handleOnError(eventTypeName: String, msg: Option[String], exception: Throwable) = { val errorMsg = if (msg.isDefined) msg.get else exception.getMessage val clientError = Some(ClientError(errorMsg, exception = Some(exception))) - eitherOfListeners match { - case Left((des, listener)) => // Java - listener.onError(errorMsg, toJavaClientError(clientError)) - case Right((des, listener)) => //Scala - listener.onError(errorMsg, clientError) - } - } - def handleOnSubscribed(endpoint: String, cursor: Option[Cursor]): Unit = eitherOfListeners match { - case Left((des, listener)) => - val res: Optional[JCursor] = toJavaCursor(cursor) - listener.onSubscribed(endpoint, res) // Java - case Right((des, listener)) => listener.onSubscribed(endpoint, cursor) + listener.onError(errorMsg, clientError) } + def handleOnSubscribed(endpoint: String, cursor: Option[Cursor]): Unit = listener.onSubscribed(endpoint, cursor) } +class JavaEventHandlerImpl[J <: JEvent](des: Deserializer[JEventStreamBatch[J]], listener: JListener[J]) extends EventHandler { + private val log = Logger(LoggerFactory.getLogger(this.getClass)) + private def createException(msg: String) = new IllegalStateException(msg) + checkArgument(listener != null, "Listener must not be null", null) + checkArgument(des != null, "Deserializer must not be null", null) -private object EventHandler { - def transformScala[S <: Event](message: String, des: Deserializer[EventStreamBatch[S]]): Either[ErrorResult, ScalaResult[S]] = Try(des.from(message)) match { - case Success(EventStreamBatch(cursor, events)) => Right(ScalaResult(events, cursor)) - case Failure(err) => Left(ErrorResult(err)) - } + def id: String = listener.getId - def transformJava[J <: JEvent](message: String, des: Deserializer[JEventStreamBatch[J]]): Either[ErrorResult, JavaResult[J]] = { - Try(des.from(message)) match { - case Success(eventBatch) => - val events = if (eventBatch.getEvents != null && !eventBatch.getEvents.isEmpty()) Some(eventBatch.getEvents) else None + def handleOnReceive(eventTypeName: String, msg: String): Either[ErrorResult, Cursor] = { + Try(des.from(msg)) match { + case Success(eventBatch) if (eventBatch.getEvents != null && !eventBatch.getEvents.isEmpty()) => val Some(sCursor: Cursor) = toScalaCursor(eventBatch.getCursor) val jcursor: JCursor = eventBatch.getCursor - Right(JavaResult(events, sCursor, jcursor)) + listener.onReceive(eventTypeName, jcursor, eventBatch.getEvents) + Right(sCursor) + case Success(eventBatch) => + val Some(sCursor: Cursor) = toScalaCursor(eventBatch.getCursor) + Right(sCursor) case Failure(err) => Left(ErrorResult(err)) + val errorMsg = s"Could not deserialize[Java] url [$eventTypeName] listener [${listener.getId}] msg [$msg] error[${err.getMessage}]" + log.error(errorMsg) + listener.onError(errorMsg, Optional.empty()) + Left(ErrorResult(createException(errorMsg))) } } -} \ No newline at end of file + + def handleOnError(eventTypeName: String, msg: Option[String], exception: Throwable) = { + val errorMsg = if (msg.isDefined) msg.get else exception.getMessage + val clientError = Some(ClientError(errorMsg, exception = Some(exception))) + listener.onError(errorMsg, toJavaClientError(clientError)) + } + def handleOnSubscribed(endpoint: String, cursor: Option[Cursor]): Unit = { + val res: Optional[JCursor] = toJavaCursor(cursor) + listener.onSubscribed(endpoint, res) + } + +} + diff --git a/it/src/main/java/org/zalando/nakadi/client/examples/java/EventListenerExample.java b/it/src/main/java/org/zalando/nakadi/client/examples/java/EventListenerExample.java index a414042..1b70b74 100644 --- a/it/src/main/java/org/zalando/nakadi/client/examples/java/EventListenerExample.java +++ b/it/src/main/java/org/zalando/nakadi/client/examples/java/EventListenerExample.java @@ -31,10 +31,9 @@ public static void main(String[] args) throws InterruptedException, Listener listener = new EventCounterListener("Java-Test"); StreamParameters params = new StreamParameters( -// Optional.of(new Cursor("0", "BEGIN")), - Optional.empty(), + Optional.of(new Cursor("0", "BEGIN")), Optional.of(100),// batchLimit, - Optional.empty(),// streamLimit, + Optional.of(200),// streamLimit, Optional.empty(),// batchFlushTimeout, Optional.empty(),// streamTimeout, Optional.empty(),// streamKeepAliveLimit, diff --git a/it/src/main/java/org/zalando/nakadi/client/java/test/factory/MySimpleEvent.java b/it/src/main/java/org/zalando/nakadi/client/java/test/factory/events/MySimpleEvent.java similarity index 82% rename from it/src/main/java/org/zalando/nakadi/client/java/test/factory/MySimpleEvent.java rename to it/src/main/java/org/zalando/nakadi/client/java/test/factory/events/MySimpleEvent.java index 85434c6..7ec2c03 100644 --- a/it/src/main/java/org/zalando/nakadi/client/java/test/factory/MySimpleEvent.java +++ b/it/src/main/java/org/zalando/nakadi/client/java/test/factory/events/MySimpleEvent.java @@ -1,4 +1,4 @@ -package org.zalando.nakadi.client.java.test.factory; +package org.zalando.nakadi.client.java.test.factory.events; import org.zalando.nakadi.client.java.model.Event; diff --git a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala index 582d2c5..27b580f 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala @@ -59,13 +59,13 @@ object EventListenerExample extends App { val cursor = Cursor("0", "0") val parameters = new StreamParameters( - cursor = None//Some(cursor) // + cursor = Some(cursor) // , batchLimit = Some(250) // Maximum number of `Event`s in each chunk (and therefore per partition) of the stream. - // , streamLimit = Some(2) // Maximum number of `Event`s to stream (over all partitions being streamed in this + , streamLimit = Some(500) // Maximum number of `Event`s to stream (over all partitions being streamed in this //connection). , batchFlushTimeout = Some(5) // Maximum time in seconds to wait for the flushing of each chunk (per partition). // ,streamKeepAliveLimit=Some(4) - , streamTimeout = Some(30) + , streamTimeout = Some(7) ) /** diff --git a/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala b/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala index ab5d91d..4c6a6df 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala @@ -9,7 +9,7 @@ object ClientFactory { def host(): String = "nakadi.test.fernando.io" def localHost(): String = "localhost" def localPort(): Integer = 8080 - def OAuth2Token(): Option[() => String] = Option(() => "1c1d7814-c31e-40b8-bbca-f340c6909b63") + def OAuth2Token(): Option[() => String] = Option(() => "5ff42811-3b0c-4a33-9836-d1adec4db94f") def port(): Integer = 443 def getJavaClient() = builder().buildJavaClient(); @@ -18,8 +18,8 @@ object ClientFactory { private def builder() = { // useTest() -// useStaging() - useLocal() + useStaging() +// useLocal() } private def useLocal() = { new ClientBuilder() // diff --git a/project/Dependencies.scala b/project/Dependencies.scala index d219152..ef8c2d0 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -2,7 +2,7 @@ import sbt._ object Dependencies { - val akkaVersion = "2.4.4" + val akkaVersion = "2.4.7" val jacksonVersion = "2.7.3" val apiDeps = { Seq( From dd25e3229ae53fcd36f16d55e974c67232fb178e Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Mon, 20 Jun 2016 12:04:10 +0200 Subject: [PATCH 091/183] techjira:LAAS-60 Adding equals & hashCode to StreamParameter and Cursor. Fixes #66. --- .../zalando/nakadi/client/java/Listener.java | 6 +- .../nakadi/client/java/StreamParameters.java | 70 +++++++++ .../nakadi/client/java/model/Cursor.java | 33 +++++ .../client/java/JavaClientHandler.scala | 5 +- .../nakadi/client/scala/ClientImpl.scala | 2 +- .../nakadi/client/scala/MessageHandler.scala | 135 +++++++----------- .../examples/java/EventListenerExample.java | 5 +- .../factory/{ => events}/MySimpleEvent.java | 2 +- .../examples/scala/EventListenerExample.scala | 6 +- .../nakadi/client/scala/ClientFactory.scala | 6 +- project/Dependencies.scala | 2 +- 11 files changed, 174 insertions(+), 98 deletions(-) rename it/src/main/java/org/zalando/nakadi/client/java/test/factory/{ => events}/MySimpleEvent.java (82%) diff --git a/api/src/main/java/org/zalando/nakadi/client/java/Listener.java b/api/src/main/java/org/zalando/nakadi/client/java/Listener.java index f99104e..1ea2caf 100644 --- a/api/src/main/java/org/zalando/nakadi/client/java/Listener.java +++ b/api/src/main/java/org/zalando/nakadi/client/java/Listener.java @@ -10,9 +10,9 @@ public interface Listener { String getId(); - void onReceive(String eventType, Cursor cursor, List events); + void onReceive(String endpoint, Cursor cursor, List events); - void onSubscribed(String eventType, Optional cursor); + void onSubscribed(String endpoint, Optional cursor); - void onError(String eventType, Optional error); + void onError(String endpoint, Optional error); } diff --git a/api/src/main/java/org/zalando/nakadi/client/java/StreamParameters.java b/api/src/main/java/org/zalando/nakadi/client/java/StreamParameters.java index 13be205..212a8ad 100644 --- a/api/src/main/java/org/zalando/nakadi/client/java/StreamParameters.java +++ b/api/src/main/java/org/zalando/nakadi/client/java/StreamParameters.java @@ -63,4 +63,74 @@ public Optional getFlowId() { return flowId; } + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((batchFlushTimeout == null) ? 0 : batchFlushTimeout.hashCode()); + result = prime * result + ((batchLimit == null) ? 0 : batchLimit.hashCode()); + result = prime * result + ((cursor == null) ? 0 : cursor.hashCode()); + result = prime * result + ((flowId == null) ? 0 : flowId.hashCode()); + result = prime * result + ((streamKeepAliveLimit == null) ? 0 : streamKeepAliveLimit.hashCode()); + result = prime * result + ((streamLimit == null) ? 0 : streamLimit.hashCode()); + result = prime * result + ((streamTimeout == null) ? 0 : streamTimeout.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + StreamParameters other = (StreamParameters) obj; + if (batchFlushTimeout == null) { + if (other.batchFlushTimeout != null) + return false; + } else if (!batchFlushTimeout.equals(other.batchFlushTimeout)) + return false; + if (batchLimit == null) { + if (other.batchLimit != null) + return false; + } else if (!batchLimit.equals(other.batchLimit)) + return false; + if (cursor == null) { + if (other.cursor != null) + return false; + } else if (!cursor.equals(other.cursor)) + return false; + if (flowId == null) { + if (other.flowId != null) + return false; + } else if (!flowId.equals(other.flowId)) + return false; + if (streamKeepAliveLimit == null) { + if (other.streamKeepAliveLimit != null) + return false; + } else if (!streamKeepAliveLimit.equals(other.streamKeepAliveLimit)) + return false; + if (streamLimit == null) { + if (other.streamLimit != null) + return false; + } else if (!streamLimit.equals(other.streamLimit)) + return false; + if (streamTimeout == null) { + if (other.streamTimeout != null) + return false; + } else if (!streamTimeout.equals(other.streamTimeout)) + return false; + return true; + } + + @Override + public String toString() { + return "StreamParameters [cursor=" + cursor + ", batchLimit=" + batchLimit + ", streamLimit=" + streamLimit + + ", batchFlushTimeout=" + batchFlushTimeout + ", streamTimeout=" + streamTimeout + ", streamKeepAliveLimit=" + + streamKeepAliveLimit + ", flowId=" + flowId + "]"; + } + + + } diff --git a/api/src/main/java/org/zalando/nakadi/client/java/model/Cursor.java b/api/src/main/java/org/zalando/nakadi/client/java/model/Cursor.java index 5f530a8..597fb04 100644 --- a/api/src/main/java/org/zalando/nakadi/client/java/model/Cursor.java +++ b/api/src/main/java/org/zalando/nakadi/client/java/model/Cursor.java @@ -33,4 +33,37 @@ public String getOffset() { return offset; } + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((offset == null) ? 0 : offset.hashCode()); + result = prime * result + ((partition == null) ? 0 : partition.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Cursor other = (Cursor) obj; + if (offset == null) { + if (other.offset != null) + return false; + } else if (!offset.equals(other.offset)) + return false; + if (partition == null) { + if (other.partition != null) + return false; + } else if (!partition.equals(other.partition)) + return false; + return true; + } + + + } diff --git a/client/src/main/scala/org/zalando/nakadi/client/java/JavaClientHandler.scala b/client/src/main/scala/org/zalando/nakadi/client/java/JavaClientHandler.scala index 38682e1..1903bf8 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/java/JavaClientHandler.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/java/JavaClientHandler.scala @@ -21,7 +21,6 @@ import org.zalando.nakadi.client.java.{ Listener => JListener } import org.zalando.nakadi.client.scala.Connection import org.zalando.nakadi.client.scala.EmptyScalaEvent import org.zalando.nakadi.client.scala.EventHandler -import org.zalando.nakadi.client.scala.EventHandlerImpl import org.zalando.nakadi.client.scala.HttpFactory import org.zalando.nakadi.client.scala.{ StreamParameters => ScalaStreamParameters } import org.zalando.nakadi.client.utils.FutureConversions @@ -37,6 +36,8 @@ import org.zalando.nakadi.client.handler.SubscriptionHandlerImpl import org.zalando.nakadi.client.handler.SubscriptionHandler import org.zalando.nakadi.client.utils.GeneralConversions import akka.http.scaladsl.model.StatusCodes +import org.zalando.nakadi.client.scala.ScalaEventHandlerImpl +import org.zalando.nakadi.client.scala.JavaEventHandlerImpl /** * Handler for mapping(Java<->Scala) and handling http calls and listener subscriptions for the Java API. @@ -113,7 +114,7 @@ class JavaClientHandlerImpl(val connection: Connection, subscriber: Subscription def subscribe[T <: JEvent](eventTypeName: String, endpoint: String, parameters: JStreamParameters, listener: JListener[T])(implicit des: Deserializer[JEventStreamBatch[T]]) = { import ModelConverter._ val params: Option[ScalaStreamParameters] = toScalaStreamParameters(parameters) - val eventHandler: EventHandler = new EventHandlerImpl[T, EmptyScalaEvent](Left((des, listener))) + val eventHandler: EventHandler = new JavaEventHandlerImpl(des, listener) val finalUrl = withUrl(endpoint, params) val res = subscriber.subscribe(eventTypeName, finalUrl, getCursor(params), eventHandler) toJavaClientError(res) diff --git a/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala index 5f65833..ba69076 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala @@ -117,7 +117,7 @@ class ClientImpl(connection: Connection, subscriber: SubscriptionHandler, charSe val url = URI_EVENTS_OF_EVENT_TYPE.format(eventType) logger.debug("Subscribing listener {} - cursor {} - parameters {} - eventType {} - url {}", listener.id, cursor, params, eventType, url) val finalUrl = withUrl(url, Some(params)) - val eventHandler: EventHandler = new EventHandlerImpl[EmptyJavaEvent, T](Right((des, listener))) + val eventHandler: EventHandler = new ScalaEventHandlerImpl(des, listener) subscriber.subscribe(eventTypeName, finalUrl, cursor, eventHandler) } diff --git a/client/src/main/scala/org/zalando/nakadi/client/scala/MessageHandler.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/MessageHandler.scala index d6543d9..ff05872 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/scala/MessageHandler.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/scala/MessageHandler.scala @@ -17,27 +17,14 @@ import java.util.{ List => JList } import java.util.Optional import org.slf4j.LoggerFactory import com.typesafe.scalalogging.Logger +import jdk.nashorn.internal.runtime.regexp.joni.constants.Arguments +import com.google.common.base.Preconditions._ -/** - * Internal Models for handling logic between Java and Scala - */ +case class ErrorResult(error: Throwable) /** - * Base result for Deserialization Attempt. + * EventHandlers should handle the logic of deserializing the messages and acting op on this attempt. */ -trait Result - -private case class JavaResult[J <: JEvent]( - val javaEvents: Option[java.util.List[J]], - val scalaCursor: Cursor, - val javaCursor: JCursor) extends Result - -private case class ScalaResult[S <: Event]( - scalaEvents: Option[Seq[S]] = None, - scalaCursor: Cursor) extends Result - -case class ErrorResult(error: Throwable) extends Result - trait EventHandler { def id(): String def handleOnReceive(eventTypeName: String, msg: String): Either[ErrorResult, Cursor] @@ -45,89 +32,75 @@ trait EventHandler { def handleOnError(eventTypeName: String, msg: Option[String], exception: Throwable) } -class EventHandlerImpl[J <: JEvent, S <: Event]( - eitherOfListeners: Either[(Deserializer[JEventStreamBatch[J]], JListener[J]), (Deserializer[EventStreamBatch[S]], Listener[S])]) extends EventHandler { - import EventHandler._ +class ScalaEventHandlerImpl[S <: Event](des: Deserializer[EventStreamBatch[S]], listener: Listener[S]) extends EventHandler { private val log = Logger(LoggerFactory.getLogger(this.getClass)) private def createException(msg: String) = new IllegalStateException(msg) + checkArgument(listener != null, "Listener must not be null", null) + checkArgument(des != null, "Deserializer must not be null", null) - lazy val id: String = { - - eitherOfListeners match { - //Validations - case Left((des, listener)) if (listener == null) => throw new IllegalStateException("EventHandler created without a Listener/Deserializer") - case Right((des, listener)) if (listener == null) => throw new IllegalStateException("EventHandler created without a Listener/Deserializer") - //Real handling - case Left((_, listener)) => listener.getId - case Right((_, listener)) => listener.id - } - } + def id: String = listener.id def handleOnReceive(eventTypeName: String, msg: String): Either[ErrorResult, Cursor] = { - eitherOfListeners match { - case Left((des, listener)) => // Java - transformJava(msg, des).right.flatMap { - case JavaResult(Some(events), sCursor, jCursor) => - listener.onReceive(eventTypeName, jCursor, events) - Right(sCursor) - case JavaResult(None, sCursor, jCursor) => - Right(sCursor) - case _ => - val errorMsg = s"Could not handle JAVA Transformation url [$eventTypeName] listener [${listener.getId}] msg [$msg]" - log.error(errorMsg) - listener.onError(errorMsg, Optional.empty()) - Left(ErrorResult(createException(errorMsg))) - } - case Right((des, listener)) => //Scala - transformScala(msg, des).right.flatMap { - case ScalaResult(Some(events), cursor) => - listener.onReceive(eventTypeName, cursor, events) - Right(cursor) - case ScalaResult(None, cursor) => - Right(cursor) - case _ => - val errorMsg = s"Could not handle SCALA Transformation url [$eventTypeName] listener [${listener.id}] msg [$msg]" - listener.onError(errorMsg, None) - log.error(errorMsg) - Left(ErrorResult(createException(errorMsg))) - } + Try(des.from(msg)) match { + case Success(EventStreamBatch(cursor, None)) => Right(cursor) + case Success(EventStreamBatch(cursor, Some(Nil))) => Right(cursor) + case Success(EventStreamBatch(cursor, Some(events))) => + listener.onReceive(eventTypeName, cursor, events) + Right(cursor) + case Failure(err) => + val errorMsg = s"Could not deserialize[Scala] url [$eventTypeName] listener [${listener.id}] msg [$msg] error[${err.getMessage}]" + log.error(errorMsg) + listener.onError(errorMsg, None) + Left(ErrorResult(createException(errorMsg))) + } + } def handleOnError(eventTypeName: String, msg: Option[String], exception: Throwable) = { val errorMsg = if (msg.isDefined) msg.get else exception.getMessage val clientError = Some(ClientError(errorMsg, exception = Some(exception))) - eitherOfListeners match { - case Left((des, listener)) => // Java - listener.onError(errorMsg, toJavaClientError(clientError)) - case Right((des, listener)) => //Scala - listener.onError(errorMsg, clientError) - } - } - def handleOnSubscribed(endpoint: String, cursor: Option[Cursor]): Unit = eitherOfListeners match { - case Left((des, listener)) => - val res: Optional[JCursor] = toJavaCursor(cursor) - listener.onSubscribed(endpoint, res) // Java - case Right((des, listener)) => listener.onSubscribed(endpoint, cursor) + listener.onError(errorMsg, clientError) } + def handleOnSubscribed(endpoint: String, cursor: Option[Cursor]): Unit = listener.onSubscribed(endpoint, cursor) } +class JavaEventHandlerImpl[J <: JEvent](des: Deserializer[JEventStreamBatch[J]], listener: JListener[J]) extends EventHandler { + private val log = Logger(LoggerFactory.getLogger(this.getClass)) + private def createException(msg: String) = new IllegalStateException(msg) + checkArgument(listener != null, "Listener must not be null", null) + checkArgument(des != null, "Deserializer must not be null", null) -private object EventHandler { - def transformScala[S <: Event](message: String, des: Deserializer[EventStreamBatch[S]]): Either[ErrorResult, ScalaResult[S]] = Try(des.from(message)) match { - case Success(EventStreamBatch(cursor, events)) => Right(ScalaResult(events, cursor)) - case Failure(err) => Left(ErrorResult(err)) - } + def id: String = listener.getId - def transformJava[J <: JEvent](message: String, des: Deserializer[JEventStreamBatch[J]]): Either[ErrorResult, JavaResult[J]] = { - Try(des.from(message)) match { - case Success(eventBatch) => - val events = if (eventBatch.getEvents != null && !eventBatch.getEvents.isEmpty()) Some(eventBatch.getEvents) else None + def handleOnReceive(eventTypeName: String, msg: String): Either[ErrorResult, Cursor] = { + Try(des.from(msg)) match { + case Success(eventBatch) if (eventBatch.getEvents != null && !eventBatch.getEvents.isEmpty()) => val Some(sCursor: Cursor) = toScalaCursor(eventBatch.getCursor) val jcursor: JCursor = eventBatch.getCursor - Right(JavaResult(events, sCursor, jcursor)) + listener.onReceive(eventTypeName, jcursor, eventBatch.getEvents) + Right(sCursor) + case Success(eventBatch) => + val Some(sCursor: Cursor) = toScalaCursor(eventBatch.getCursor) + Right(sCursor) case Failure(err) => Left(ErrorResult(err)) + val errorMsg = s"Could not deserialize[Java] url [$eventTypeName] listener [${listener.getId}] msg [$msg] error[${err.getMessage}]" + log.error(errorMsg) + listener.onError(errorMsg, Optional.empty()) + Left(ErrorResult(createException(errorMsg))) } } -} \ No newline at end of file + + def handleOnError(eventTypeName: String, msg: Option[String], exception: Throwable) = { + val errorMsg = if (msg.isDefined) msg.get else exception.getMessage + val clientError = Some(ClientError(errorMsg, exception = Some(exception))) + listener.onError(errorMsg, toJavaClientError(clientError)) + } + def handleOnSubscribed(endpoint: String, cursor: Option[Cursor]): Unit = { + val res: Optional[JCursor] = toJavaCursor(cursor) + listener.onSubscribed(endpoint, res) + } + +} + diff --git a/it/src/main/java/org/zalando/nakadi/client/examples/java/EventListenerExample.java b/it/src/main/java/org/zalando/nakadi/client/examples/java/EventListenerExample.java index a414042..1b70b74 100644 --- a/it/src/main/java/org/zalando/nakadi/client/examples/java/EventListenerExample.java +++ b/it/src/main/java/org/zalando/nakadi/client/examples/java/EventListenerExample.java @@ -31,10 +31,9 @@ public static void main(String[] args) throws InterruptedException, Listener listener = new EventCounterListener("Java-Test"); StreamParameters params = new StreamParameters( -// Optional.of(new Cursor("0", "BEGIN")), - Optional.empty(), + Optional.of(new Cursor("0", "BEGIN")), Optional.of(100),// batchLimit, - Optional.empty(),// streamLimit, + Optional.of(200),// streamLimit, Optional.empty(),// batchFlushTimeout, Optional.empty(),// streamTimeout, Optional.empty(),// streamKeepAliveLimit, diff --git a/it/src/main/java/org/zalando/nakadi/client/java/test/factory/MySimpleEvent.java b/it/src/main/java/org/zalando/nakadi/client/java/test/factory/events/MySimpleEvent.java similarity index 82% rename from it/src/main/java/org/zalando/nakadi/client/java/test/factory/MySimpleEvent.java rename to it/src/main/java/org/zalando/nakadi/client/java/test/factory/events/MySimpleEvent.java index 85434c6..7ec2c03 100644 --- a/it/src/main/java/org/zalando/nakadi/client/java/test/factory/MySimpleEvent.java +++ b/it/src/main/java/org/zalando/nakadi/client/java/test/factory/events/MySimpleEvent.java @@ -1,4 +1,4 @@ -package org.zalando.nakadi.client.java.test.factory; +package org.zalando.nakadi.client.java.test.factory.events; import org.zalando.nakadi.client.java.model.Event; diff --git a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala index 582d2c5..27b580f 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala @@ -59,13 +59,13 @@ object EventListenerExample extends App { val cursor = Cursor("0", "0") val parameters = new StreamParameters( - cursor = None//Some(cursor) // + cursor = Some(cursor) // , batchLimit = Some(250) // Maximum number of `Event`s in each chunk (and therefore per partition) of the stream. - // , streamLimit = Some(2) // Maximum number of `Event`s to stream (over all partitions being streamed in this + , streamLimit = Some(500) // Maximum number of `Event`s to stream (over all partitions being streamed in this //connection). , batchFlushTimeout = Some(5) // Maximum time in seconds to wait for the flushing of each chunk (per partition). // ,streamKeepAliveLimit=Some(4) - , streamTimeout = Some(30) + , streamTimeout = Some(7) ) /** diff --git a/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala b/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala index ab5d91d..7ea40d8 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala @@ -9,7 +9,7 @@ object ClientFactory { def host(): String = "nakadi.test.fernando.io" def localHost(): String = "localhost" def localPort(): Integer = 8080 - def OAuth2Token(): Option[() => String] = Option(() => "1c1d7814-c31e-40b8-bbca-f340c6909b63") + def OAuth2Token(): Option[() => String] = Option(() => "********-****-****-****-************") def port(): Integer = 443 def getJavaClient() = builder().buildJavaClient(); @@ -30,7 +30,7 @@ object ClientFactory { } private def useTest() = { ClientBuilder() - .withHost("nakadi-sandbox.aruha-test.zalan.do") + .withHost("nakadi********.**********.********") .withPort(443) .withSecuredConnection(true) //s .withVerifiedSslCertificate(false) //s @@ -38,7 +38,7 @@ object ClientFactory { } private def useStaging() = { ClientBuilder() - .withHost("nakadi-staging.aruha-test.zalan.do") + .withHost("nakadi********.**********.********") .withPort(443) .withSecuredConnection(true) //s .withVerifiedSslCertificate(false) //s diff --git a/project/Dependencies.scala b/project/Dependencies.scala index d219152..ef8c2d0 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -2,7 +2,7 @@ import sbt._ object Dependencies { - val akkaVersion = "2.4.4" + val akkaVersion = "2.4.7" val jacksonVersion = "2.7.3" val apiDeps = { Seq( From f201384376c07938f1542523c8f5ede030bf8138 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Mon, 20 Jun 2016 14:40:38 +0200 Subject: [PATCH 092/183] techjira:LAAS-60 Changed the return type for stopping the client. Added toString method. --- .../main/java/org/zalando/nakadi/client/java/Client.java | 2 +- .../zalando/nakadi/client/java/model/EventTypeSchema.java | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/api/src/main/java/org/zalando/nakadi/client/java/Client.java b/api/src/main/java/org/zalando/nakadi/client/java/Client.java index b87f826..9a1082a 100644 --- a/api/src/main/java/org/zalando/nakadi/client/java/Client.java +++ b/api/src/main/java/org/zalando/nakadi/client/java/Client.java @@ -134,7 +134,7 @@ public interface Client { * Shuts down the communication system of the client * @return Void in case of success */ - Future stop(); + void stop(); /** * Registers the subscription of a listener to start streaming events from a partition in non-blocking fashion. diff --git a/api/src/main/java/org/zalando/nakadi/client/java/model/EventTypeSchema.java b/api/src/main/java/org/zalando/nakadi/client/java/model/EventTypeSchema.java index 3c486f8..0e9e905 100644 --- a/api/src/main/java/org/zalando/nakadi/client/java/model/EventTypeSchema.java +++ b/api/src/main/java/org/zalando/nakadi/client/java/model/EventTypeSchema.java @@ -34,4 +34,11 @@ public String getSchema() { return schema; } + @Override + public String toString() { + return "EventTypeSchema [type=" + type + ", schema=" + schema + "]"; + } + + + } From 02a44c55311e45110f1aea3564cfc6503e0f5fa3 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Tue, 21 Jun 2016 10:35:53 +0200 Subject: [PATCH 093/183] techjira:LAAS-60 Implemented Integration tests for enums #65 --- .../test/factory/events/MySimpleEvent.java | 7 +- .../nakadi/client/scala/ClientFactory.scala | 16 +--- .../test/factory/EventIntegrationHelper.scala | 28 +++++-- .../test/factory/events/MySimpleEvent.scala | 4 +- .../nakadi/client/scala/SimpleEventTest.scala | 73 ++++++++++--------- 5 files changed, 73 insertions(+), 55 deletions(-) diff --git a/it/src/main/java/org/zalando/nakadi/client/java/test/factory/events/MySimpleEvent.java b/it/src/main/java/org/zalando/nakadi/client/java/test/factory/events/MySimpleEvent.java index 7ec2c03..a2da0c6 100644 --- a/it/src/main/java/org/zalando/nakadi/client/java/test/factory/events/MySimpleEvent.java +++ b/it/src/main/java/org/zalando/nakadi/client/java/test/factory/events/MySimpleEvent.java @@ -2,12 +2,15 @@ import org.zalando.nakadi.client.java.model.Event; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + public class MySimpleEvent implements Event { private String orderNumber; - - public MySimpleEvent(String orderNumber) { + @JsonCreator + public MySimpleEvent(@JsonProperty("order_number") String orderNumber) { this.orderNumber = orderNumber; } diff --git a/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala b/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala index 7ea40d8..a54eb81 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala @@ -6,19 +6,15 @@ import java.util.function.Supplier object ClientFactory { import sys.process._ import scala.language.postfixOps - def host(): String = "nakadi.test.fernando.io" - def localHost(): String = "localhost" - def localPort(): Integer = 8080 def OAuth2Token(): Option[() => String] = Option(() => "********-****-****-****-************") - def port(): Integer = 443 def getJavaClient() = builder().buildJavaClient(); def getScalaClient() = builder().build() private def builder() = { -// useTest() -// useStaging() + // useTest() + // useStaging() useLocal() } private def useLocal() = { @@ -37,11 +33,7 @@ object ClientFactory { .withTokenProvider(ClientFactory.OAuth2Token()) } private def useStaging() = { - ClientBuilder() - .withHost("nakadi********.**********.********") - .withPort(443) - .withSecuredConnection(true) //s - .withVerifiedSslCertificate(false) //s - .withTokenProvider(ClientFactory.OAuth2Token()) + useTest() + .withHost("nakadi********.**********.********") } } \ No newline at end of file diff --git a/it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/EventIntegrationHelper.scala b/it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/EventIntegrationHelper.scala index cdbe359..7afefa2 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/EventIntegrationHelper.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/EventIntegrationHelper.scala @@ -22,29 +22,29 @@ class EventIntegrationHelper(generator: EventGenerator, client: Client) extends val eventType = generator.eventType def createEventType(): Boolean = { - wasItsuccessfull(executeCall(client.createEventType(eventType)),"createEventType") + wasItsuccessfull(executeCall(client.createEventType(eventType)), "createEventType") } def getEventType(eventTypeName: String = eventType.name): Option[EventType] = executeCall(client.getEventType(eventTypeName)) match { case Left(clientError) => - wasItsuccessfull(Option(clientError),"getEventType") + wasItsuccessfull(Option(clientError), "getEventType") None case Right(result) => result } - def deleteEventType() = wasItsuccessfull(executeCall(client.deleteEventType(eventType.name)),"deleteEventType") + def deleteEventType() = wasItsuccessfull(executeCall(client.deleteEventType(eventType.name)), "deleteEventType") def publishEvents(nrOfEvents: Int): Seq[Event] = { val events = for { a <- 1 to nrOfEvents } yield generator.newEvent() - wasItsuccessfull(executeCall(client.publishEvents(eventType.name, events)),"publishEvents") + wasItsuccessfull(executeCall(client.publishEvents(eventType.name, events)), "publishEvents") log.info(s"EVENTS published: $events") events } def updateEventType(eType: EventType): Boolean = { - wasItsuccessfull(executeCall(client.updateEventType(eventType.name, eType)),"updateEventType") + wasItsuccessfull(executeCall(client.updateEventType(eventType.name, eType)), "updateEventType") } def eventTypeExist(eventTypeName: String = eventType.name): Boolean = { @@ -54,10 +54,26 @@ class EventIntegrationHelper(generator: EventGenerator, client: Client) extends } } + def getNumberOfPartitions(): Int = extractFromRightOptional(() => client.getPartitions(eventType.name)).size + + private def extractFromRightOptional[T](function: () => Future[Either[ClientError, Option[T]]]) = { + val received = executeCall(function()) + assert(received.isRight == true, s"ClientErrror ${received}") + val Right(opt) = received + assert(opt.isDefined == true, "because it expected Some, but received None") + val Some(eventTypes) = opt + eventTypes + } + + def getValidationStrategies(): Seq[EventValidationStrategy.Value] = extractFromRightOptional(() => client.getValidationStrategies()) + + def getEnrichmentStrategies(): Seq[EventEnrichmentStrategy.Value] = extractFromRightOptional(() => client.getEnrichmentStrategies()) + def getPartitionStrategies(): Seq[PartitionStrategy.Value] = extractFromRightOptional(() => client.getPartitioningStrategies()) + //Private methods private def wasItsuccessfull(in: Option[ClientError], msg: String): Boolean = in match { case Some(clientError) => - log.error("{} => ClientError: {}", generator.eventTypeId+"-"+msg, clientError) + log.error("{} => ClientError: {}", generator.eventTypeId + "-" + msg, clientError) false case None => true diff --git a/it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/events/MySimpleEvent.scala b/it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/events/MySimpleEvent.scala index 3bd5126..ec3e751 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/events/MySimpleEvent.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/events/MySimpleEvent.scala @@ -38,7 +38,8 @@ class SimpleEventListener extends Listener[MySimpleEvent] { def onReceive(sourceUrl: String, cursor: Cursor, events: Seq[MySimpleEvent]): Unit = { receivedEvents = receivedEvents ++ events - log.info(s"Received ${events.size.toLong}") + log.info(s"Received ${events.size}") + log.info(s"Total ${receivedEvents.size}") } def onSubscribed(endpoint: String, cursor: Option[Cursor]): Unit = { @@ -55,7 +56,6 @@ class SimpleEventListener extends Listener[MySimpleEvent] { log.info("############") receivedEvents } - } \ No newline at end of file diff --git a/it/src/test/scala/org/zalando/nakadi/client/scala/SimpleEventTest.scala b/it/src/test/scala/org/zalando/nakadi/client/scala/SimpleEventTest.scala index 37435d8..cfb184a 100644 --- a/it/src/test/scala/org/zalando/nakadi/client/scala/SimpleEventTest.scala +++ b/it/src/test/scala/org/zalando/nakadi/client/scala/SimpleEventTest.scala @@ -9,8 +9,10 @@ import org.zalando.nakadi.client.scala.test.factory.EventIntegrationHelper import org.zalando.nakadi.client.scala.test.factory.events.MySimpleEvent import org.zalando.nakadi.client.scala.test.factory.events.SimpleEventListener import org.zalando.nakadi.client.scala.model.PartitionStrategy +import org.scalatest.BeforeAndAfterAll +import org.zalando.nakadi.client.scala.model.EventValidationStrategy -class SimpleEventTest extends WordSpec with Matchers { +class SimpleEventTest extends WordSpec with Matchers with BeforeAndAfterAll { import org.scalatest.Matchers._ import ClientFactory._ @@ -21,13 +23,17 @@ class SimpleEventTest extends WordSpec with Matchers { val nrOfEvents = 45 val listener = new SimpleEventListener() + override def afterAll { + client.stop() + } + "404 should be handled graciously, by retuning None" in { val eventGenerator = new DefaultMySimpleEventGenerator() { def eventTypeId = s"SimpleEventIntegrationTest-Handle-404-Graciously" } val it = new EventIntegrationHelper(eventGenerator, client) it.createEventType() shouldBe true - + it.getEventType("non-existing-event-type-name") match { case Some(_) => fail("Should not fail, because eventType was not created yet!!") case None => @@ -46,12 +52,11 @@ class SimpleEventTest extends WordSpec with Matchers { val receivedEvents = listener.waitToReceive(nrOfEvents) receivedEvents.size shouldBe events.size receivedEvents shouldBe events - } "Validate created EventType" in { val eventGenerator = new DefaultMySimpleEventGenerator() { - def eventTypeId = s"SimpleEventIntegrationTest-Validate-Created-Events-$nrOfEvents" + def eventTypeId = s"SimpleEventIntegrationTest-Validate-Created-EventType" } val it = new EventIntegrationHelper(eventGenerator, client) @@ -74,43 +79,45 @@ class SimpleEventTest extends WordSpec with Matchers { eventType.partitionKeyFields shouldBe it.eventType.partitionKeyFields } - "Update existing EventType" in { - + "Validate nr of partitions after Creation of EventType" in { val eventGenerator = new DefaultMySimpleEventGenerator() { - def eventTypeId = s"SimpleEventIntegrationTest-Update-Existing-EventType" + def eventTypeId = s"SimpleEventIntegrationTest-Validate-nr-of-partitions" } val it = new EventIntegrationHelper(eventGenerator, client) it.createEventType() shouldBe true + it.getNumberOfPartitions() shouldBe 1 + } - //Generator with changes only in the schema - val schema = """{ "properties": { "order_number": { "type": "string" }, "id": { "type": "string" } } }""" - - val eventType2Update = new DefaultMySimpleEventGenerator() { - def eventTypeId = s"SimpleEventIntegrationTest-Update-Existing-EventType" -// override def schemaDefinition: String = schema - override def dataKeyFields = List("order_number") //Does not work -// override def partitionKeyFields = List("order_number") -// override def partitionStrategy = Some(PartitionStrategy.HASH) - }.eventType + "Receive partition-strategies successfully" ignore { + val eventGenerator = new DefaultMySimpleEventGenerator() { + def eventTypeId = s"SimpleEventIntegrationTest-Receive-partition-strategies-successfully" + } + val it = new EventIntegrationHelper(eventGenerator, client) + val result = it.getPartitionStrategies() + result.size shouldBe 3 //NOT IMPLEMENTED + result should contain (PartitionStrategy.HASH) + result should contain (PartitionStrategy.RANDOM) + result should contain (PartitionStrategy.USER_DEFINED) + } + "Receive validation-strategies successfully" ignore { + val eventGenerator = new DefaultMySimpleEventGenerator() { + def eventTypeId = s"SimpleEventIntegrationTest-Receive-validation-strategies-successfully" + } + val it = new EventIntegrationHelper(eventGenerator, client) + val result = it.getValidationStrategies() + result.size shouldBe 0 //NOT IMPLEMENTED + } - /* - it.updateEventType(eventType2Update) shouldBe true - val optionalOfCreatedEventType = it.getEventType() + "Receive enrichment-strategies successfully" ignore { + val eventGenerator = new DefaultMySimpleEventGenerator() { + def eventTypeId = s"SimpleEventIntegrationTest-Receive-enrichment-strategies-successfully" + } + val it = new EventIntegrationHelper(eventGenerator, client) + val result = it.getValidationStrategies() + result.size shouldBe 1 - optionalOfCreatedEventType.isDefined shouldBe true + result should contain(EventValidationStrategy.SCHEMA_VALIDATION) - val Some(eventType) = optionalOfCreatedEventType - eventType.category shouldBe eventType2Update.category - eventType.dataKeyFields shouldBe null //TODO this is not correct!! - eventType.name shouldBe it.eventType.name - eventType.owningApplication shouldBe eventType2Update.owningApplication - eventType.partitionStrategy shouldBe eventType2Update.partitionStrategy - eventType.schema shouldBe schema - eventType.statistics shouldBe eventType2Update.statistics - eventType.validationStrategies shouldBe null - eventType.enrichmentStrategies shouldBe eventType2Update.enrichmentStrategies - eventType.partitionKeyFields shouldBe eventType2Update.partitionKeyFields - */ } } From 320b0bad03fc491727474b4d31590a61c12ad44c Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Tue, 21 Jun 2016 10:36:00 +0200 Subject: [PATCH 094/183] techjira:LAAS-60 Implemented Integration tests for enums #65 --- .../java/test/factory/EventGenerator.java | 49 ++++ .../test/factory/EventGeneratorBuilder.java | 268 ++++++++++++++++++ .../test/factory/EventIntegrationHelper.java | 82 ++++++ .../test/factory/MySimpleEventGenerator.java | 22 ++ .../factory/events/SimpleEventListener.java | 57 ++++ .../zalando/client/java/SimpleEventTest.java | 127 +++++++++ .../client/scala/ClientIntegrationTest.scala | 51 ++++ 7 files changed, 656 insertions(+) create mode 100644 it/src/main/java/org/zalando/nakadi/client/java/test/factory/EventGenerator.java create mode 100644 it/src/main/java/org/zalando/nakadi/client/java/test/factory/EventGeneratorBuilder.java create mode 100644 it/src/main/java/org/zalando/nakadi/client/java/test/factory/EventIntegrationHelper.java create mode 100644 it/src/main/java/org/zalando/nakadi/client/java/test/factory/MySimpleEventGenerator.java create mode 100644 it/src/main/java/org/zalando/nakadi/client/java/test/factory/events/SimpleEventListener.java create mode 100644 it/src/test/java/org/zalando/client/java/SimpleEventTest.java create mode 100644 it/src/test/scala/org/zalando/nakadi/client/scala/ClientIntegrationTest.scala diff --git a/it/src/main/java/org/zalando/nakadi/client/java/test/factory/EventGenerator.java b/it/src/main/java/org/zalando/nakadi/client/java/test/factory/EventGenerator.java new file mode 100644 index 0000000..da3090e --- /dev/null +++ b/it/src/main/java/org/zalando/nakadi/client/java/test/factory/EventGenerator.java @@ -0,0 +1,49 @@ +package org.zalando.nakadi.client.java.test.factory; + +import java.util.List; + +import org.zalando.nakadi.client.java.enumerator.EventEnrichmentStrategy; +import org.zalando.nakadi.client.java.enumerator.EventTypeCategory; +import org.zalando.nakadi.client.java.enumerator.EventValidationStrategy; +import org.zalando.nakadi.client.java.enumerator.PartitionStrategy; +import org.zalando.nakadi.client.java.model.Event; +import org.zalando.nakadi.client.java.model.EventType; +import org.zalando.nakadi.client.java.model.EventTypeSchema; +import org.zalando.nakadi.client.java.model.EventTypeStatistics; + +/** + * Abstracts the necessary methods that can be generated for tests. + */ +public interface EventGenerator { + + String getEventTypeId(); + + String getNewId(); + + Event getNewEvent(); + + String getEventTypeName(); + + String getSchemaDefinition(); + + EventType getEventType(); + + String getOwner(); + + EventTypeCategory getCategory(); + + List getValidationStrategies(); + + List getEnrichmentStrategies(); + + PartitionStrategy getPartitionStrategy(); + + EventTypeSchema getSchemaType(); + + List getDataKeyFields(); + + List getPartitionKeyFields(); + + EventTypeStatistics getStatistics(); + +} diff --git a/it/src/main/java/org/zalando/nakadi/client/java/test/factory/EventGeneratorBuilder.java b/it/src/main/java/org/zalando/nakadi/client/java/test/factory/EventGeneratorBuilder.java new file mode 100644 index 0000000..b8bcb4b --- /dev/null +++ b/it/src/main/java/org/zalando/nakadi/client/java/test/factory/EventGeneratorBuilder.java @@ -0,0 +1,268 @@ +package org.zalando.nakadi.client.java.test.factory; + +import java.util.Collections; +import java.util.List; + +import org.apache.commons.lang.RandomStringUtils; +import org.zalando.nakadi.client.java.enumerator.EventEnrichmentStrategy; +import org.zalando.nakadi.client.java.enumerator.EventTypeCategory; +import org.zalando.nakadi.client.java.enumerator.EventValidationStrategy; +import org.zalando.nakadi.client.java.enumerator.PartitionStrategy; +import org.zalando.nakadi.client.java.enumerator.SchemaType; +import org.zalando.nakadi.client.java.model.Event; +import org.zalando.nakadi.client.java.model.EventType; +import org.zalando.nakadi.client.java.model.EventTypeSchema; +import org.zalando.nakadi.client.java.model.EventTypeStatistics; + + +public abstract class EventGeneratorBuilder { + private String eventTypeId; + private String newId= RandomStringUtils.randomAlphanumeric(12); + private Event newEvent; + private String eventTypeName; + private String schemaDefinition; + private EventType eventType = null;// TODO + private String owner = "Nakadi-klients(java-integration-test-suite)"; + private EventTypeCategory category = EventTypeCategory.UNDEFINED; + private List validationStrategies = Collections.emptyList(); + private List enrichmentStrategies = Collections.emptyList(); + private PartitionStrategy partitionStrategy = PartitionStrategy.RANDOM; + private EventTypeSchema schemaType;// TODO + private List dataKeyFields = Collections.emptyList(); + private List partitionKeyFields = Collections.emptyList(); + private EventTypeStatistics statistics = null; + + public EventGeneratorBuilder withEventTypeId(String eventTypeId) { + this.eventTypeId = eventTypeId; + return this; + } + + public EventGeneratorBuilder withNewId(String newId) { + this.newId = newId; + return this; + } + + public EventGeneratorBuilder withNewEvent(Event newEvent) { + this.newEvent = newEvent; + return this; + } + + public EventGeneratorBuilder withEventTypeName(String eventTypeName) { + this.eventTypeName = eventTypeName; + return this; + } + + public EventGeneratorBuilder withSchemaDefinition(String schemaDefinition) { + this.schemaDefinition = schemaDefinition; + return this; + } + + public EventGeneratorBuilder withEventType(EventType eventType) { + this.eventType = eventType; + return this; + } + + public EventGeneratorBuilder withOwner(String owner) { + this.owner = owner; + return this; + } + + public EventGeneratorBuilder withCategory(EventTypeCategory category) { + this.category = category; + return this; + } + + public EventGeneratorBuilder withValidationStrategies(List validationStrategies) { + this.validationStrategies = validationStrategies; + return this; + } + + public EventGeneratorBuilder withEnrichmentStrategies(List enrichmentStrategies) { + this.enrichmentStrategies = enrichmentStrategies; + return this; + } + + public EventGeneratorBuilder withPartitionStrategy(PartitionStrategy partitionStrategy) { + this.partitionStrategy = partitionStrategy; + return this; + } + + public EventGeneratorBuilder withSchemaType(EventTypeSchema schemaType) { + this.schemaType = schemaType; + return this; + } + + public EventGeneratorBuilder withDataKeyFields(List dataKeyFields) { + this.dataKeyFields = dataKeyFields; + return this; + } + + public EventGeneratorBuilder withPartitionKeyFields(List partitionKeyFields) { + this.partitionKeyFields = partitionKeyFields; + return this; + } + + public EventGeneratorBuilder withStatistics(EventTypeStatistics statistics) { + this.statistics = statistics; + return this; + } + + public EventGenerator build() { + EventGeneratorBuilder gen = this; + return new EventGenerator() { // Defaults + + @Override + public String getEventTypeId() { + return gen.getEventTypeId(); + } + + @Override + public String getNewId() { + return gen.getNewId(); + } + + @Override + public Event getNewEvent() { + return gen.getNewEvent(); + } + + @Override + public String getEventTypeName() { + return gen.getEventTypeName(); + } + + @Override + public String getSchemaDefinition() { + return gen.getSchemaDefinition(); + } + + @Override + public EventType getEventType() { + return new EventType(gen.getEventTypeName(), // + gen.getOwner(), // + gen.getCategory(),// + gen.getValidationStrategies(), // + gen.getEnrichmentStrategies(),// + gen.getPartitionStrategy(), // + gen.getSchemaType(), // + gen.getDataKeyFields(), // + gen.getPartitionKeyFields(), // + gen.getStatistics()); + } + + @Override + public String getOwner() { + return owner; + } + + @Override + public EventTypeCategory getCategory() { + return category; + } + + @Override + public List getValidationStrategies() { + return validationStrategies; + } + + @Override + public List getEnrichmentStrategies() { + return enrichmentStrategies; + } + + @Override + public PartitionStrategy getPartitionStrategy() { + return partitionStrategy; + } + + @Override + public EventTypeSchema getSchemaType() { + return schemaType; + } + + @Override + public List getDataKeyFields() { + return dataKeyFields; + } + + @Override + public List getPartitionKeyFields() { + return partitionKeyFields; + } + + @Override + public EventTypeStatistics getStatistics() { + return statistics; + } + + }; + + } + + /** + * Defaults or Lazy values should be defined under this line + */ + protected EventTypeSchema getSchemaType() { + return new EventTypeSchema(SchemaType.JSON, getSchemaDefinition()); + } + + protected String getEventTypeName() { + return getEventTypeId()+getNewId(); + } + + /** + * End of Defaults + */ + protected String getEventTypeId() { + return eventTypeId; + } + + protected String getNewId() { + return newId; + } + + protected Event getNewEvent() { + return newEvent; + } + + protected String getSchemaDefinition() { + return schemaDefinition; + } + + protected EventType getEventType() { + return eventType; + } + + protected String getOwner() { + return owner; + } + + protected EventTypeCategory getCategory() { + return category; + } + + protected List getValidationStrategies() { + return validationStrategies; + } + + protected List getEnrichmentStrategies() { + return enrichmentStrategies; + } + + protected PartitionStrategy getPartitionStrategy() { + return partitionStrategy; + } + + protected List getDataKeyFields() { + return dataKeyFields; + } + + protected List getPartitionKeyFields() { + return partitionKeyFields; + } + + protected EventTypeStatistics getStatistics() { + return statistics; + } + +} diff --git a/it/src/main/java/org/zalando/nakadi/client/java/test/factory/EventIntegrationHelper.java b/it/src/main/java/org/zalando/nakadi/client/java/test/factory/EventIntegrationHelper.java new file mode 100644 index 0000000..65ed8f8 --- /dev/null +++ b/it/src/main/java/org/zalando/nakadi/client/java/test/factory/EventIntegrationHelper.java @@ -0,0 +1,82 @@ +package org.zalando.nakadi.client.java.test.factory; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.concurrent.ExecutionException; +import java.util.stream.IntStream; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.zalando.nakadi.client.java.Client; +import org.zalando.nakadi.client.java.enumerator.PartitionStrategy; +import org.zalando.nakadi.client.java.model.Event; +import org.zalando.nakadi.client.java.model.EventType; +import org.zalando.nakadi.client.java.model.Partition; + +public class EventIntegrationHelper { + + private final EventGenerator gen; + private final Client client; + private final EventType eventType; + private Logger log = LoggerFactory.getLogger(this.getClass()); + + public EventIntegrationHelper(EventGenerator gen, Client client) { + this.gen = gen; + this.client = client; + this.eventType = gen.getEventType(); + } + + public Optional getEventType() { + try { + return client.getEventType(gen.getEventTypeName()).get(); + } catch (InterruptedException | ExecutionException e) { + log.error(gen.getEventTypeId() + " - createEventType :" + e.getMessage(), e); + return Optional.empty(); + } + } + + public boolean createEventType() { + try { + client.createEventType(eventType).get(); + return true; + } catch (RuntimeException | InterruptedException | ExecutionException e) { + logError(gen.getEventTypeId() + " - createEventType :" + e.getMessage(), e); + return false; + } + } + + @SuppressWarnings("static-access") + private void logError(String identifier, Throwable t) { + log.error("%s => %s ".format(identifier, t.getMessage())); + } + + public EventGenerator getGen() { + return gen; + } + + public Client getClient() { + return client; + } + + public Integer getNumberOfPartitions() throws InterruptedException, ExecutionException { + Optional> result = client.getPartitions(eventType.getName()).get(); + return result.get().size(); + } + public List getPartitionStrategies() throws InterruptedException, ExecutionException { + return client.getPartitioningStrategies().get().get(); + + } + + public List publishEvents(Integer nrOfEvents) { + List events = new ArrayList(); + IntStream.range(0, nrOfEvents).forEach(nr -> events.add(gen.getNewEvent())); + client.publishEvents(eventType.getName(), events); + log.info("#######"); + log.info("#######" + events.size()); + log.info("#######"); + return events; + + } + +} diff --git a/it/src/main/java/org/zalando/nakadi/client/java/test/factory/MySimpleEventGenerator.java b/it/src/main/java/org/zalando/nakadi/client/java/test/factory/MySimpleEventGenerator.java new file mode 100644 index 0000000..b462b39 --- /dev/null +++ b/it/src/main/java/org/zalando/nakadi/client/java/test/factory/MySimpleEventGenerator.java @@ -0,0 +1,22 @@ +package org.zalando.nakadi.client.java.test.factory; + +import org.zalando.nakadi.client.java.model.Event; +import org.zalando.nakadi.client.java.test.factory.events.MySimpleEvent; + +public class MySimpleEventGenerator extends EventGeneratorBuilder { + + protected Event getNewEvent() { + return new MySimpleEvent(getNewId()); + } + + @Override + protected String getEventTypeName() { + return getEventTypeId() + getNewId(); + } + + @Override + protected String getSchemaDefinition() { + return "{ 'properties': { 'order_number': { 'type': 'string' } } }".replaceAll("'", "\""); + } + +} diff --git a/it/src/main/java/org/zalando/nakadi/client/java/test/factory/events/SimpleEventListener.java b/it/src/main/java/org/zalando/nakadi/client/java/test/factory/events/SimpleEventListener.java new file mode 100644 index 0000000..a5b3b6e --- /dev/null +++ b/it/src/main/java/org/zalando/nakadi/client/java/test/factory/events/SimpleEventListener.java @@ -0,0 +1,57 @@ +package org.zalando.nakadi.client.java.test.factory.events; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.zalando.nakadi.client.java.ClientError; +import org.zalando.nakadi.client.java.Listener; +import org.zalando.nakadi.client.java.model.Cursor; + +public class SimpleEventListener implements Listener { + private List receivedEvents = new ArrayList(); + private Logger log = LoggerFactory.getLogger(this.getClass()); + + public SimpleEventListener(){ + + } + + @Override + public String getId() { + return "MySimpleEventListner"; + } + + @Override + public void onReceive(String endpoint, Cursor cursor, List events) { + receivedEvents.addAll(events); + log.info("Received: {}", events.size()); + log.info("Total: {}", receivedEvents.size()); + + } + + @Override + public void onSubscribed(String endpoint, Optional cursor) { + log.info("Subscribed to Endpoint {} - cursor {}", endpoint, cursor); + + } + + @Override + public void onError(String eventType, Optional error) { + log.error("Error {}", error); + + } + + public List waitToReceive(Integer nrOfEvents) throws InterruptedException { + while (nrOfEvents > receivedEvents.size()){ + log.info("Total: {}", receivedEvents.size()); + log.info("Number of events to wait "+nrOfEvents); + Thread.sleep(2000); + } + log.info("##############"); + log.info("Waited to receive {} events, actual size ={}", nrOfEvents, receivedEvents.size()); + log.info("##############"); + return receivedEvents; + } +} diff --git a/it/src/test/java/org/zalando/client/java/SimpleEventTest.java b/it/src/test/java/org/zalando/client/java/SimpleEventTest.java new file mode 100644 index 0000000..55ab8ff --- /dev/null +++ b/it/src/test/java/org/zalando/client/java/SimpleEventTest.java @@ -0,0 +1,127 @@ +package org.zalando.client.java; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.util.List; +import java.util.Optional; +import java.util.concurrent.ExecutionException; + +import org.junit.After; +import org.junit.Test; +import org.zalando.nakadi.client.java.Client; +import org.zalando.nakadi.client.java.StreamParameters; +import org.zalando.nakadi.client.java.enumerator.PartitionStrategy; +import org.zalando.nakadi.client.java.model.Cursor; +import org.zalando.nakadi.client.java.model.Event; +import org.zalando.nakadi.client.java.model.EventStreamBatch; +import org.zalando.nakadi.client.java.model.EventType; +import org.zalando.nakadi.client.java.test.factory.EventGenerator; +import org.zalando.nakadi.client.java.test.factory.EventGeneratorBuilder; +import org.zalando.nakadi.client.java.test.factory.EventIntegrationHelper; +import org.zalando.nakadi.client.java.test.factory.MySimpleEventGenerator; +import org.zalando.nakadi.client.java.test.factory.events.MySimpleEvent; +import org.zalando.nakadi.client.java.test.factory.events.SimpleEventListener; +import org.zalando.nakadi.client.scala.ClientFactory; + +import com.fasterxml.jackson.core.type.TypeReference; + +public class SimpleEventTest { + + private EventGeneratorBuilder builder = new MySimpleEventGenerator(); + private Client client = ClientFactory.getJavaClient();; + private Integer nrOfEvents = 45; + private SimpleEventListener listener = new SimpleEventListener(); + private TypeReference> typeRef = new TypeReference>() { + }; + + @After + public void shutdown() throws InterruptedException, ExecutionException { + client.stop(); + } + + @Test + public void handle404Graciously() throws InterruptedException, ExecutionException { + EventGenerator gen = builder// + .withEventTypeId("SimpleEventTest-handle404Graciously")// + .build(); + EventIntegrationHelper it = new EventIntegrationHelper(gen, client); + assertTrue(it.createEventType()); + assertTrue(it.getEventType().isPresent()); + Optional result = client.getEventType("none-existing-event-type-name").get(); + assertEquals(result.isPresent(), false); + } + + @Test + public void validatePublishedNrOfEvents() throws InterruptedException { + EventGenerator gen = builder.withEventTypeId("SimpleEventTest-validatePublishedNrOfEvents").build(); + EventIntegrationHelper it = new EventIntegrationHelper(gen, client); + assertTrue("EventType should be created", it.createEventType()); + Thread.sleep(1000);// Creation can take time. + Optional eventTypeOpt = it.getEventType(); + assertTrue("Did not return the eventType", eventTypeOpt.isPresent()); + List createdEvents = it.publishEvents(nrOfEvents); + Optional cursor = Optional.of(new Cursor("0", "BEGIN")); + Optional batchLimit = Optional.empty(); + Optional streamLimit = Optional.empty(); + Optional batchFlushTimeout = Optional.empty(); + Optional streamTimeout = Optional.empty(); + Optional streamKeepAliveLimit = Optional.empty(); + Optional flowId = Optional.empty(); + StreamParameters parameters = new StreamParameters(cursor, // + batchLimit, // + streamLimit, // + batchFlushTimeout, // + streamTimeout, // + streamKeepAliveLimit, // + flowId); + client.subscribe(it.getGen().getEventTypeName(), parameters, listener, typeRef); + List receivedEvents = listener.waitToReceive(nrOfEvents); + assertEquals("Created & Received events differ in number", createdEvents.size(), receivedEvents.size()); + } + + @Test + public void validateCreatedEventType() throws InterruptedException { + EventGenerator gen = builder.withEventTypeId("SimpleEventTest-validateCreatedEventType").build(); + EventIntegrationHelper it = new EventIntegrationHelper(gen, client); + assertTrue("EventType should be created", it.createEventType()); + Thread.sleep(1000);// Creation can take time. + Optional eventTypeOpt = it.getEventType(); + assertTrue("Did not return the eventType", eventTypeOpt.isPresent()); + + EventType originalEventType = it.getEventType().get(); + EventType eventType = eventTypeOpt.get(); + assertEquals(eventType.getCategory(), originalEventType.getCategory()); + assertEquals(eventType.getDataKeyFields(), originalEventType.getDataKeyFields()); + assertEquals(eventType.getName(), originalEventType.getName()); + assertEquals(eventType.getOwningApplication(), originalEventType.getOwningApplication()); + assertEquals(eventType.getPartitionKeyFields(), originalEventType.getPartitionKeyFields()); + assertEquals(eventType.getPartitionStrategy(), originalEventType.getPartitionStrategy()); + assertEquals(eventType.getSchema().getSchema(), originalEventType.getSchema().getSchema()); + assertEquals(eventType.getSchema().getType(), originalEventType.getSchema().getType()); + assertEquals(eventType.getStatistics(), originalEventType.getStatistics()); + assertEquals(eventType.getValidationStrategies(), originalEventType.getValidationStrategies()); + } + + @Test + public void validateNrOfPartition() throws InterruptedException, ExecutionException { + EventGenerator gen = builder.withEventTypeId("SimpleEventTest-validateNrOfPartition").build(); + EventIntegrationHelper it = new EventIntegrationHelper(gen, client); + assertTrue("EventType should be created", it.createEventType()); + assertEquals(it.getNumberOfPartitions(), Integer.valueOf(1)); + + } + + @Test + public void receivePartitionStrategies() throws InterruptedException, ExecutionException{ + EventGenerator gen = builder.withEventTypeId("SimpleEventTest-receivePartitionStrategies").build(); + EventIntegrationHelper it = new EventIntegrationHelper(gen, client); + List strategies = it.getPartitionStrategies(); + assertEquals(strategies.size(), 3); + for(PartitionStrategy ps:strategies){ + assertTrue("Did not find PartitionStrategy:"+ps.name(),PartitionStrategy.withName(ps.name()).isPresent()); + + } + } + +} diff --git a/it/src/test/scala/org/zalando/nakadi/client/scala/ClientIntegrationTest.scala b/it/src/test/scala/org/zalando/nakadi/client/scala/ClientIntegrationTest.scala new file mode 100644 index 0000000..ab3d206 --- /dev/null +++ b/it/src/test/scala/org/zalando/nakadi/client/scala/ClientIntegrationTest.scala @@ -0,0 +1,51 @@ +package org.zalando.nakadi.client.scala +import org.scalatest.Matchers +import org.scalatest.WordSpec +import org.zalando.nakadi.client.scala.model.Cursor +import org.zalando.nakadi.client.scala.model.JacksonJsonMarshaller +import org.zalando.nakadi.client.scala.model.PartitionStrategyType +import org.zalando.nakadi.client.scala.test.factory.EventIntegrationHelper +import org.zalando.nakadi.client.scala.test.factory.events.MySimpleEvent +import org.zalando.nakadi.client.scala.test.factory.events.SimpleEventListener +import org.zalando.nakadi.client.scala.model.PartitionStrategy +import org.scalatest.BeforeAndAfter +import scala.concurrent.Await +import scala.concurrent.duration.DurationInt +import org.scalatest.BeforeAndAfterAll + +class ClientIntegrationTest extends WordSpec with Matchers with BeforeAndAfterAll { + + import org.scalatest.Matchers._ + import ClientFactory._ + import JacksonJsonMarshaller._ + import MySimpleEvent._ + + val client = ClientFactory.getScalaClient() + val nrOfEvents = 45 + val listener = new SimpleEventListener() + + override def afterAll { + client.stop() + } + + "GET /metrics" in { + val result = Await.result(client.getMetrics(), 10.seconds) + result.isRight shouldBe true + val Right(metricsOpt) = result + metricsOpt.isDefined shouldBe true + val Some(metrics) = metricsOpt + println(" >>>> " + metrics) + //TODO Deserialization of Metrics is failing + // metrics.metrics.size > 0 + } + + "GET /event-types" in { + val result = Await.result(client.getEventTypes(), 10.seconds) + result.isRight shouldBe true + val Right(eventTypesOpt) = result + eventTypesOpt.isDefined shouldBe true + val Some(eventTypes) = eventTypesOpt + } + +} + From bc5fae2cca7fd1e6aa083a09b29b1ceb4718edc6 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Tue, 21 Jun 2016 10:50:28 +0200 Subject: [PATCH 095/183] techjira:LAAS-60 Improvements in the API --- .../java/enumerator/PartitionStrategy.java | 10 ++++---- .../zalando/nakadi/client/scala/Client.scala | 22 ----------------- .../scala/model/EmptyListToOptional.scala | 24 ------------------- .../nakadi/client/java/ClientImpl.java | 19 ++++++--------- .../client/java/JavaClientHandler.scala | 13 ++++++---- .../nakadi/client/scala/ClientImpl.scala | 9 +------ .../nakadi/client/scala/Connection.scala | 3 ++- .../model/ScalaJacksonJsonMarshaller.scala | 1 + 8 files changed, 25 insertions(+), 76 deletions(-) delete mode 100644 api/src/main/scala/org/zalando/nakadi/client/scala/model/EmptyListToOptional.scala diff --git a/api/src/main/java/org/zalando/nakadi/client/java/enumerator/PartitionStrategy.java b/api/src/main/java/org/zalando/nakadi/client/java/enumerator/PartitionStrategy.java index 2d87927..a9a946b 100644 --- a/api/src/main/java/org/zalando/nakadi/client/java/enumerator/PartitionStrategy.java +++ b/api/src/main/java/org/zalando/nakadi/client/java/enumerator/PartitionStrategy.java @@ -1,6 +1,6 @@ package org.zalando.nakadi.client.java.enumerator; -import scala.Option; +import java.util.Optional; import com.fasterxml.jackson.annotation.JsonValue; @@ -26,11 +26,11 @@ public String getStrategy() { return strategy; } - public static Option withName(String code){ + public static Optional withName(String code){ for(PartitionStrategy e : PartitionStrategy.values()){ - if (e.strategy.equals(code)) - return Option.apply(e); + if (e.name().equals(code)) + return Optional.of(e); } - return Option.empty(); + return Optional.empty(); } } diff --git a/api/src/main/scala/org/zalando/nakadi/client/scala/Client.scala b/api/src/main/scala/org/zalando/nakadi/client/scala/Client.scala index ca83efc..04bcc7d 100644 --- a/api/src/main/scala/org/zalando/nakadi/client/scala/Client.scala +++ b/api/src/main/scala/org/zalando/nakadi/client/scala/Client.scala @@ -89,28 +89,6 @@ trait Client { */ def publishEvents[T <: Event](eventTypeName: String, events: Seq[T]): Future[Option[ClientError]] - /** - * Publishes a single Events for the given EventType. - * {{{ - * curl --request POST -d @fileWithEvent /event-types/{name}/events - * }}} - * @param eventTypeName - Name of the EventType - * @param event - Event to publish - * - */ - def publishEvent[T <: Event](eventTypeName: String, event: T, ser: Serializer[T]): Future[Option[ClientError]] - - /** - * Publishes a single Events to the given EventType. - * {{{ - * curl --request POST -d @fileWithEvent /event-types/{name}/events - * }}} - * @param eventTypeName - Name of the EventType - * @param event - Event to publish - * - */ - def publishEvent[T <: Event](eventTypeName: String, event: T): Future[Option[ClientError]] - /** * List the partitions tola the given EventType. * {{{ diff --git a/api/src/main/scala/org/zalando/nakadi/client/scala/model/EmptyListToOptional.scala b/api/src/main/scala/org/zalando/nakadi/client/scala/model/EmptyListToOptional.scala deleted file mode 100644 index f64dad3..0000000 --- a/api/src/main/scala/org/zalando/nakadi/client/scala/model/EmptyListToOptional.scala +++ /dev/null @@ -1,24 +0,0 @@ -package org.zalando.nakadi.client.scala.model - -import com.fasterxml.jackson.databind.JsonDeserializer -import com.fasterxml.jackson.core.JsonParser -import com.fasterxml.jackson.databind.DeserializationContext - -class EmptyListToOptional extends JsonDeserializer[Option[Seq[AnyRef]]] { - - def deserialize(p: JsonParser, context: DeserializationContext) = { - - p.getValueAsString match { - case v => - println("#######") - println("#######") - println("#######") - println(s"Value $v") - println("#######") - println("#######") - println("#######") - None - } - - } -} \ No newline at end of file diff --git a/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java b/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java index 206eb82..3035db6 100644 --- a/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java +++ b/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java @@ -30,10 +30,8 @@ public class ClientImpl implements Client { // List Deserializers private final Deserializer> seqOfEventTypeDeserializer = SerializationUtils.seqOfEventTypeDeserializer(); private final Deserializer> seqOfPartitionDeserializer = SerializationUtils.seqOfPartitionDeserializer(); - private final Deserializer> seqOfEventValidationStrategy = SerializationUtils - .seqOfEventValidationStrategy(); - private final Deserializer> seqOfEventEnrichmentStrategy = SerializationUtils - .seqOfEventEnrichmentStrategy(); + private final Deserializer> seqOfEventValidationStrategy = SerializationUtils.seqOfEventValidationStrategy(); + private final Deserializer> seqOfEventEnrichmentStrategy = SerializationUtils.seqOfEventEnrichmentStrategy(); private final Deserializer> seqOfPartitionStrategy = SerializationUtils.seqOfPartitionStrategy(); // Serializers private final Serializer eventTypeSerializer = SerializationUtils.defaultSerializer(); @@ -45,13 +43,11 @@ public ClientImpl(JavaClientHandler handler) { @Override public Future> getMetrics() { - return handler.get(Uri.URI_METRICS(), metricsDeserializer); } @Override public Future>> getEventTypes() { - return handler.get(Uri.URI_EVENT_TYPES(), seqOfEventTypeDeserializer); } @@ -117,19 +113,18 @@ public Future>> getPartitioningStrategies() { } @Override - public Future stop() { - throw new NotImplementedException(); + public void stop() { + handler.stop(); } @Override public Optional subscribe(String eventTypeName, StreamParameters parameters, Listener listener, Deserializer> deserializer) { - return handler.subscribe(eventTypeName,Uri.getEventStreamingUri(eventTypeName), parameters, listener, deserializer); + return handler.subscribe(eventTypeName, Uri.getEventStreamingUri(eventTypeName), parameters, listener, deserializer); } @Override - public Optional subscribe(String eventTypeName, StreamParameters parameters, Listener listener, - TypeReference> typeRef) { - return handler.subscribe(eventTypeName,Uri.getEventStreamingUri(eventTypeName), parameters, listener, SerializationUtils.withCustomDeserializer(typeRef)); + public Optional subscribe(String eventTypeName, StreamParameters parameters, Listener listener, TypeReference> typeRef) { + return handler.subscribe(eventTypeName, Uri.getEventStreamingUri(eventTypeName), parameters, listener, SerializationUtils.withCustomDeserializer(typeRef)); } @Override diff --git a/client/src/main/scala/org/zalando/nakadi/client/java/JavaClientHandler.scala b/client/src/main/scala/org/zalando/nakadi/client/java/JavaClientHandler.scala index 1903bf8..21d7868 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/java/JavaClientHandler.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/java/JavaClientHandler.scala @@ -38,6 +38,8 @@ import org.zalando.nakadi.client.utils.GeneralConversions import akka.http.scaladsl.model.StatusCodes import org.zalando.nakadi.client.scala.ScalaEventHandlerImpl import org.zalando.nakadi.client.scala.JavaEventHandlerImpl +import scala.concurrent.Await +import scala.concurrent.duration.Duration /** * Handler for mapping(Java<->Scala) and handling http calls and listener subscriptions for the Java API. @@ -49,6 +51,7 @@ trait JavaClientHandler { def post[T](endpoint: String, model: T)(implicit serializer: Serializer[T]): java.util.concurrent.Future[Void] def subscribe[T <: JEvent](eventTypeName: String, endpoint: String, parameters: JStreamParameters, listener: JListener[T])(implicit des: Deserializer[JEventStreamBatch[T]]): Optional[ClientError] def unsubscribe[T <: JEvent](eventTypeName: String, partition: Optional[String], listener: JListener[T]) + def stop(): Unit } @@ -58,11 +61,8 @@ class JavaClientHandlerImpl(val connection: Connection, subscriber: Subscription import GeneralConversions._ private implicit val mat = connection.materializer() - //TODO: Use constructor later make the tests simpler - def deserialize[T](response: HttpResponse, des: Deserializer[T]): Future[Optional[T]] = response match { case HttpResponse(status, headers, entity, protocol) if (status.isSuccess()) => - Try(Unmarshal(entity).to[String].map(body => des.from(body))) match { case Success(result) => result.map(Optional.of(_)) case Failure(error) => throw new RuntimeException(error.getMessage) @@ -71,7 +71,6 @@ class JavaClientHandlerImpl(val connection: Connection, subscriber: Subscription Future.successful(Optional.empty()) case HttpResponse(status, headers, entity, protocol) if (status.isFailure()) => throw new RuntimeException(status.reason()) - } def get[T](endpoint: String, des: Deserializer[T]): java.util.concurrent.Future[Optional[T]] = { @@ -123,6 +122,12 @@ class JavaClientHandlerImpl(val connection: Connection, subscriber: Subscription def unsubscribe[T <: JEvent](eventTypeName: String, partition: Optional[String], listener: JListener[T]) = { subscriber.unsubscribe(eventTypeName, toOption(partition), listener.getId) } + + def stop(): Unit = { + mat.shutdown() + Await.ready(connection.actorSystem().terminate(), Duration.Inf) + } + private def getCursor(params: Option[ScalaStreamParameters]): Option[ScalaCursor] = params match { case Some(ScalaStreamParameters(cursor, batchLimit, streamLimit, batchFlushTimeout, streamTimeout, streamKeepAliveLimit, flowId)) => cursor case None => None diff --git a/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala index ba69076..7bb5f66 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala @@ -70,14 +70,6 @@ class ClientImpl(connection: Connection, subscriber: SubscriptionHandler, charSe logFutureOption(connection.post(URI_EVENTS_OF_EVENT_TYPE.format(eventTypeName), events).flatMap(in => mapToOption(in))) } - def publishEvent[T <: Event](name: String, event: T, ser: Serializer[T]): Future[Option[ClientError]] = { - logFutureOption(connection.post(URI_EVENTS_OF_EVENT_TYPE.format(name), event).flatMap(in => mapToOption(in))) - } - - def publishEvent[T <: Event](name: String, event: T): Future[Option[ClientError]] = { - logFutureOption(connection.post(URI_EVENTS_OF_EVENT_TYPE.format(name), event).flatMap(in => mapToOption(in))) - } - def getPartitions(name: String): Future[Either[ClientError, Option[Seq[Partition]]]] = { logFutureEither(connection.get(URI_PARTITIONS_BY_EVENT_TYPE.format(name)).flatMap(in => mapToEither(in)(deserializer(listOfPartitionTR)))) } @@ -94,6 +86,7 @@ class ClientImpl(connection: Connection, subscriber: SubscriptionHandler, charSe logFutureEither(connection.get(URI_PARTITIONING_STRATEGIES).flatMap(mapToEither(_)(deserializer(listOfPartitionStrategyTR)))) def stop(): Option[ClientError] = { + materializer.shutdown() val result = Await.ready(connection.actorSystem().terminate(), Duration.Inf) None } diff --git a/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala index 1af65a7..bb9d1fc 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala @@ -100,7 +100,8 @@ sealed class ConnectionImpl(val host: String, val port: Int, val tokenProvider: type StepResult[T] = (T, Option[String]) implicit val actorSystem = ActorSystem("Nakadi-Client-Connections") - def materializer(decider: Supervision.Decider): ActorMaterializer = { + + def materializer(decider: Supervision.Decider): ActorMaterializer = { ActorMaterializer( ActorMaterializerSettings(actorSystem) .withSupervisionStrategy(decider)) } diff --git a/client/src/main/scala/org/zalando/nakadi/client/scala/model/ScalaJacksonJsonMarshaller.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/model/ScalaJacksonJsonMarshaller.scala index 9cfd58f..d49ed94 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/scala/model/ScalaJacksonJsonMarshaller.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/scala/model/ScalaJacksonJsonMarshaller.scala @@ -80,6 +80,7 @@ object JacksonJsonMarshaller { .registerModule(new DefaultScalaModule) .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) .configure(DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT, true) + .enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY) .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) .setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE) .setSerializationInclusion(JsonInclude.Include.NON_EMPTY) From f3c5ba75d1488ae6124f22e754c82896241280a1 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Tue, 21 Jun 2016 10:53:00 +0200 Subject: [PATCH 096/183] techjira:LAAS-60 Improvements in the API --- .../client/utils/FutureConversions.scala | 31 ------------------- 1 file changed, 31 deletions(-) diff --git a/client/src/main/scala/org/zalando/nakadi/client/utils/FutureConversions.scala b/client/src/main/scala/org/zalando/nakadi/client/utils/FutureConversions.scala index 7292e29..06a7986 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/utils/FutureConversions.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/utils/FutureConversions.scala @@ -13,10 +13,6 @@ object FutureConversions { case Left(error) => throw new RuntimeException(error) case Right(t) => t } - // private def extractOption[T >: Null](option: Option[ClientError]): T = option match { - // case Some(v) => throw new RuntimeException(v.msg) - // case None => null - // } def fromOption2Optional[T](in: scala.concurrent.Future[Option[T]]): java.util.concurrent.Future[Optional[T]] = { new MFuture[Option[T], Optional[T]](in, a => fromOptional2Optional(a)) @@ -31,22 +27,6 @@ object FutureConversions { new MFuture[T, Void](in, a => null) } - // /** - // * Transforms the sequence in the option wrapped in the either right inside a - // * future into a java List wrapped in an Optional wrapped in a Java future. - // * If either left inside the future contains a client error, - // * then a RuntimeException is thrown with the error! - // */ - // def fromSeqOfOptionalEither2OptionalList[T](in: scala.concurrent.Future[Either[ClientError, Option[Seq[T]]]]): java.util.concurrent.Future[Optional[java.util.List[T]]] = { - // new MFuture[Either[ClientError, Option[Seq[T]]], Optional[java.util.List[T]]](in, a => fromSeqOfOptionalEither2OptionalList(a)) - // } - // - // /** - // * Transforms an optional into a Void if it is empty, else RuntimeException is thrown with the error! - // */ - // def fromOptional2Future(in: scala.concurrent.Future[Option[ClientError]]): java.util.concurrent.Future[Void] = { - // new MFuture[Option[ClientError], Void](in, a => extractOption(a)) - // } private def fromSequenceToList[T](in: Seq[T]): Optional[java.util.List[T]] = in match { case Nil => Optional.empty() @@ -54,17 +34,6 @@ object FutureConversions { } - // private def fromRightOptionOfEither2Option[R](in: Either[ClientError, Option[R]]): Optional[R] = in match { - // case Left(e) => throw new RuntimeException(e.msg) - // case Right(Some(value)) => Optional.of(value) - // case Right(None) => Optional.empty() - // } - // private def fromSeqOfOptionalEither2OptionalList[R](in: Either[ClientError, Option[Seq[R]]]): Optional[java.util.List[R]] = in match { - // case Left(e) => throw new RuntimeException(e.msg) - // case Right(None) => Optional.empty() - // case Right(Some(Nil)) => Optional.of(new java.util.ArrayList[R]()) - // case Right(Some(seq)) => Optional.of(new java.util.ArrayList[R](seq)) - // } private def fromOptional2Optional[R](in: Option[R]): Optional[R] = in match { case Some(value) => Optional.of(value) case None => Optional.empty() From 9b4842bb67744ceaf816b43abe9b5ecc65ee913c Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Tue, 21 Jun 2016 13:07:00 +0200 Subject: [PATCH 097/183] techjira:LAAS-60 Changing the creation of the unique id of the Actor. fixes #68 --- .../client/actor/SupervisingActor.scala | 12 +++--- .../java/test/factory/EventGenerator.java | 2 +- .../test/factory/EventGeneratorBuilder.java | 22 +++------- .../test/factory/MySimpleEventGenerator.java | 5 ++- .../test/factory/events/MySimpleEvent.scala | 9 ++-- .../zalando/client/java/SimpleEventTest.java | 31 +++++++------ .../nakadi/client/scala/SimpleEventTest.scala | 43 ++++++++++++++++--- 7 files changed, 79 insertions(+), 45 deletions(-) diff --git a/client/src/main/scala/org/zalando/nakadi/client/actor/SupervisingActor.scala b/client/src/main/scala/org/zalando/nakadi/client/actor/SupervisingActor.scala index b8ddcf8..50060e2 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/actor/SupervisingActor.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/actor/SupervisingActor.scala @@ -27,7 +27,7 @@ import akka.util.ByteString object SupervisingActor { case class SubscribeMsg(eventTypeName: String, endpoint: String, cursor: Option[Cursor], handler: EventHandler) { override def toString(): String = - s"SubscriptionKey(eventTypeName: $eventTypeName - endpoint: $endpoint - cursor: $cursor - ${handler.id})" + s"SubscriptionKey(eventTypeName: $eventTypeName - endpoint: $endpoint - cursor: $cursor - listener: ${handler.id})" } case class UnsubscribeMsg(eventTypeName: String, partition: Option[String], eventHandlerId: String) case class OffsetMsg(cursor: Cursor, subKey: SubscriptionKey) @@ -44,7 +44,7 @@ class SupervisingActor(val connection: Connection, val subscriptionHandler: Subs import SupervisingActor._ import ConsumingActor._ val subscriptions: SubscriptionHolder = new SubscriptionHolderImpl() - + var subscriptionCounter = 0; override val supervisorStrategy: SupervisorStrategy = { def defaultDecider: Decider = { case _: ActorInitializationException ⇒ Stop @@ -64,8 +64,9 @@ class SupervisingActor(val connection: Connection, val subscriptionHandler: Subs log.info("New subscription {}", subscrition) subscribe(subscrition) case unsubscription: UnsubscribeMsg => - log.info("Number of subscriptions {}", subscriptions.activeSize) + log.info("Number of subscriptions before unsubscribing {}", subscriptions.activeSize) unsubscribe(unsubscription) + log.info("Number of subscriptions after unsubscribing {}", subscriptions.activeSize) case Terminated(terminatedActor) => log.info(s"ConsumingActor terminated {}", terminatedActor.path.name) subscriptions.entryByActor(terminatedActor) match { @@ -89,8 +90,9 @@ class SupervisingActor(val connection: Connection, val subscriptionHandler: Subs } def subscribe(subscribe: SubscribeMsg) = { + subscriptionCounter+=1 val SubscribeMsg(eventTypeName, endpoint, optCursor, eventHandler) = subscribe - log.info("Subscription nr {} - cursor {} - eventType {} - listener {}", subscriptions.activeSize , optCursor, eventTypeName, eventHandler.id()) + log.info("Subscription nr {} - cursor {} - eventType {} - listener {}", subscriptionCounter , optCursor, eventTypeName, eventHandler.id()) val subKey: SubscriptionKey = optCursor match { case Some(Cursor(partition, _)) => SubscriptionKey(eventTypeName, Some(partition)) @@ -98,7 +100,7 @@ class SupervisingActor(val connection: Connection, val subscriptionHandler: Subs } //Create the Consumer - val consumingActor = context.actorOf(Props(classOf[ConsumingActor], subKey, eventHandler), "EventConsumingActor-" + subscriptions.activeSize) + val consumingActor = context.actorOf(Props(classOf[ConsumingActor], subKey, eventHandler), "EventConsumingActor-" + subscriptionCounter) context.watch(consumingActor) //If the streaming is ending!! diff --git a/it/src/main/java/org/zalando/nakadi/client/java/test/factory/EventGenerator.java b/it/src/main/java/org/zalando/nakadi/client/java/test/factory/EventGenerator.java index da3090e..67f0af9 100644 --- a/it/src/main/java/org/zalando/nakadi/client/java/test/factory/EventGenerator.java +++ b/it/src/main/java/org/zalando/nakadi/client/java/test/factory/EventGenerator.java @@ -12,7 +12,7 @@ import org.zalando.nakadi.client.java.model.EventTypeStatistics; /** - * Abstracts the necessary methods that can be generated for tests. + * Abstracts the necessary values that can be generated for tests. */ public interface EventGenerator { diff --git a/it/src/main/java/org/zalando/nakadi/client/java/test/factory/EventGeneratorBuilder.java b/it/src/main/java/org/zalando/nakadi/client/java/test/factory/EventGeneratorBuilder.java index b8bcb4b..de0fe7e 100644 --- a/it/src/main/java/org/zalando/nakadi/client/java/test/factory/EventGeneratorBuilder.java +++ b/it/src/main/java/org/zalando/nakadi/client/java/test/factory/EventGeneratorBuilder.java @@ -3,7 +3,6 @@ import java.util.Collections; import java.util.List; -import org.apache.commons.lang.RandomStringUtils; import org.zalando.nakadi.client.java.enumerator.EventEnrichmentStrategy; import org.zalando.nakadi.client.java.enumerator.EventTypeCategory; import org.zalando.nakadi.client.java.enumerator.EventValidationStrategy; @@ -14,20 +13,18 @@ import org.zalando.nakadi.client.java.model.EventTypeSchema; import org.zalando.nakadi.client.java.model.EventTypeStatistics; - public abstract class EventGeneratorBuilder { private String eventTypeId; - private String newId= RandomStringUtils.randomAlphanumeric(12); + private Integer newId = new Integer(0); private Event newEvent; - private String eventTypeName; private String schemaDefinition; - private EventType eventType = null;// TODO + private EventType eventType = null; private String owner = "Nakadi-klients(java-integration-test-suite)"; private EventTypeCategory category = EventTypeCategory.UNDEFINED; private List validationStrategies = Collections.emptyList(); private List enrichmentStrategies = Collections.emptyList(); private PartitionStrategy partitionStrategy = PartitionStrategy.RANDOM; - private EventTypeSchema schemaType;// TODO + private EventTypeSchema schemaType; private List dataKeyFields = Collections.emptyList(); private List partitionKeyFields = Collections.emptyList(); private EventTypeStatistics statistics = null; @@ -37,7 +34,7 @@ public EventGeneratorBuilder withEventTypeId(String eventTypeId) { return this; } - public EventGeneratorBuilder withNewId(String newId) { + public EventGeneratorBuilder withNewId(Integer newId) { this.newId = newId; return this; } @@ -47,11 +44,6 @@ public EventGeneratorBuilder withNewEvent(Event newEvent) { return this; } - public EventGeneratorBuilder withEventTypeName(String eventTypeName) { - this.eventTypeName = eventTypeName; - return this; - } - public EventGeneratorBuilder withSchemaDefinition(String schemaDefinition) { this.schemaDefinition = schemaDefinition; return this; @@ -118,7 +110,7 @@ public String getEventTypeId() { @Override public String getNewId() { - return gen.getNewId(); + return gen.getNewId().toString(); } @Override @@ -207,7 +199,7 @@ protected EventTypeSchema getSchemaType() { } protected String getEventTypeName() { - return getEventTypeId()+getNewId(); + return getEventTypeId() + getNewId(); } /** @@ -218,7 +210,7 @@ protected String getEventTypeId() { } protected String getNewId() { - return newId; + return newId.toString(); } protected Event getNewEvent() { diff --git a/it/src/main/java/org/zalando/nakadi/client/java/test/factory/MySimpleEventGenerator.java b/it/src/main/java/org/zalando/nakadi/client/java/test/factory/MySimpleEventGenerator.java index b462b39..c7e005b 100644 --- a/it/src/main/java/org/zalando/nakadi/client/java/test/factory/MySimpleEventGenerator.java +++ b/it/src/main/java/org/zalando/nakadi/client/java/test/factory/MySimpleEventGenerator.java @@ -1,17 +1,18 @@ package org.zalando.nakadi.client.java.test.factory; +import org.apache.commons.lang.RandomStringUtils; import org.zalando.nakadi.client.java.model.Event; import org.zalando.nakadi.client.java.test.factory.events.MySimpleEvent; public class MySimpleEventGenerator extends EventGeneratorBuilder { - + private final String id= RandomStringUtils.randomAlphanumeric(12); protected Event getNewEvent() { return new MySimpleEvent(getNewId()); } @Override protected String getEventTypeName() { - return getEventTypeId() + getNewId(); + return getEventTypeId() +id; } @Override diff --git a/it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/events/MySimpleEvent.scala b/it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/events/MySimpleEvent.scala index ec3e751..e9bed5d 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/events/MySimpleEvent.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/events/MySimpleEvent.scala @@ -13,12 +13,15 @@ import org.slf4j.Logger object MySimpleEvent { trait DefaultMySimpleEventGenerator extends EventGenerator { - - def newId: String = Random.alphanumeric.take(12).mkString + private var counter = 0; + def newId: String = { + counter += 1 + counter.toString() + } def newEvent(): Event = new MySimpleEvent(newId) - lazy val eventTypeName: String = eventTypeId + newId + lazy val eventTypeName: String = eventTypeId + Random.alphanumeric.take(12).mkString def schemaDefinition: String = """{ "properties": { "order_number": { "type": "string" } } }""" } diff --git a/it/src/test/java/org/zalando/client/java/SimpleEventTest.java b/it/src/test/java/org/zalando/client/java/SimpleEventTest.java index 55ab8ff..ebcbfbc 100644 --- a/it/src/test/java/org/zalando/client/java/SimpleEventTest.java +++ b/it/src/test/java/org/zalando/client/java/SimpleEventTest.java @@ -28,10 +28,9 @@ public class SimpleEventTest { - private EventGeneratorBuilder builder = new MySimpleEventGenerator(); private Client client = ClientFactory.getJavaClient();; private Integer nrOfEvents = 45; - private SimpleEventListener listener = new SimpleEventListener(); + private TypeReference> typeRef = new TypeReference>() { }; @@ -42,7 +41,7 @@ public void shutdown() throws InterruptedException, ExecutionException { @Test public void handle404Graciously() throws InterruptedException, ExecutionException { - EventGenerator gen = builder// + EventGenerator gen = new MySimpleEventGenerator()// .withEventTypeId("SimpleEventTest-handle404Graciously")// .build(); EventIntegrationHelper it = new EventIntegrationHelper(gen, client); @@ -54,7 +53,9 @@ public void handle404Graciously() throws InterruptedException, ExecutionExceptio @Test public void validatePublishedNrOfEvents() throws InterruptedException { - EventGenerator gen = builder.withEventTypeId("SimpleEventTest-validatePublishedNrOfEvents").build(); + SimpleEventListener listener = new SimpleEventListener(); + EventGenerator gen = new MySimpleEventGenerator()// + .withEventTypeId("SimpleEventTest-validatePublishedNrOfEvents").build(); EventIntegrationHelper it = new EventIntegrationHelper(gen, client); assertTrue("EventType should be created", it.createEventType()); Thread.sleep(1000);// Creation can take time. @@ -80,9 +81,11 @@ public void validatePublishedNrOfEvents() throws InterruptedException { assertEquals("Created & Received events differ in number", createdEvents.size(), receivedEvents.size()); } + @Test public void validateCreatedEventType() throws InterruptedException { - EventGenerator gen = builder.withEventTypeId("SimpleEventTest-validateCreatedEventType").build(); + EventGenerator gen = new MySimpleEventGenerator()// + .withEventTypeId("SimpleEventTest-validateCreatedEventType").build(); EventIntegrationHelper it = new EventIntegrationHelper(gen, client); assertTrue("EventType should be created", it.createEventType()); Thread.sleep(1000);// Creation can take time. @@ -105,22 +108,24 @@ public void validateCreatedEventType() throws InterruptedException { @Test public void validateNrOfPartition() throws InterruptedException, ExecutionException { - EventGenerator gen = builder.withEventTypeId("SimpleEventTest-validateNrOfPartition").build(); + EventGenerator gen = new MySimpleEventGenerator()// + .withEventTypeId("SimpleEventTest-validateNrOfPartition").build(); EventIntegrationHelper it = new EventIntegrationHelper(gen, client); assertTrue("EventType should be created", it.createEventType()); assertEquals(it.getNumberOfPartitions(), Integer.valueOf(1)); } - + @Test - public void receivePartitionStrategies() throws InterruptedException, ExecutionException{ - EventGenerator gen = builder.withEventTypeId("SimpleEventTest-receivePartitionStrategies").build(); - EventIntegrationHelper it = new EventIntegrationHelper(gen, client); + public void receivePartitionStrategies() throws InterruptedException, ExecutionException { + EventGenerator gen = new MySimpleEventGenerator()// + .withEventTypeId("SimpleEventTest-receivePartitionStrategies").build(); + EventIntegrationHelper it = new EventIntegrationHelper(gen, client); List strategies = it.getPartitionStrategies(); assertEquals(strategies.size(), 3); - for(PartitionStrategy ps:strategies){ - assertTrue("Did not find PartitionStrategy:"+ps.name(),PartitionStrategy.withName(ps.name()).isPresent()); - + for (PartitionStrategy ps : strategies) { + assertTrue("Did not find PartitionStrategy:" + ps.name(), PartitionStrategy.withName(ps.name()).isPresent()); + } } diff --git a/it/src/test/scala/org/zalando/nakadi/client/scala/SimpleEventTest.scala b/it/src/test/scala/org/zalando/nakadi/client/scala/SimpleEventTest.scala index cfb184a..6c990bd 100644 --- a/it/src/test/scala/org/zalando/nakadi/client/scala/SimpleEventTest.scala +++ b/it/src/test/scala/org/zalando/nakadi/client/scala/SimpleEventTest.scala @@ -21,13 +21,12 @@ class SimpleEventTest extends WordSpec with Matchers with BeforeAndAfterAll { val client = ClientFactory.getScalaClient() val nrOfEvents = 45 - val listener = new SimpleEventListener() override def afterAll { - client.stop() + // client.stop() } - "404 should be handled graciously, by retuning None" in { + "404_should be handled graciously" in { val eventGenerator = new DefaultMySimpleEventGenerator() { def eventTypeId = s"SimpleEventIntegrationTest-Handle-404-Graciously" } @@ -46,6 +45,8 @@ class SimpleEventTest extends WordSpec with Matchers with BeforeAndAfterAll { } val it = new EventIntegrationHelper(eventGenerator, client) val cursor = Some(Cursor("0", "BEGIN")) + val listener = new SimpleEventListener() + it.createEventType() shouldBe true val events = it.publishEvents(nrOfEvents) client.subscribe(eventGenerator.eventTypeName, StreamParameters(cursor = cursor), listener) @@ -54,6 +55,36 @@ class SimpleEventTest extends WordSpec with Matchers with BeforeAndAfterAll { receivedEvents shouldBe events } + "Multiple events listeners must work in parallel" in { + val eventGenerator1 = new DefaultMySimpleEventGenerator() { + def eventTypeId = s"SimpleEventIntegrationTest-Multiple-listeners-in-parallel-$nrOfEvents" + } + val it = new EventIntegrationHelper(eventGenerator1, client) + val cursor = Some(Cursor("0", "BEGIN")) + it.createEventType() shouldBe true + val events = it.publishEvents(nrOfEvents) + val listener = new SimpleEventListener() + val listener2 = new SimpleEventListener() + val listener3 = new SimpleEventListener() + + client.subscribe(eventGenerator1.eventTypeName, StreamParameters(cursor = cursor), listener) + client.subscribe(eventGenerator1.eventTypeName, StreamParameters(cursor = cursor), listener2) + + val receivedEvents = listener.waitToReceive(nrOfEvents) + receivedEvents.size shouldBe events.size + receivedEvents shouldBe events + + val receivedEvents2 = listener2.waitToReceive(nrOfEvents) + receivedEvents2.size shouldBe events.size + receivedEvents2 shouldBe events + + client.subscribe(eventGenerator1.eventTypeName, StreamParameters(cursor = cursor), listener) + val receivedEvents3 = listener.waitToReceive(nrOfEvents * 2) + receivedEvents3.size shouldBe (nrOfEvents * 2) + + } + + "Validate created EventType" in { val eventGenerator = new DefaultMySimpleEventGenerator() { def eventTypeId = s"SimpleEventIntegrationTest-Validate-Created-EventType" @@ -95,9 +126,9 @@ class SimpleEventTest extends WordSpec with Matchers with BeforeAndAfterAll { val it = new EventIntegrationHelper(eventGenerator, client) val result = it.getPartitionStrategies() result.size shouldBe 3 //NOT IMPLEMENTED - result should contain (PartitionStrategy.HASH) - result should contain (PartitionStrategy.RANDOM) - result should contain (PartitionStrategy.USER_DEFINED) + result should contain(PartitionStrategy.HASH) + result should contain(PartitionStrategy.RANDOM) + result should contain(PartitionStrategy.USER_DEFINED) } "Receive validation-strategies successfully" ignore { val eventGenerator = new DefaultMySimpleEventGenerator() { From a03766fb84de57c5c46b33f1daf2fc8a59eb7e83 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Tue, 21 Jun 2016 15:37:23 +0200 Subject: [PATCH 098/183] techjira:LAAS-60 Fixing a bug in the unsubscription logic. Fixes #27 --- .../client/actor/SubscriptionHolder.scala | 14 +++----- .../client/actor/SupervisingActor.scala | 8 +++-- .../test/factory/EventIntegrationHelper.java | 4 +-- .../factory/events/SimpleEventListener.java | 7 ++++ .../zalando/client/java/SimpleEventTest.java | 36 +++++++++++++++++-- .../nakadi/client/scala/SimpleEventTest.scala | 27 ++++++++++---- 6 files changed, 72 insertions(+), 24 deletions(-) diff --git a/client/src/main/scala/org/zalando/nakadi/client/actor/SubscriptionHolder.scala b/client/src/main/scala/org/zalando/nakadi/client/actor/SubscriptionHolder.scala index 441a8e6..db973f4 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/actor/SubscriptionHolder.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/actor/SubscriptionHolder.scala @@ -23,23 +23,21 @@ trait SubscriptionHolder { class SubscriptionHolderImpl extends SubscriptionHolder { import SupervisingActor._ private var subscriptions: Map[SubscriptionKey, SubscriptionEntry] = Map() //EventTypeName+Partition - private var cursors: Map[SubscriptionKey, Option[Cursor]] = Map() + private var cursors: Map[SubscriptionKey, Option[Cursor]] = Map() private var actors: Map[String, SubscriptionKey] = Map() - private var subscriptionCounter = 1 private val logger = Logger(LoggerFactory.getLogger(this.getClass)) - def addCursor(key: SubscriptionKey, cursor: Option[Cursor]): Unit = { + def addCursor(key: SubscriptionKey, cursor: Option[Cursor]): Unit = { cursors = cursors + ((key, cursor)) } def addSubscription(key: SubscriptionKey, key2: ActorRef, entry: SubscriptionEntry) = { subscriptions = subscriptions + ((key, entry)) actors = actors + ((key2.path.toString(), key)) - subscriptionCounter+=1 } def unsubscribe(key: SubscriptionKey): Unit = { - + subscriptions = subscriptions - (key) } def entry(key: SubscriptionKey): Option[SubscriptionEntry] = { @@ -53,10 +51,6 @@ class SubscriptionHolderImpl extends SubscriptionHolder { subscriptions.size } - def count(): Int = { - subscriptionCounter - } - def addActor(actor: ActorRef, key: SubscriptionKey): Unit = { actors = actors + ((actor.path.toString(), key)) } @@ -66,7 +60,7 @@ class SubscriptionHolderImpl extends SubscriptionHolder { } def cursorByActor(actor: ActorRef): Option[Cursor] = { - actors.get(actor.path.toString()).flatMap(x => cursors.get(x).flatMap(a => a )) + actors.get(actor.path.toString()).flatMap(x => cursors.get(x).flatMap(a => a)) } } \ No newline at end of file diff --git a/client/src/main/scala/org/zalando/nakadi/client/actor/SupervisingActor.scala b/client/src/main/scala/org/zalando/nakadi/client/actor/SupervisingActor.scala index 50060e2..eac7bb7 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/actor/SupervisingActor.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/actor/SupervisingActor.scala @@ -90,9 +90,9 @@ class SupervisingActor(val connection: Connection, val subscriptionHandler: Subs } def subscribe(subscribe: SubscribeMsg) = { - subscriptionCounter+=1 + subscriptionCounter += 1 val SubscribeMsg(eventTypeName, endpoint, optCursor, eventHandler) = subscribe - log.info("Subscription nr {} - cursor {} - eventType {} - listener {}", subscriptionCounter , optCursor, eventTypeName, eventHandler.id()) + log.info("Subscription nr {} - cursor {} - eventType {} - listener {}", subscriptionCounter, optCursor, eventTypeName, eventHandler.id()) val subKey: SubscriptionKey = optCursor match { case Some(Cursor(partition, _)) => SubscriptionKey(eventTypeName, Some(partition)) @@ -115,7 +115,7 @@ class SupervisingActor(val connection: Connection, val subscriptionHandler: Subs subscriptions.addCursor(subKey, optCursor) } - def unsubscribe(unsubscription: UnsubscribeMsg) = { + def unsubscribe(unsubscription: UnsubscribeMsg): Boolean = { val UnsubscribeMsg(eventTypeName, partition, eventHandlerId) = unsubscription val key: SubscriptionKey = SubscriptionKey(eventTypeName, partition) log.info("Unsubscribe({}) ", unsubscription) @@ -124,8 +124,10 @@ class SupervisingActor(val connection: Connection, val subscriptionHandler: Subs log.info("Unsubscribing Listener : {} from actor: {}", handler, actor) actor ! PoisonPill subscriptions.unsubscribe(key) + true case None => log.warning("Listener not found for {}", unsubscription) + false } } diff --git a/it/src/main/java/org/zalando/nakadi/client/java/test/factory/EventIntegrationHelper.java b/it/src/main/java/org/zalando/nakadi/client/java/test/factory/EventIntegrationHelper.java index 65ed8f8..2c2bd1c 100644 --- a/it/src/main/java/org/zalando/nakadi/client/java/test/factory/EventIntegrationHelper.java +++ b/it/src/main/java/org/zalando/nakadi/client/java/test/factory/EventIntegrationHelper.java @@ -68,10 +68,10 @@ public List getPartitionStrategies() throws InterruptedExcept } - public List publishEvents(Integer nrOfEvents) { + public List publishEvents(Integer nrOfEvents) throws InterruptedException, ExecutionException { List events = new ArrayList(); IntStream.range(0, nrOfEvents).forEach(nr -> events.add(gen.getNewEvent())); - client.publishEvents(eventType.getName(), events); + client.publishEvents(eventType.getName(), events).get(); log.info("#######"); log.info("#######" + events.size()); log.info("#######"); diff --git a/it/src/main/java/org/zalando/nakadi/client/java/test/factory/events/SimpleEventListener.java b/it/src/main/java/org/zalando/nakadi/client/java/test/factory/events/SimpleEventListener.java index a5b3b6e..b9b9541 100644 --- a/it/src/main/java/org/zalando/nakadi/client/java/test/factory/events/SimpleEventListener.java +++ b/it/src/main/java/org/zalando/nakadi/client/java/test/factory/events/SimpleEventListener.java @@ -43,6 +43,11 @@ public void onError(String eventType, Optional error) { } + + public List getReceivedEvents() { + return receivedEvents; + } + public List waitToReceive(Integer nrOfEvents) throws InterruptedException { while (nrOfEvents > receivedEvents.size()){ log.info("Total: {}", receivedEvents.size()); @@ -54,4 +59,6 @@ public List waitToReceive(Integer nrOfEvents) throws InterruptedE log.info("##############"); return receivedEvents; } + + } diff --git a/it/src/test/java/org/zalando/client/java/SimpleEventTest.java b/it/src/test/java/org/zalando/client/java/SimpleEventTest.java index ebcbfbc..1a2681a 100644 --- a/it/src/test/java/org/zalando/client/java/SimpleEventTest.java +++ b/it/src/test/java/org/zalando/client/java/SimpleEventTest.java @@ -2,6 +2,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import java.util.List; import java.util.Optional; @@ -17,7 +18,6 @@ import org.zalando.nakadi.client.java.model.EventStreamBatch; import org.zalando.nakadi.client.java.model.EventType; import org.zalando.nakadi.client.java.test.factory.EventGenerator; -import org.zalando.nakadi.client.java.test.factory.EventGeneratorBuilder; import org.zalando.nakadi.client.java.test.factory.EventIntegrationHelper; import org.zalando.nakadi.client.java.test.factory.MySimpleEventGenerator; import org.zalando.nakadi.client.java.test.factory.events.MySimpleEvent; @@ -41,6 +41,7 @@ public void shutdown() throws InterruptedException, ExecutionException { @Test public void handle404Graciously() throws InterruptedException, ExecutionException { + EventGenerator gen = new MySimpleEventGenerator()// .withEventTypeId("SimpleEventTest-handle404Graciously")// .build(); @@ -52,7 +53,7 @@ public void handle404Graciously() throws InterruptedException, ExecutionExceptio } @Test - public void validatePublishedNrOfEvents() throws InterruptedException { + public void validatePublishedNrOfEvents() throws InterruptedException, ExecutionException { SimpleEventListener listener = new SimpleEventListener(); EventGenerator gen = new MySimpleEventGenerator()// .withEventTypeId("SimpleEventTest-validatePublishedNrOfEvents").build(); @@ -81,6 +82,37 @@ public void validatePublishedNrOfEvents() throws InterruptedException { assertEquals("Created & Received events differ in number", createdEvents.size(), receivedEvents.size()); } + @Test + public void unsubscribedListenerShouldNotReceiveAnyEvents() throws InterruptedException, ExecutionException { + SimpleEventListener listener = new SimpleEventListener(); + EventGenerator gen = new MySimpleEventGenerator()// + .withEventTypeId("SimpleEventTest-validatePublishedNrOfEvents").build(); + EventIntegrationHelper it = new EventIntegrationHelper(gen, client); + assertTrue("EventType should be created", it.createEventType()); + Thread.sleep(1000);// Creation can take time. + Optional eventTypeOpt = it.getEventType(); + assertTrue("Did not return the eventType", eventTypeOpt.isPresent()); + + Optional cursor = Optional.of(new Cursor("0", "BEGIN")); + Optional batchLimit = Optional.empty(); + Optional streamLimit = Optional.empty(); + Optional batchFlushTimeout = Optional.empty(); + Optional streamTimeout = Optional.empty(); + Optional streamKeepAliveLimit = Optional.empty(); + Optional flowId = Optional.empty(); + StreamParameters parameters = new StreamParameters(cursor, // + batchLimit, // + streamLimit, // + batchFlushTimeout, // + streamTimeout, // + streamKeepAliveLimit, // + flowId); + client.subscribe(it.getGen().getEventTypeName(), parameters, listener, typeRef); + client.unsubscribe(it.getGen().getEventTypeName(), Optional.of("0"), listener); + List createdEvents = it.publishEvents(nrOfEvents); + Thread.sleep(5000); + assertEquals("Unsubscribed listener must not receive events", 0, listener.getReceivedEvents().size()); + } @Test public void validateCreatedEventType() throws InterruptedException { diff --git a/it/src/test/scala/org/zalando/nakadi/client/scala/SimpleEventTest.scala b/it/src/test/scala/org/zalando/nakadi/client/scala/SimpleEventTest.scala index 6c990bd..428dc21 100644 --- a/it/src/test/scala/org/zalando/nakadi/client/scala/SimpleEventTest.scala +++ b/it/src/test/scala/org/zalando/nakadi/client/scala/SimpleEventTest.scala @@ -56,10 +56,10 @@ class SimpleEventTest extends WordSpec with Matchers with BeforeAndAfterAll { } "Multiple events listeners must work in parallel" in { - val eventGenerator1 = new DefaultMySimpleEventGenerator() { + val eventGenerator = new DefaultMySimpleEventGenerator() { def eventTypeId = s"SimpleEventIntegrationTest-Multiple-listeners-in-parallel-$nrOfEvents" } - val it = new EventIntegrationHelper(eventGenerator1, client) + val it = new EventIntegrationHelper(eventGenerator, client) val cursor = Some(Cursor("0", "BEGIN")) it.createEventType() shouldBe true val events = it.publishEvents(nrOfEvents) @@ -67,8 +67,8 @@ class SimpleEventTest extends WordSpec with Matchers with BeforeAndAfterAll { val listener2 = new SimpleEventListener() val listener3 = new SimpleEventListener() - client.subscribe(eventGenerator1.eventTypeName, StreamParameters(cursor = cursor), listener) - client.subscribe(eventGenerator1.eventTypeName, StreamParameters(cursor = cursor), listener2) + client.subscribe(eventGenerator.eventTypeName, StreamParameters(cursor = cursor), listener) + client.subscribe(eventGenerator.eventTypeName, StreamParameters(cursor = cursor), listener2) val receivedEvents = listener.waitToReceive(nrOfEvents) receivedEvents.size shouldBe events.size @@ -78,13 +78,26 @@ class SimpleEventTest extends WordSpec with Matchers with BeforeAndAfterAll { receivedEvents2.size shouldBe events.size receivedEvents2 shouldBe events - client.subscribe(eventGenerator1.eventTypeName, StreamParameters(cursor = cursor), listener) + client.subscribe(eventGenerator.eventTypeName, StreamParameters(cursor = cursor), listener) val receivedEvents3 = listener.waitToReceive(nrOfEvents * 2) receivedEvents3.size shouldBe (nrOfEvents * 2) - + } - + "An unsubscribed listener should not receive any events" in { + val eventGenerator = new DefaultMySimpleEventGenerator() { + def eventTypeId = s"SimpleEventIntegrationTest-Multiple-listeners-in-parallel-$nrOfEvents" + } + val it = new EventIntegrationHelper(eventGenerator, client) + val cursor = Some(Cursor("0", "BEGIN")) + it.createEventType() shouldBe true + val listener = new SimpleEventListener() + client.subscribe(eventGenerator.eventTypeName, StreamParameters(cursor = cursor), listener) + client.unsubscribe(eventGenerator.eventTypeName, Option("0"), listener) + it.publishEvents(nrOfEvents) + Thread.sleep(5000) + listener.receivedEvents.size shouldBe 0 + } "Validate created EventType" in { val eventGenerator = new DefaultMySimpleEventGenerator() { def eventTypeId = s"SimpleEventIntegrationTest-Validate-Created-EventType" From 08b1733cc543c218ccae8fa4c81c122c7840b618 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Tue, 21 Jun 2016 15:38:52 +0200 Subject: [PATCH 099/183] techjira:LAAS-60 Development release 2.0.0-pre-alpha.15 --- project/Build.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/Build.scala b/project/Build.scala index 8aa6d4d..f857788 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -57,7 +57,7 @@ lazy val client = withDefaults( project.settings( name := projectName, organization := "org.zalando.nakadi.client", - version := "2.0.0-pre-alpha.14", + version := "2.0.0-pre-alpha.15", crossPaths := false, scalaVersion := "2.11.8", publishTo := whereToPublishTo(isSnapshot.value), From 7bb9ee66a698b55dd5f7117744bd1c617e021a06 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Wed, 22 Jun 2016 15:11:39 +0200 Subject: [PATCH 100/183] techjira:LAAS-60 Adding dependencies to facilitate execution of junit execution within sbt --- project/Dependencies.scala | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/project/Dependencies.scala b/project/Dependencies.scala index ef8c2d0..9779b50 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -21,11 +21,12 @@ object Dependencies { "com.typesafe.akka" %% "akka-actor" % akkaVersion, "com.typesafe.akka" %% "akka-http-experimental" % akkaVersion, "com.typesafe.akka" %% "akka-stream" % akkaVersion, - "com.typesafe.akka" %% "akka-testkit" % akkaVersion % "test", - "org.scalatest" %% "scalatest" % "2.2.6" % "test", - "com.google.code.findbugs" % "jsr305" % "1.3.9" % "test", - "junit" % "junit" % "4.12" % "test", - "org.mockito" % "mockito-core" % "1.10.19" % "test" + "com.typesafe.akka" %% "akka-testkit" % akkaVersion % "test", + "org.scalatest" %% "scalatest" % "2.2.6" % "test", + "com.google.code.findbugs" % "jsr305" % "1.3.9" % "test", + "junit" % "junit" % "4.12" % "test", + "org.mockito" % "mockito-core" % "1.10.19" % "test", + "com.novocode" % "junit-interface" % "0.11" % "test" ) } From d0bc24a9bc600d8996efba313592e614d4a529aa Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Thu, 23 Jun 2016 13:07:25 +0200 Subject: [PATCH 101/183] techjira:LAAS-60 Changing according to feedback from @codechimp --- .../zalando/nakadi/client/java/Client.java | 157 ++++++---- .../enumerator/EventEnrichmentStrategy.java | 19 +- .../java/enumerator/EventTypeCategory.java | 35 ++- .../enumerator/EventValidationStrategy.java | 40 +-- .../java/enumerator/PartitionStrategy.java | 44 +-- .../client/java/enumerator/SchemaType.java | 10 +- .../nakadi/client/java/model/EventType.java | 293 ++++++++---------- .../java/model/EventTypeStatistics.java | 71 ++++- .../nakadi/client/java/model/Metrics.java | 7 +- .../nakadi/client/java/model/Partition.java | 66 ++-- .../nakadi/client/java/model/Problem.java | 43 ++- .../zalando/nakadi/client/scala/Client.scala | 9 - .../nakadi/client/java/ClientImpl.java | 5 - client/src/test/resources/application.conf | 29 -- .../test/factory/EventIntegrationHelper.scala | 1 - .../nakadi/client/scala/SimpleEventTest.scala | 15 +- 16 files changed, 444 insertions(+), 400 deletions(-) delete mode 100644 client/src/test/resources/application.conf diff --git a/api/src/main/java/org/zalando/nakadi/client/java/Client.java b/api/src/main/java/org/zalando/nakadi/client/java/Client.java index 9a1082a..2440083 100644 --- a/api/src/main/java/org/zalando/nakadi/client/java/Client.java +++ b/api/src/main/java/org/zalando/nakadi/client/java/Client.java @@ -7,7 +7,6 @@ import org.zalando.nakadi.client.Deserializer; import org.zalando.nakadi.client.Serializer; import org.zalando.nakadi.client.java.enumerator.EventEnrichmentStrategy; -import org.zalando.nakadi.client.java.enumerator.EventValidationStrategy; import org.zalando.nakadi.client.java.enumerator.PartitionStrategy; import org.zalando.nakadi.client.java.model.Event; import org.zalando.nakadi.client.java.model.EventStreamBatch; @@ -20,144 +19,178 @@ public interface Client { /** - * Retrieves monitoring metrics. NOTE: metrics format is v + * Retrieves all metric data. * * @return metrics data */ Future> getMetrics(); /** - * Retrieves all registered EventTypes. + * Retrieves a list of all registered EventTypes. * * @return List of known EventTypes */ Future>> getEventTypes(); /** - * Creates an eventType(topic). + * Creates a new `EventType`. * - * @param eventType The EventType to create + * @param eventType + * The EventType to create * @return Void in case of success */ Future createEventType(EventType eventType); /** - * Retrieves the EventType. + * Retrieves the EventType identified by its name. * - * @param eventTypeName The unique name (id) of the EventType to retrieve - * @return The EventType if it can be found + * @param eventTypeName + * The name of the EventType to retrieve. + * @return The EventType if it can be found */ Future> getEventType(String eventTypeName); /** - * Updates the eventType. - * @param eventTypeName The unique name (id) of the EventType to update - * @param eventType The eventType to be updated. + * Updates the eventType identified by its name. + * + * @param eventTypeName + * The name of the EventType to update. + * @param eventType + * The eventType to update. * @return Void in case of success */ Future updateEventType(String eventTypeName, EventType eventType); /** - * Deletes an eventType. - * @param eventTypeName The unique name (id) of the EventType to update + * Deletes an eventType identified by its name. + * + * @param eventTypeName + * The name of the EventType to delete. * @return Void in case of success */ Future deleteEventType(String eventTypeName); - + /** * Publishes a single event to the given eventType using a custom serializer.
          - * Partition selection is done using the defined partition resolution,
          - * which is defined per topic and managed by the event store. - * @param eventTypeName The unique name (id) of the EventType target - * @param event The event to be published - * @param serializer The custom serializer to serialize the event. + * + * @param eventTypeName + * The name of the EventType target. + * @param event + * The event to publish. + * @param serializer + * The serializer to use for the serialization of the event. * @return Void in case of success */ Future publishEvent(String eventTypeName, T event, Serializer> serializer); - + /** - * Publishes a single event to the given eventType.
          - * Partition selection is done using the defined partition resolution,
          - * which is defined per topic and managed by the event store. - * @param eventTypeName The unique name (id) of the EventType target - * @param event The event to be published - * @param ref The Jackson TypeReference of the Event to be used by the default Jackson Marshaller. - * @return Void in case of success + * Publishes a single event to the given eventType identified by its name.
          + * + * @param eventTypeName + * The name of the EventType target. + * @param event + * The event to publish + * @param ref + * The Jackson TypeReference of the Event to be used by the default Jackson Marshaller. + * @return Void in case of success */ Future publishEvent(String eventTypeName, T event); /** - * Publishes a List of events to the given eventType using a custom serializer.
          - * Partition selection is done using the defined partition resolution,
          - * which is defined per topic and managed by the event store. - * @param eventTypeName The unique name (id) of the EventType target - * @param events The list of events to be published - * @param serializer The custom serializer to serialize the events. + * Publishes a Batch(list) of events to the given eventType, identified by its name, using a custom serializer. + * + * @param eventTypeName + * The name of the EventType target. + * @param events + * The list of events to be published + * @param serializer + * The custom serializer to serialize the events. * @return Void in case of success */ Future publishEvents(String eventTypeName, List events, Serializer> serializer); + /** - * Publishes a List of events to the given eventType.
          - * Partition selection is done using the defined partition resolution,
          - * which is defined per topic and managed by the event store. - * @param eventTypeName The unique name (id) of the EventType target - * @param event The event to be published - * @return Void in case of success + * Publishes a List of events to the given eventType identified by its name. + * + * @param eventTypeName + * The name of the EventType target. + * @param event + * The event to publish + * @return Void in case of success */ Future publishEvents(String eventTypeName, List events); /** * Retrieves the existing partitions for the given EventType. - * @param eventTypeName The unique name (id) of the EventType + * + * @param eventTypeName + * The name of the EventType target. * @return list of existing partitions */ Future>> getPartitions(String eventTypeName); /** - * Retrieves a List of all Validation strategies supported by the Event store. - * @return list of validation strategies - */ - Future>> getValidationStrategies(); - - /** - * Retrieves a List of all Enrichment strategies supported by the Event store. + * Retrieves a List of all enrichment strategies supported. + * * @return list of enrichment strategies */ Future>> getEnrichmentStrategies(); + /** - * Retrieves a List of all Partition strategies supported by the Event store. + * Retrieves a List of all partition strategies supported. + * * @return list of enrichment strategies */ Future>> getPartitioningStrategies(); /** * Shuts down the communication system of the client - * @return Void in case of success + * */ void stop(); - + /** - * Registers the subscription of a listener to start streaming events from a partition in non-blocking fashion. - * @param eventTypeName The unique name (id) of the EventType target - * @param parameters Parameters for customizing the details of the streaming. - * @param listener Listener to pass the event to when it is received. + * Subscribes a listener to the eventType, identified by its name, and start streaming events in a non-blocking + * fashion. + * + * @param eventTypeName + * The name of the EventType target. + * @param parameters + * Parameters for customizing the details of the streaming. + * @param listener + * Listener to pass the event to when it is received. + * @param deserializer + * Deserializer to use for deserializing events. * @return ClientError in case of failure and Empty Optional in case of success. */ Optional subscribe(String eventTypeName, StreamParameters parameters, Listener listener, Deserializer> deserializer); + /** - * Registers the subscription of a listener to start streaming events from a partition in non-blocking fashion. - * @param eventTypeName The unique name (id) of the EventType target - * @param parameters Parameters for customizing the details of the streaming. - * @param typeRef TypeReference for unmarshalling with the Jackson ObjectMapper. + * Subscribes a listener to the eventType, identified by its name, and start streaming events in a non-blocking + * fashion. + * + * @param eventTypeName + * The name of the EventType target. + * @param parameters + * Parameters for customizing the details of the streaming. + * @param listener + * Listener to pass the event to when it is received. + * @param typeRef + * TypeReference for unmarshalling with the Jackson ObjectMapper. * @return ClientError in case of failure and Empty Optional in case of success. */ Optional subscribe(String eventTypeName, StreamParameters parameters, Listener listener, TypeReference> typeRef); + /** * Removes the subscription of a listener, to stop streaming events from a partition. - * @param eventTypeName The unique name (id) of the EventType target - * @param partition The partition assigned to this listener. - * @param listener Listener to pass the event to when it is received. + * + * @param eventTypeName + * The name of the EventType target. + * @param partition + * The partition assigned to this listener. + * @param listener + * Listener to pass the event to when it is received. * @return Void in case of success */ - void unsubscribe(String eventTypeName,Optional partition, Listener listener); + void unsubscribe(String eventTypeName, Optional partition, Listener listener); } \ No newline at end of file diff --git a/api/src/main/java/org/zalando/nakadi/client/java/enumerator/EventEnrichmentStrategy.java b/api/src/main/java/org/zalando/nakadi/client/java/enumerator/EventEnrichmentStrategy.java index d7d00a4..dd0d374 100644 --- a/api/src/main/java/org/zalando/nakadi/client/java/enumerator/EventEnrichmentStrategy.java +++ b/api/src/main/java/org/zalando/nakadi/client/java/enumerator/EventEnrichmentStrategy.java @@ -1,16 +1,13 @@ package org.zalando.nakadi.client.java.enumerator; -import scala.Option; +import java.util.Optional; import com.fasterxml.jackson.annotation.JsonValue; /** - * Defines a rule for transformation of an incoming Event. No existing fields - * might be modified. In practice this is used to set automatically values in - * the Event upon reception (i.e. set a reception timestamp on the Event). Rules - * might require additional parameters; see the `doc` field of the existing - * rules for details. See GET /registry/enrichment-strategies for a list of - * available rules. + * Determines the enrichment to be performed on an Event upon reception(server side). Enrichment is performed once upon + * reception (and after validation) of an Event and is only possible on fields that are not defined on the incoming + * Event. */ public enum EventEnrichmentStrategy { @@ -27,11 +24,11 @@ public String getMetadata() { return metadata; } - public static Option withName(String code) { + public static Optional withName(String code) { for (EventEnrichmentStrategy e : EventEnrichmentStrategy.values()) { - if (e.metadata.equals(code)) - return Option.apply(e); + if (e != null && e.metadata.equals(code)) + return Optional.of(e); } - return Option.empty(); + return Optional.empty(); } } diff --git a/api/src/main/java/org/zalando/nakadi/client/java/enumerator/EventTypeCategory.java b/api/src/main/java/org/zalando/nakadi/client/java/enumerator/EventTypeCategory.java index 1c0e768..87b3cad 100644 --- a/api/src/main/java/org/zalando/nakadi/client/java/enumerator/EventTypeCategory.java +++ b/api/src/main/java/org/zalando/nakadi/client/java/enumerator/EventTypeCategory.java @@ -1,6 +1,6 @@ package org.zalando.nakadi.client.java.enumerator; -import scala.Option; +import java.util.Optional; import com.fasterxml.jackson.annotation.JsonValue; @@ -8,25 +8,26 @@ * Defines the category of an EventType.
          */ public enum EventTypeCategory { - UNDEFINED("undefined"), // - DATA("data"), // - BUSINESS("business"); + UNDEFINED("undefined"), // + DATA("data"), // + BUSINESS("business"); - private final String category; + private final String category; - private EventTypeCategory(String category) { - this.category = category; - } - @JsonValue - public String getCategory() { - return category; - } + private EventTypeCategory(String category) { + this.category = category; + } + + @JsonValue + public String getCategory() { + return category; + } - public static Option withName(String code){ - for(EventTypeCategory e : EventTypeCategory.values()){ - if (e.category.equals(code)) - return Option.apply(e); + public static Optional withName(String code) { + for (EventTypeCategory e : EventTypeCategory.values()) { + if (e != null && e.category.equals(code)) + return Optional.of(e); } - return Option.empty(); + return Optional.empty(); } } diff --git a/api/src/main/java/org/zalando/nakadi/client/java/enumerator/EventValidationStrategy.java b/api/src/main/java/org/zalando/nakadi/client/java/enumerator/EventValidationStrategy.java index cb437e5..cf901f4 100644 --- a/api/src/main/java/org/zalando/nakadi/client/java/enumerator/EventValidationStrategy.java +++ b/api/src/main/java/org/zalando/nakadi/client/java/enumerator/EventValidationStrategy.java @@ -1,31 +1,31 @@ package org.zalando.nakadi.client.java.enumerator; -import scala.Option; +import java.util.Optional; import com.fasterxml.jackson.annotation.JsonValue; /** - * Defines a rule for validation of an incoming Event. Rules might require - * additional parameters; see the `doc` field of the existing rules for details. - * See GET /registry/validation-strategies for a list of available rules. + * Defines a rule for validation of an incoming Event. Rules might require additional parameters; see the `doc` field of + * the existing rules for details. See GET /registry/validation-strategies for a list of available rules. */ public enum EventValidationStrategy { - SCHEMA_VALIDATION("schema-validation"); - private final String strategy; + SCHEMA_VALIDATION("schema-validation"); + private final String strategy; - private EventValidationStrategy(String strategy) { - this.strategy = strategy; - } - @JsonValue - public String getStrategy() { - return strategy; - } + private EventValidationStrategy(String strategy) { + this.strategy = strategy; + } - public static Option withName(String code) { - for (EventValidationStrategy e : EventValidationStrategy.values()) { - if (e.strategy.equals(code)) - return Option.apply(e); - } - return Option.empty(); - } + @JsonValue + public String getStrategy() { + return strategy; + } + + public static Optional withName(String code) { + for (EventValidationStrategy e : EventValidationStrategy.values()) { + if (e != null && e.strategy.equals(code)) + return Optional.of(e); + } + return Optional.empty(); + } } diff --git a/api/src/main/java/org/zalando/nakadi/client/java/enumerator/PartitionStrategy.java b/api/src/main/java/org/zalando/nakadi/client/java/enumerator/PartitionStrategy.java index a9a946b..68825e7 100644 --- a/api/src/main/java/org/zalando/nakadi/client/java/enumerator/PartitionStrategy.java +++ b/api/src/main/java/org/zalando/nakadi/client/java/enumerator/PartitionStrategy.java @@ -5,31 +5,31 @@ import com.fasterxml.jackson.annotation.JsonValue; /** - * Defines a rule for the resolution of incoming Events into partitions. Rules - * might require additional parameters; see the `doc` field of the existing - * rules for details. See `GET /registry/partition-strategies` for a list of - * available rules. + * Defines a rule for the resolution of incoming Events into partitions. Rules might require additional parameters; see + * the `doc` field of the existing rules for details. See `GET /registry/partition-strategies` for a list of available + * rules. */ public enum PartitionStrategy { - HASH("hash"), // - USER_DEFINED("user_defined"), // - RANDOM("random"); - - private final String strategy; - - private PartitionStrategy(String strategy) { - this.strategy = strategy; - } - @JsonValue - public String getStrategy() { - return strategy; - } - - public static Optional withName(String code){ - for(PartitionStrategy e : PartitionStrategy.values()){ - if (e.name().equals(code)) - return Optional.of(e); + HASH("hash"), // + USER_DEFINED("user_defined"), // + RANDOM("random"); + + private final String strategy; + + private PartitionStrategy(String strategy) { + this.strategy = strategy; + } + + @JsonValue + public String getStrategy() { + return strategy; + } + + public static Optional withName(String code) { + for (PartitionStrategy e : PartitionStrategy.values()) { + if (e != null && e.name().equals(code)) + return Optional.of(e); } return Optional.empty(); } diff --git a/api/src/main/java/org/zalando/nakadi/client/java/enumerator/SchemaType.java b/api/src/main/java/org/zalando/nakadi/client/java/enumerator/SchemaType.java index 9bfc46b..9153571 100644 --- a/api/src/main/java/org/zalando/nakadi/client/java/enumerator/SchemaType.java +++ b/api/src/main/java/org/zalando/nakadi/client/java/enumerator/SchemaType.java @@ -1,6 +1,6 @@ package org.zalando.nakadi.client.java.enumerator; -import scala.Option; +import java.util.Optional; import com.fasterxml.jackson.annotation.JsonValue; @@ -17,12 +17,12 @@ public String getSchema() { return schema; } - public static Option withName(String code) { + public static Optional withName(String code) { for (SchemaType e : SchemaType.values()) { - if (e.schema.equals(code)) - return Option.apply(e); + if (e != null && e.schema.equals(code)) + return Optional.of(e); } - return Option.empty(); + return Optional.empty(); } } diff --git a/api/src/main/java/org/zalando/nakadi/client/java/model/EventType.java b/api/src/main/java/org/zalando/nakadi/client/java/model/EventType.java index 8cc9a4c..b3c2f2e 100644 --- a/api/src/main/java/org/zalando/nakadi/client/java/model/EventType.java +++ b/api/src/main/java/org/zalando/nakadi/client/java/model/EventType.java @@ -1,5 +1,7 @@ package org.zalando.nakadi.client.java.model; +import java.util.Collection; +import java.util.Collections; import java.util.List; import org.zalando.nakadi.client.java.enumerator.EventEnrichmentStrategy; @@ -16,168 +18,133 @@ * */ public class EventType { - private final String name; - private final String owningApplication; - private final EventTypeCategory category; - private final List validationStrategies; - private final List enrichmentStrategies; - private final PartitionStrategy partitionStrategy; - private final EventTypeSchema schema; - private final List dataKeyFields; - private final List partitionKeyFields; - private final EventTypeStatistics statistics; - - /** - * An event type defines the schema and its runtime properties. - * - * @param name - * Name of this EventType. Encodes the owner/responsible for this - * EventType. The name for the EventType SHOULD follow the - * pattern, but is not enforced - * 'stups_owning_application.event-type', for example - * 'gizig.price-change'. The components of the name are: * - * Organization: the organizational unit where the team is - * located; can be omitted. * Team name: name of the team - * responsible for owning application; can be omitted. * Owning - * application: SHOULD match the field owning_application; - * indicates * EventType name: name of this EventType; SHOULD end - * in ChangeEvent for DataChangeEvents; MUST be in the past tense - * for BusinessEvents. (TBD: how to deal with organizational - * changes? Should be reflected on the name of the EventType? - * Isn't it better to omit [organization:team] completely?) - * @param owningApplication - * Indicator of the Application owning this `EventType`. - * @param category - * Defines the category of this EventType. The value set will - * influence, if not set otherwise, the default set of - * validation-strategies, enrichment-strategies, and the - * effective_schema in the following way: - `undefined`: No - * predefined changes apply. `effective_schema` is exactly the - * same as the `EventTypeSchema`. Default validation_strategy for - * this `EventType` is `[{name: 'schema-validation'}]`. - `data`: - * Events of this category will be DataChangeEvents. - * `effective_schema` contains `metadata`, and adds fields - * `data_op` and `data_type`. The passed EventTypeSchema defines - * the schema of `data`. Default validation_strategy for this - * `EventType` is `[{name: 'datachange-schema-validation'}]`. - - * `business`: Events of this category will be BusinessEvents. - * `effective_schema` contains `metadata` and any additionally - * defined properties passed in the `EventTypeSchema` directly on - * top level of the Event. If name conflicts arise, creation of - * this EventType will be rejected. Default validation_strategy - * for this `EventType` is `[{name: 'schema-validation'}]`. - * @param validationStrategies - * Determines the validation that has to be executed upon - * reception of Events of this type. Events are rejected if any - * of the rules fail (see details of Problem response on the - * Event publishing methods). Rule evaluation order is the same - * as in this array. If not explicitly set, default value will - * respect the definition of the `EventType.category`. - * @param enrichmentStrategies - * Determines the enrichment to be performed on an Event upon - * reception. Enrichment is performed once upon reception (and - * after validation) of an Event and is only possible on fields - * that are not defined on the incoming Event. See documentation - * for the write operation for details on behaviour in case of - * unsuccessful enrichment. - * @param partitionStrategy - * Determines how the assignment of the event to a Partition - * should be handled. - * @param schema - * The schema for this EventType. This is expected to be a json - * schema in yaml format (other formats might be added in the - * future). - * @param dataKeyFields - * Indicators of the path of the properties that constitute the - * primary key (identifier) of the data within this Event. If set - * MUST be a valid required field as defined in the schema. (TBD - * should be required? Is applicable only to both Business and - * DataChange Events?) - * @param partitioningKeyFields - * Indicator of the field used for guaranteeing the ordering of - * Events of this type (used by the PartitionResolutionStrategy). - * If set MUST be a valid required field as defined in the - * schema. - * @param statistics - * Statistics of this EventType used for optimization purposes. - * Internal use of these values might change over time. (TBD: - * measured statistics). - * - */ - @JsonCreator - public EventType( - @JsonProperty("name") String name, - @JsonProperty("owning_application") String owningApplication, - @JsonProperty("category") EventTypeCategory category, - @JsonProperty("validation_strategies") List validationStrategies, - @JsonProperty("enrichment_strategies") List enrichmentStrategies, - @JsonProperty("partition_strategy") PartitionStrategy partitionStrategy, - @JsonProperty("schema") EventTypeSchema schema, - @JsonProperty("data_key_fields") List dataKeyFields, - @JsonProperty("partition_key_fields") List partitionKeyFields, - @JsonProperty("default_statistics") EventTypeStatistics statistics) { - this.name = name; - this.owningApplication = owningApplication; - this.category = category; - this.validationStrategies = validationStrategies; - this.enrichmentStrategies = enrichmentStrategies; - this.partitionStrategy = partitionStrategy; - this.schema = schema; - this.dataKeyFields = dataKeyFields; - this.partitionKeyFields = partitionKeyFields; - this.statistics = statistics; - } - - public String getName() { - return name; - } - - public String getOwningApplication() { - return owningApplication; - } - - public EventTypeCategory getCategory() { - return category; - } - - public List getValidationStrategies() { - return validationStrategies; - } - - public List getEnrichmentStrategies() { - return enrichmentStrategies; - } - - public PartitionStrategy getPartitionStrategy() { - return partitionStrategy; - } - - public EventTypeSchema getSchema() { - return schema; - } - - public List getDataKeyFields() { - return dataKeyFields; - } - - public List getPartitionKeyFields() { - return partitionKeyFields; - } - - public EventTypeStatistics getStatistics() { - return statistics; - } - - @Override - public String toString() { - return "EventType [name=" + name + ", owningApplication=" - + owningApplication + ", category=" + category - + ", validationStrategies=" + validationStrategies - + ", enrichmentStrategies=" + enrichmentStrategies - + ", partitionStrategy=" + partitionStrategy + ", schema=" - + schema + ", dataKeyFields=" + dataKeyFields - + ", partitionKeyFields=" + partitionKeyFields - + ", statistics=" + statistics + "]"; - } + private final String name; + private final String owningApplication; + private final EventTypeCategory category; + private final List validationStrategies; + private final List enrichmentStrategies; + private final PartitionStrategy partitionStrategy; + private final EventTypeSchema schema; + private final List dataKeyFields; + private final List partitionKeyFields; + private final EventTypeStatistics statistics; + + /** + * An event type defines the schema and its runtime properties. + * + * @param name + * Name of this EventType. Encodes the owner/responsible for this EventType. The name for the EventType + * SHOULD follow the pattern, but is not enforced 'stups_owning_application.event-type', for example + * 'gizig.price-change'. The components of the name are: * Organization: the organizational unit where + * the team is located; can be omitted. * Team name: name of the team responsible for owning application; + * can be omitted. * Owning application: SHOULD match the field owning_application; indicates * EventType + * name: name of this EventType; SHOULD end in ChangeEvent for DataChangeEvents; MUST be in the past + * tense for BusinessEvents. (TBD: how to deal with organizational changes? Should be reflected on the + * name of the EventType? Isn't it better to omit [organization:team] completely?) + * @param owningApplication + * Indicator of the Application owning this `EventType`. + * @param category + * Defines the category of this EventType. The value set will influence, if not set otherwise, the + * default set of validation-strategies, enrichment-strategies, and the effective_schema in the following + * way: - `undefined`: No predefined changes apply. `effective_schema` is exactly the same as the + * `EventTypeSchema`. Default validation_strategy for this `EventType` is `[{name: + * 'schema-validation'}]`. - `data`: Events of this category will be DataChangeEvents. `effective_schema` + * contains `metadata`, and adds fields `data_op` and `data_type`. The passed EventTypeSchema defines the + * schema of `data`. Default validation_strategy for this `EventType` is `[{name: + * 'datachange-schema-validation'}]`. - `business`: Events of this category will be BusinessEvents. + * `effective_schema` contains `metadata` and any additionally defined properties passed in the + * `EventTypeSchema` directly on top level of the Event. If name conflicts arise, creation of this + * EventType will be rejected. Default validation_strategy for this `EventType` is `[{name: + * 'schema-validation'}]`. + * @param validationStrategies + * Determines the validation that has to be executed upon reception of Events of this type. Events are + * rejected if any of the rules fail (see details of Problem response on the Event publishing methods). + * Rule evaluation order is the same as in this array. If not explicitly set, default value will respect + * the definition of the `EventType.category`. + * @param enrichmentStrategies + * Determines the enrichment to be performed on an Event upon reception. Enrichment is performed once + * upon reception (and after validation) of an Event and is only possible on fields that are not defined + * on the incoming Event. See documentation for the write operation for details on behaviour in case of + * unsuccessful enrichment. + * @param partitionStrategy + * Determines how the assignment of the event to a Partition should be handled. + * @param schema + * The schema for this EventType. This is expected to be a json schema in yaml format (other formats + * might be added in the future). + * @param dataKeyFields + * Indicators of the path of the properties that constitute the primary key (identifier) of the data + * within this Event. If set MUST be a valid required field as defined in the schema. (TBD should be + * required? Is applicable only to both Business and DataChange Events?) + * @param partitioningKeyFields + * Indicator of the field used for guaranteeing the ordering of Events of this type (used by the + * PartitionResolutionStrategy). If set MUST be a valid required field as defined in the schema. + * @param statistics + * Statistics of this EventType used for optimization purposes. Internal use of these values might change + * over time. (TBD: measured statistics). + * + */ + @JsonCreator + public EventType(@JsonProperty("name") String name, @JsonProperty("owning_application") String owningApplication, @JsonProperty("category") EventTypeCategory category, + @JsonProperty("validation_strategies") List validationStrategies, @JsonProperty("enrichment_strategies") List enrichmentStrategies, + @JsonProperty("partition_strategy") PartitionStrategy partitionStrategy, @JsonProperty("schema") EventTypeSchema schema, @JsonProperty("data_key_fields") List dataKeyFields, + @JsonProperty("partition_key_fields") List partitionKeyFields, @JsonProperty("default_statistics") EventTypeStatistics statistics) { + this.name = name; + this.owningApplication = owningApplication; + this.category = category; + this.validationStrategies = validationStrategies; + this.enrichmentStrategies = enrichmentStrategies; + this.partitionStrategy = partitionStrategy; + this.schema = schema; + this.dataKeyFields = dataKeyFields; + this.partitionKeyFields = partitionKeyFields; + this.statistics = statistics; + } + + public String getName() { + return name; + } + + public String getOwningApplication() { + return owningApplication; + } + + public EventTypeCategory getCategory() { + return category; + } + + public List getValidationStrategies() { + return Collections.unmodifiableList(validationStrategies); + } + + public List getEnrichmentStrategies() { + return Collections.unmodifiableList(enrichmentStrategies); + } + + public PartitionStrategy getPartitionStrategy() { + return partitionStrategy; + } + + public EventTypeSchema getSchema() { + return schema; + } + + public List getDataKeyFields() { + return Collections.unmodifiableList(dataKeyFields); + } + + public List getPartitionKeyFields() { + return Collections.unmodifiableList(partitionKeyFields); + } + + public EventTypeStatistics getStatistics() { + return statistics; + } + + @Override + public String toString() { + return "EventType [name=" + name + ", owningApplication=" + owningApplication + ", category=" + category + ", validationStrategies=" + validationStrategies + ", enrichmentStrategies=" + + enrichmentStrategies + ", partitionStrategy=" + partitionStrategy + ", schema=" + schema + ", dataKeyFields=" + dataKeyFields + ", partitionKeyFields=" + partitionKeyFields + + ", statistics=" + statistics + "]"; + } } diff --git a/api/src/main/java/org/zalando/nakadi/client/java/model/EventTypeStatistics.java b/api/src/main/java/org/zalando/nakadi/client/java/model/EventTypeStatistics.java index cc7371f..99324e8 100644 --- a/api/src/main/java/org/zalando/nakadi/client/java/model/EventTypeStatistics.java +++ b/api/src/main/java/org/zalando/nakadi/client/java/model/EventTypeStatistics.java @@ -3,37 +3,88 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; +/** + * Operational statistics for an EventType. This data is generated by Nakadi based on the runtime and might be used to + * guide changes in internal parameters. + * + */ public class EventTypeStatistics { private final Integer messagesPerMinute; private final Integer messageSize; private final Integer readParallelism; private final Integer writeParallelism; - - + @JsonCreator - public EventTypeStatistics( - @JsonProperty("messages_per_minute") Integer messagesPerMinute, - @JsonProperty("message_size") Integer messageSize, - @JsonProperty("read_parallelism") Integer readParallelism, - @JsonProperty("write_parallelism") Integer writeParallelism) { + public EventTypeStatistics(@JsonProperty("messages_per_minute") Integer messagesPerMinute, @JsonProperty("message_size") Integer messageSize, + @JsonProperty("read_parallelism") Integer readParallelism, @JsonProperty("write_parallelism") Integer writeParallelism) { this.messagesPerMinute = messagesPerMinute; this.messageSize = messageSize; this.readParallelism = readParallelism; this.writeParallelism = writeParallelism; } + public Integer getMessagesPerMinute() { return messagesPerMinute; } + public Integer getMessageSize() { return messageSize; } + public Integer getReadParallelism() { return readParallelism; } + public Integer getWriteParallelism() { return writeParallelism; } - - - + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((messageSize == null) ? 0 : messageSize.hashCode()); + result = prime * result + ((messagesPerMinute == null) ? 0 : messagesPerMinute.hashCode()); + result = prime * result + ((readParallelism == null) ? 0 : readParallelism.hashCode()); + result = prime * result + ((writeParallelism == null) ? 0 : writeParallelism.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + EventTypeStatistics other = (EventTypeStatistics) obj; + if (messageSize == null) { + if (other.messageSize != null) + return false; + } else if (!messageSize.equals(other.messageSize)) + return false; + if (messagesPerMinute == null) { + if (other.messagesPerMinute != null) + return false; + } else if (!messagesPerMinute.equals(other.messagesPerMinute)) + return false; + if (readParallelism == null) { + if (other.readParallelism != null) + return false; + } else if (!readParallelism.equals(other.readParallelism)) + return false; + if (writeParallelism == null) { + if (other.writeParallelism != null) + return false; + } else if (!writeParallelism.equals(other.writeParallelism)) + return false; + return true; + } + + @Override + public String toString() { + return "EventTypeStatistics [messagesPerMinute=" + messagesPerMinute + ", messageSize=" + messageSize + ", readParallelism=" + readParallelism + ", writeParallelism=" + writeParallelism + "]"; + } + } diff --git a/api/src/main/java/org/zalando/nakadi/client/java/model/Metrics.java b/api/src/main/java/org/zalando/nakadi/client/java/model/Metrics.java index 33ce946..e7f7629 100644 --- a/api/src/main/java/org/zalando/nakadi/client/java/model/Metrics.java +++ b/api/src/main/java/org/zalando/nakadi/client/java/model/Metrics.java @@ -6,7 +6,7 @@ import com.fasterxml.jackson.annotation.JsonProperty; public class Metrics { - private final Map metrics; + private final Map metrics; @JsonCreator public Metrics(@JsonProperty Map metrics) { @@ -17,4 +17,9 @@ public Map getMetrics() { return metrics; } + @Override + public String toString() { + return "Metrics [metrics=" + metrics + "]"; + } + } diff --git a/api/src/main/java/org/zalando/nakadi/client/java/model/Partition.java b/api/src/main/java/org/zalando/nakadi/client/java/model/Partition.java index 9813b09..d797818 100644 --- a/api/src/main/java/org/zalando/nakadi/client/java/model/Partition.java +++ b/api/src/main/java/org/zalando/nakadi/client/java/model/Partition.java @@ -4,9 +4,8 @@ import com.fasterxml.jackson.annotation.JsonProperty; /** - * Partition information. Can be helpful when trying to start a stream using an - * unmanaged API. This information is not related to the state of the consumer - * clients. + * Partition information. Can be helpful when trying to start a stream using an unmanaged API. This information is not + * related to the state of the consumer clients. */ public class Partition { private final String partition; @@ -14,30 +13,19 @@ public class Partition { private final String newestAvailableOffset; /** - * Partition information. Can be helpful when trying to start a stream using - * an unmanaged API. This information is not related to the state of the - * consumer clients. + * Partition information. Can be helpful when trying to start a stream using an unmanaged API. This information is + * not related to the state of the consumer clients. * * @param partition * The partition's id. * @param oldestAvailableOffset * An offset of the oldest available Event in that partition. - * This value will be changing upon removal of Events from the - * partition by the background archiving/cleanup mechanism. * @param newestAvailableOffset * An offset of the newest available Event in that partition. - * This value will be changing upon reception of new events for - * this partition by Nakadi. This value can be used to construct - * a cursor when opening streams (see `GET - * /event-type/{name}/events` for details). Might assume the - * special name BEGIN, meaning a pointer to the offset of the - * oldest available event in the partition. */ @JsonCreator - public Partition( - @JsonProperty("partition") String partition, - @JsonProperty("oldest_available_offset")String oldestAvailableOffset, - @JsonProperty("newest_available_offset")String newestAvailableOffset) { + public Partition(@JsonProperty("partition") String partition, @JsonProperty("oldest_available_offset") String oldestAvailableOffset, + @JsonProperty("newest_available_offset") String newestAvailableOffset) { this.partition = partition; this.oldestAvailableOffset = oldestAvailableOffset; this.newestAvailableOffset = newestAvailableOffset; @@ -55,4 +43,46 @@ public String getNewestAvailableOffset() { return newestAvailableOffset; } + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((newestAvailableOffset == null) ? 0 : newestAvailableOffset.hashCode()); + result = prime * result + ((oldestAvailableOffset == null) ? 0 : oldestAvailableOffset.hashCode()); + result = prime * result + ((partition == null) ? 0 : partition.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Partition other = (Partition) obj; + if (newestAvailableOffset == null) { + if (other.newestAvailableOffset != null) + return false; + } else if (!newestAvailableOffset.equals(other.newestAvailableOffset)) + return false; + if (oldestAvailableOffset == null) { + if (other.oldestAvailableOffset != null) + return false; + } else if (!oldestAvailableOffset.equals(other.oldestAvailableOffset)) + return false; + if (partition == null) { + if (other.partition != null) + return false; + } else if (!partition.equals(other.partition)) + return false; + return true; + } + + @Override + public String toString() { + return "Partition [partition=" + partition + ", oldestAvailableOffset=" + oldestAvailableOffset + ", newestAvailableOffset=" + newestAvailableOffset + "]"; + } + } diff --git a/api/src/main/java/org/zalando/nakadi/client/java/model/Problem.java b/api/src/main/java/org/zalando/nakadi/client/java/model/Problem.java index 2fc5be6..49da60d 100644 --- a/api/src/main/java/org/zalando/nakadi/client/java/model/Problem.java +++ b/api/src/main/java/org/zalando/nakadi/client/java/model/Problem.java @@ -3,49 +3,62 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; +/** + * Represents a Poblem Type. + * + */ public class Problem { private final String type; private final String title; private final Integer status; private final String detail; private final String instance; - + /** - * @ param type An absolute URI that identifies the problem type. When dereferenced, it SHOULD provide human-readable API documentation for the problem type (e.g., using HTML). This Problem object is the same as provided by https://github.com/zalando/problem - * @ param title A short, summary of the problem type. Written in English and readable for engineers (usually not suited for non technical stakeholders and not localized) - * @ param status The HTTP status code generated by the origin server for this occurrence of the problem. - * @ param detail A human readable explanation specific to this occurrence of the problem. - * @ param instance An absolute URI that identifies the specific occurrence of the problem. It may or may not yield further information if dereferenced. + * @param type + * Identifies the problem type. + * @param title + * A short, summary of the problem type. + * @param status + * The HTTP status code generated by the origin server for this occurrence of the problem. + * @param detail + * A human readable explanation specific to this occurrence of the problem. + * @param instance + * An absolute URI that identifies the specific occurrence of the problem. */ @JsonCreator - public Problem( - @JsonProperty("type") String type, - @JsonProperty("title") String title, - @JsonProperty("status") Integer status, - @JsonProperty("detail") String detail, - @JsonProperty("instance") String instance) { + public Problem(@JsonProperty("type") String type, @JsonProperty("title") String title, @JsonProperty("status") Integer status, @JsonProperty("detail") String detail, + @JsonProperty("instance") String instance) { this.type = type; this.title = title; this.status = status; this.detail = detail; this.instance = instance; } + public String getType() { return type; } + public String getTitle() { return title; } + public Integer getStatus() { return status; } + public String getDetail() { return detail; } + public String getInstance() { return instance; } - - - + + @Override + public String toString() { + return "Problem [type=" + type + ", title=" + title + ", status=" + status + ", detail=" + detail + ", instance=" + instance + "]"; + } + } diff --git a/api/src/main/scala/org/zalando/nakadi/client/scala/Client.scala b/api/src/main/scala/org/zalando/nakadi/client/scala/Client.scala index 04bcc7d..ecb0dd3 100644 --- a/api/src/main/scala/org/zalando/nakadi/client/scala/Client.scala +++ b/api/src/main/scala/org/zalando/nakadi/client/scala/Client.scala @@ -98,15 +98,6 @@ trait Client { */ def getPartitions(eventTypeName: String): Future[Either[ClientError, Option[Seq[Partition]]]] - /** - * Returns all of the validation strategies supported by this installation of Nakadi. - * - * {{{ - * curl --request GET /registry/validation-strategies - * }}} - */ - def getValidationStrategies(): Future[Either[ClientError, Option[Seq[EventValidationStrategy.Value]]]] - /** * Returns all of the enrichment strategies supported by this installation of Nakadi. * {{{ diff --git a/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java b/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java index 3035db6..da87992 100644 --- a/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java +++ b/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java @@ -96,11 +96,6 @@ public Future>> getPartitions(String eventTypeName) { return handler.get(Uri.getPartitions(eventTypeName), seqOfPartitionDeserializer); } - @Override - public Future>> getValidationStrategies() { - return handler.get(Uri.URI_VALIDATION_STRATEGIES(), seqOfEventValidationStrategy); - } - @Override public Future>> getEnrichmentStrategies() { return handler.get(Uri.URI_ENRICHMENT_STRATEGIES(), seqOfEventEnrichmentStrategy); diff --git a/client/src/test/resources/application.conf b/client/src/test/resources/application.conf deleted file mode 100644 index e7b40a1..0000000 --- a/client/src/test/resources/application.conf +++ /dev/null @@ -1,29 +0,0 @@ -akka { - loglevel = "DEBUG" - stdout-loglevel = "DEBUG" - - actor { - debug { - # enable DEBUG logging of actor lifecycle changes - lifecycle = on - unhandled = on - } - } - - cluster { - seed-nodes = [ - "akka://ClusterSystem@127.0.0.1:2551", - "akka://ClusterSystem@127.0.0.1:2552"] - - auto-down = on - } -} - -akka.cluster.metrics.enabled=off - -# Enable metrics extension in akka-cluster-metrics. -#akka.extensions=["akka.cluster.metrics.ClusterMetricsExtension"] - -# Sigar native library extract location during tests. -# Note: use per-jvm-instance folder when running multiple jvm on one host. -#akka.cluster.metrics.native-library-extract-folder=${user.dir}/target/native diff --git a/it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/EventIntegrationHelper.scala b/it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/EventIntegrationHelper.scala index 7afefa2..eaed568 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/EventIntegrationHelper.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/EventIntegrationHelper.scala @@ -65,7 +65,6 @@ class EventIntegrationHelper(generator: EventGenerator, client: Client) extends eventTypes } - def getValidationStrategies(): Seq[EventValidationStrategy.Value] = extractFromRightOptional(() => client.getValidationStrategies()) def getEnrichmentStrategies(): Seq[EventEnrichmentStrategy.Value] = extractFromRightOptional(() => client.getEnrichmentStrategies()) def getPartitionStrategies(): Seq[PartitionStrategy.Value] = extractFromRightOptional(() => client.getPartitioningStrategies()) diff --git a/it/src/test/scala/org/zalando/nakadi/client/scala/SimpleEventTest.scala b/it/src/test/scala/org/zalando/nakadi/client/scala/SimpleEventTest.scala index 428dc21..1c093f0 100644 --- a/it/src/test/scala/org/zalando/nakadi/client/scala/SimpleEventTest.scala +++ b/it/src/test/scala/org/zalando/nakadi/client/scala/SimpleEventTest.scala @@ -11,6 +11,7 @@ import org.zalando.nakadi.client.scala.test.factory.events.SimpleEventListener import org.zalando.nakadi.client.scala.model.PartitionStrategy import org.scalatest.BeforeAndAfterAll import org.zalando.nakadi.client.scala.model.EventValidationStrategy +import org.zalando.nakadi.client.scala.model.EventEnrichmentStrategy class SimpleEventTest extends WordSpec with Matchers with BeforeAndAfterAll { @@ -143,25 +144,15 @@ class SimpleEventTest extends WordSpec with Matchers with BeforeAndAfterAll { result should contain(PartitionStrategy.RANDOM) result should contain(PartitionStrategy.USER_DEFINED) } - "Receive validation-strategies successfully" ignore { - val eventGenerator = new DefaultMySimpleEventGenerator() { - def eventTypeId = s"SimpleEventIntegrationTest-Receive-validation-strategies-successfully" - } - val it = new EventIntegrationHelper(eventGenerator, client) - val result = it.getValidationStrategies() - result.size shouldBe 0 //NOT IMPLEMENTED - } "Receive enrichment-strategies successfully" ignore { val eventGenerator = new DefaultMySimpleEventGenerator() { def eventTypeId = s"SimpleEventIntegrationTest-Receive-enrichment-strategies-successfully" } val it = new EventIntegrationHelper(eventGenerator, client) - val result = it.getValidationStrategies() + val result = it.getEnrichmentStrategies() result.size shouldBe 1 - - result should contain(EventValidationStrategy.SCHEMA_VALIDATION) - + result should contain(EventEnrichmentStrategy.METADATA) } } From 06613892c55107b0b1e3bcc6253a5d96db07efae Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Thu, 23 Jun 2016 14:38:40 +0200 Subject: [PATCH 102/183] techjira:LAAS-60 Removed ValidationStrategies. Fixes #75 --- .../enumerator/EventValidationStrategy.java | 31 --------- .../nakadi/client/java/model/EventType.java | 66 ++++++------------- .../nakadi/client/scala/model/Model.scala | 39 ++++------- .../nakadi/client/java/ClientImpl.java | 2 - .../client/java/utils/SerializationUtils.java | 5 -- .../model/JavaJacksonJsonMarshaller.scala | 2 - .../nakadi/client/scala/ClientImpl.scala | 5 -- .../model/ScalaJacksonJsonMarshaller.scala | 2 - .../scala/SerializerDeserializerTest.scala | 5 +- .../nakadi/client/utils/TestScalaEntity.scala | 23 ++++++- .../examples/java/EventCreationExample.java | 4 -- .../java/test/factory/EventGenerator.java | 3 - .../test/factory/EventGeneratorBuilder.java | 18 +---- .../examples/scala/EventCreationExample.scala | 2 - .../nakadi/client/scala/TestFactory.scala | 2 +- .../scala/test/factory/EventGenerator.scala | 6 -- .../zalando/client/java/SimpleEventTest.java | 5 +- .../nakadi/client/scala/SimpleEventTest.scala | 2 - 18 files changed, 58 insertions(+), 164 deletions(-) delete mode 100644 api/src/main/java/org/zalando/nakadi/client/java/enumerator/EventValidationStrategy.java diff --git a/api/src/main/java/org/zalando/nakadi/client/java/enumerator/EventValidationStrategy.java b/api/src/main/java/org/zalando/nakadi/client/java/enumerator/EventValidationStrategy.java deleted file mode 100644 index cf901f4..0000000 --- a/api/src/main/java/org/zalando/nakadi/client/java/enumerator/EventValidationStrategy.java +++ /dev/null @@ -1,31 +0,0 @@ -package org.zalando.nakadi.client.java.enumerator; - -import java.util.Optional; - -import com.fasterxml.jackson.annotation.JsonValue; - -/** - * Defines a rule for validation of an incoming Event. Rules might require additional parameters; see the `doc` field of - * the existing rules for details. See GET /registry/validation-strategies for a list of available rules. - */ -public enum EventValidationStrategy { - SCHEMA_VALIDATION("schema-validation"); - private final String strategy; - - private EventValidationStrategy(String strategy) { - this.strategy = strategy; - } - - @JsonValue - public String getStrategy() { - return strategy; - } - - public static Optional withName(String code) { - for (EventValidationStrategy e : EventValidationStrategy.values()) { - if (e != null && e.strategy.equals(code)) - return Optional.of(e); - } - return Optional.empty(); - } -} diff --git a/api/src/main/java/org/zalando/nakadi/client/java/model/EventType.java b/api/src/main/java/org/zalando/nakadi/client/java/model/EventType.java index b3c2f2e..43a03f1 100644 --- a/api/src/main/java/org/zalando/nakadi/client/java/model/EventType.java +++ b/api/src/main/java/org/zalando/nakadi/client/java/model/EventType.java @@ -1,12 +1,10 @@ package org.zalando.nakadi.client.java.model; -import java.util.Collection; import java.util.Collections; import java.util.List; import org.zalando.nakadi.client.java.enumerator.EventEnrichmentStrategy; import org.zalando.nakadi.client.java.enumerator.EventTypeCategory; -import org.zalando.nakadi.client.java.enumerator.EventValidationStrategy; import org.zalando.nakadi.client.java.enumerator.PartitionStrategy; import com.fasterxml.jackson.annotation.JsonCreator; @@ -21,7 +19,6 @@ public class EventType { private final String name; private final String owningApplication; private final EventTypeCategory category; - private final List validationStrategies; private final List enrichmentStrategies; private final PartitionStrategy partitionStrategy; private final EventTypeSchema schema; @@ -33,39 +30,12 @@ public class EventType { * An event type defines the schema and its runtime properties. * * @param name - * Name of this EventType. Encodes the owner/responsible for this EventType. The name for the EventType - * SHOULD follow the pattern, but is not enforced 'stups_owning_application.event-type', for example - * 'gizig.price-change'. The components of the name are: * Organization: the organizational unit where - * the team is located; can be omitted. * Team name: name of the team responsible for owning application; - * can be omitted. * Owning application: SHOULD match the field owning_application; indicates * EventType - * name: name of this EventType; SHOULD end in ChangeEvent for DataChangeEvents; MUST be in the past - * tense for BusinessEvents. (TBD: how to deal with organizational changes? Should be reflected on the - * name of the EventType? Isn't it better to omit [organization:team] completely?) * @param owningApplication * Indicator of the Application owning this `EventType`. * @param category - * Defines the category of this EventType. The value set will influence, if not set otherwise, the - * default set of validation-strategies, enrichment-strategies, and the effective_schema in the following - * way: - `undefined`: No predefined changes apply. `effective_schema` is exactly the same as the - * `EventTypeSchema`. Default validation_strategy for this `EventType` is `[{name: - * 'schema-validation'}]`. - `data`: Events of this category will be DataChangeEvents. `effective_schema` - * contains `metadata`, and adds fields `data_op` and `data_type`. The passed EventTypeSchema defines the - * schema of `data`. Default validation_strategy for this `EventType` is `[{name: - * 'datachange-schema-validation'}]`. - `business`: Events of this category will be BusinessEvents. - * `effective_schema` contains `metadata` and any additionally defined properties passed in the - * `EventTypeSchema` directly on top level of the Event. If name conflicts arise, creation of this - * EventType will be rejected. Default validation_strategy for this `EventType` is `[{name: - * 'schema-validation'}]`. - * @param validationStrategies - * Determines the validation that has to be executed upon reception of Events of this type. Events are - * rejected if any of the rules fail (see details of Problem response on the Event publishing methods). - * Rule evaluation order is the same as in this array. If not explicitly set, default value will respect - * the definition of the `EventType.category`. + * Defines the category of this EventType. * @param enrichmentStrategies - * Determines the enrichment to be performed on an Event upon reception. Enrichment is performed once - * upon reception (and after validation) of an Event and is only possible on fields that are not defined - * on the incoming Event. See documentation for the write operation for details on behaviour in case of - * unsuccessful enrichment. + * Determines the enrichment to be performed on an Event upon reception. * @param partitionStrategy * Determines how the assignment of the event to a Partition should be handled. * @param schema @@ -73,11 +43,10 @@ public class EventType { * might be added in the future). * @param dataKeyFields * Indicators of the path of the properties that constitute the primary key (identifier) of the data - * within this Event. If set MUST be a valid required field as defined in the schema. (TBD should be - * required? Is applicable only to both Business and DataChange Events?) + * within this Event. * @param partitioningKeyFields * Indicator of the field used for guaranteeing the ordering of Events of this type (used by the - * PartitionResolutionStrategy). If set MUST be a valid required field as defined in the schema. + * PartitionResolutionStrategy). * @param statistics * Statistics of this EventType used for optimization purposes. Internal use of these values might change * over time. (TBD: measured statistics). @@ -85,13 +54,12 @@ public class EventType { */ @JsonCreator public EventType(@JsonProperty("name") String name, @JsonProperty("owning_application") String owningApplication, @JsonProperty("category") EventTypeCategory category, - @JsonProperty("validation_strategies") List validationStrategies, @JsonProperty("enrichment_strategies") List enrichmentStrategies, + @JsonProperty("enrichment_strategies") List enrichmentStrategies, @JsonProperty("partition_strategy") PartitionStrategy partitionStrategy, @JsonProperty("schema") EventTypeSchema schema, @JsonProperty("data_key_fields") List dataKeyFields, @JsonProperty("partition_key_fields") List partitionKeyFields, @JsonProperty("default_statistics") EventTypeStatistics statistics) { this.name = name; this.owningApplication = owningApplication; this.category = category; - this.validationStrategies = validationStrategies; this.enrichmentStrategies = enrichmentStrategies; this.partitionStrategy = partitionStrategy; this.schema = schema; @@ -100,6 +68,15 @@ public EventType(@JsonProperty("name") String name, @JsonProperty("owning_applic this.statistics = statistics; } + private List unmodifiableList(List in) { + + if (in == null || in.isEmpty()) { + return Collections.emptyList(); + } else { + return Collections.unmodifiableList(in); + } + } + public String getName() { return name; } @@ -112,12 +89,9 @@ public EventTypeCategory getCategory() { return category; } - public List getValidationStrategies() { - return Collections.unmodifiableList(validationStrategies); - } public List getEnrichmentStrategies() { - return Collections.unmodifiableList(enrichmentStrategies); + return unmodifiableList(enrichmentStrategies); } public PartitionStrategy getPartitionStrategy() { @@ -129,11 +103,11 @@ public EventTypeSchema getSchema() { } public List getDataKeyFields() { - return Collections.unmodifiableList(dataKeyFields); + return unmodifiableList(dataKeyFields); } public List getPartitionKeyFields() { - return Collections.unmodifiableList(partitionKeyFields); + return unmodifiableList(partitionKeyFields); } public EventTypeStatistics getStatistics() { @@ -142,9 +116,9 @@ public EventTypeStatistics getStatistics() { @Override public String toString() { - return "EventType [name=" + name + ", owningApplication=" + owningApplication + ", category=" + category + ", validationStrategies=" + validationStrategies + ", enrichmentStrategies=" - + enrichmentStrategies + ", partitionStrategy=" + partitionStrategy + ", schema=" + schema + ", dataKeyFields=" + dataKeyFields + ", partitionKeyFields=" + partitionKeyFields - + ", statistics=" + statistics + "]"; + return "EventType [name=" + name + ", owningApplication=" + owningApplication + ", category=" + category + ", enrichmentStrategies=" + enrichmentStrategies + ", partitionStrategy=" + + partitionStrategy + ", schema=" + schema + ", dataKeyFields=" + dataKeyFields + ", partitionKeyFields=" + partitionKeyFields + ", statistics=" + statistics + "]"; } + } diff --git a/api/src/main/scala/org/zalando/nakadi/client/scala/model/Model.scala b/api/src/main/scala/org/zalando/nakadi/client/scala/model/Model.scala index 98eb061..3d12ea4 100644 --- a/api/src/main/scala/org/zalando/nakadi/client/scala/model/Model.scala +++ b/api/src/main/scala/org/zalando/nakadi/client/scala/model/Model.scala @@ -7,8 +7,6 @@ import com.fasterxml.jackson.core.`type`.TypeReference // Updated untill commit 57cace5 - - /** * The Event definition will be externalized in future versions of this document. * A basic payload of an Event. The actual schema is dependent on the information configured for the @@ -47,7 +45,7 @@ case class Cursor( */ case class EventMetadata( eid: String, - @JsonProperty("event_type") eventTypeName: Option[String], + @JsonProperty("event_type") eventTypeName: Option[String], occurredAt: String, receivedAt: Option[String], parentEids: Seq[String], @@ -82,8 +80,7 @@ trait DataChangeEventQualifier { case class DataChangeEvent[T]( data: T, dataType: String, - @JsonProperty("data_op") - @JsonScalaEnumeration(classOf[DataOperationType]) dataOperation: DataOperation.Value, + @JsonProperty("data_op")@JsonScalaEnumeration(classOf[DataOperationType]) dataOperation: DataOperation.Value, metadata: Option[EventMetadata]) extends DataChangeEventQualifier with Event /** @@ -94,8 +91,7 @@ case class DataChangeEvent[T]( * @ param instance An absolute URI that identifies the specific occurrence of the problem. It may or may not yield further information if dereferenced. */ case class Problem( - @JsonProperty("type") - problemType: String, + @JsonProperty("type") problemType: String, title: String, status: Integer, detail: Option[String], @@ -126,29 +122,27 @@ case class EventStreamBatch[T <: Event]( /** * An event type defines the schema and its runtime properties. - * @param name Name of this EventType. Encodes the owner/responsible for this EventType. The name for the EventType SHOULD follow the pattern, but is not enforced 'stups_owning_application.event-type', for example 'gizig.price-change'. The components of the name are: * Organization: the organizational unit where the team is located; can be omitted. * Team name: name of the team responsible for owning application; can be omitted. * Owning application: SHOULD match the field owning_application; indicates * EventType name: name of this EventType; SHOULD end in ChangeEvent for DataChangeEvents; MUST be in the past tense for BusinessEvents. (TBD: how to deal with organizational changes? Should be reflected on the name of the EventType? Isn't it better to omit [organization:team] completely?) + * @param name Name of this EventType. Encodes the owner/responsible for this EventType. * @param owningApplication Indicator of the Application owning this `EventType`. - * @param category Defines the category of this EventType. The value set will influence, if not set otherwise, the default set of validation-strategies, enrichment-strategies, and the effective_schema in the following way: - `undefined`: No predefined changes apply. `effective_schema` is exactly the same as the `EventTypeSchema`. Default validation_strategy for this `EventType` is `[{name: 'schema-validation'}]`. - `data`: Events of this category will be DataChangeEvents. `effective_schema` contains `metadata`, and adds fields `data_op` and `data_type`. The passed EventTypeSchema defines the schema of `data`. Default validation_strategy for this `EventType` is `[{name: 'datachange-schema-validation'}]`. - `business`: Events of this category will be BusinessEvents. `effective_schema` contains `metadata` and any additionally defined properties passed in the `EventTypeSchema` directly on top level of the Event. If name conflicts arise, creation of this EventType will be rejected. Default validation_strategy for this `EventType` is `[{name: 'schema-validation'}]`. - * @param validationStrategies Determines the validation that has to be executed upon reception of Events of this type. Events are rejected if any of the rules fail (see details of Problem response on the Event publishing methods). Rule evaluation order is the same as in this array. If not explicitly set, default value will respect the definition of the `EventType.category`. - * @param enrichmentStrategies Determines the enrichment to be performed on an Event upon reception. Enrichment is performed once upon reception (and after validation) of an Event and is only possible on fields that are not defined on the incoming Event. See documentation for the write operation for details on behaviour in case of unsuccessful enrichment. + * @param category Defines the category of this EventType. + * @param enrichmentStrategies Determines the enrichment to be performed on an Event upon reception. * @param partitionStrategy Determines how the assignment of the event to a Partition should be handled. - * @param schema The schema for this EventType. This is expected to be a json schema in yaml format (other formats might be added in the future). - * @param dataKeyFields Indicators of the path of the properties that constitute the primary key (identifier) of the data within this Event. If set MUST be a valid required field as defined in the schema. (TBD should be required? Is applicable only to both Business and DataChange Events?) - * @param partitionKeyFields Indicator of the field used for guaranteeing the ordering of Events of this type (used by the PartitionResolutionStrategy). If set MUST be a valid required field as defined in the schema. - * @param statistics Statistics of this EventType used for optimization purposes. Internal use of these values might change over time. (TBD: measured statistics). + * @param schema The schema for this EventType. + * @param dataKeyFields Indicators of the path of the properties that constitute the primary key (identifier) of the data within this Event. + * @param partitionKeyFields Indicator of the field used for guaranteeing the ordering of Events of this type (used by the PartitionResolutionStrategy). + * @param statistics Statistics of this EventType used for optimization purposes. * */ case class EventType( name: String, owningApplication: String, @JsonScalaEnumeration(classOf[EventTypeCategoryType]) category: EventTypeCategory.Value, - @JsonScalaEnumeration(classOf[EventValidationStrategyType]) validationStrategies: Seq[EventValidationStrategy.Value], @JsonScalaEnumeration(classOf[EventEnrichmentStrategyType]) enrichmentStrategies: Seq[EventEnrichmentStrategy.Value], @JsonScalaEnumeration(classOf[PartitionStrategyType]) partitionStrategy: Option[PartitionStrategy.Value], schema: EventTypeSchema, dataKeyFields: Seq[String], partitionKeyFields: Seq[String], - @JsonProperty("default_statistics")statistics: Option[EventTypeStatistics]) + @JsonProperty("default_statistics") statistics: Option[EventTypeStatistics]) /** * The schema for an EventType, expected to be a json schema in yaml @@ -158,7 +152,7 @@ case class EventType( * Failure to respect the syntax will fail any operation on an EventType. */ case class EventTypeSchema( - @JsonProperty("type")@JsonScalaEnumeration(classOf[SchemaTypeType]) schemaType: SchemaType.Value, + @JsonProperty("type")@JsonScalaEnumeration(classOf[SchemaTypeType]) schemaType: SchemaType.Value, schema: String) /** @@ -176,15 +170,6 @@ case class EventTypeStatistics( readParallelism: Integer, writeParallelism: Integer) -/** - * Defines a rule for validation of an incoming Event. Rules might require additional parameters; see the `doc` field of the existing rules for details. See GET /registry/validation-strategies for a list of available rules. - */ -case object EventValidationStrategy extends Enumeration { - type EventValidationStrategy = Value - val SCHEMA_VALIDATION = Value("schema-validation") -} -class EventValidationStrategyType extends TypeReference[EventValidationStrategy.type] - /** * Defines a rule for the resolution of incoming Events into partitions. Rules might require additional parameters; see the `doc` field of the existing rules for details. See `GET /registry/partition-strategies` for a list of available rules. */ diff --git a/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java b/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java index da87992..204bd88 100644 --- a/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java +++ b/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java @@ -8,7 +8,6 @@ import org.zalando.nakadi.client.Deserializer; import org.zalando.nakadi.client.Serializer; import org.zalando.nakadi.client.java.enumerator.EventEnrichmentStrategy; -import org.zalando.nakadi.client.java.enumerator.EventValidationStrategy; import org.zalando.nakadi.client.java.enumerator.PartitionStrategy; import org.zalando.nakadi.client.java.model.Event; import org.zalando.nakadi.client.java.model.EventStreamBatch; @@ -30,7 +29,6 @@ public class ClientImpl implements Client { // List Deserializers private final Deserializer> seqOfEventTypeDeserializer = SerializationUtils.seqOfEventTypeDeserializer(); private final Deserializer> seqOfPartitionDeserializer = SerializationUtils.seqOfPartitionDeserializer(); - private final Deserializer> seqOfEventValidationStrategy = SerializationUtils.seqOfEventValidationStrategy(); private final Deserializer> seqOfEventEnrichmentStrategy = SerializationUtils.seqOfEventEnrichmentStrategy(); private final Deserializer> seqOfPartitionStrategy = SerializationUtils.seqOfPartitionStrategy(); // Serializers diff --git a/client/src/main/java/org/zalando/nakadi/client/java/utils/SerializationUtils.java b/client/src/main/java/org/zalando/nakadi/client/java/utils/SerializationUtils.java index 8d32370..195e15a 100644 --- a/client/src/main/java/org/zalando/nakadi/client/java/utils/SerializationUtils.java +++ b/client/src/main/java/org/zalando/nakadi/client/java/utils/SerializationUtils.java @@ -5,7 +5,6 @@ import org.zalando.nakadi.client.Deserializer; import org.zalando.nakadi.client.Serializer; import org.zalando.nakadi.client.java.enumerator.EventEnrichmentStrategy; -import org.zalando.nakadi.client.java.enumerator.EventValidationStrategy; import org.zalando.nakadi.client.java.enumerator.PartitionStrategy; import org.zalando.nakadi.client.java.model.EventType; import org.zalando.nakadi.client.java.model.JavaJacksonJsonMarshaller; @@ -52,10 +51,6 @@ public static Deserializer> seqOfPartitionDeserializer() { return JavaJacksonJsonMarshaller.deserializer(JavaJacksonJsonMarshaller.listOfPartitionTR()); } - public static Deserializer> seqOfEventValidationStrategy() { - return JavaJacksonJsonMarshaller.deserializer(JavaJacksonJsonMarshaller.listOfEventValidationStrategyTR()); - } - public static Deserializer> seqOfEventEnrichmentStrategy() { return JavaJacksonJsonMarshaller.deserializer(JavaJacksonJsonMarshaller.listOfEventEnrichmentStrategyTR()); } diff --git a/client/src/main/scala/org/zalando/nakadi/client/java/model/JavaJacksonJsonMarshaller.scala b/client/src/main/scala/org/zalando/nakadi/client/java/model/JavaJacksonJsonMarshaller.scala index b4ed63e..34176b8 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/java/model/JavaJacksonJsonMarshaller.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/java/model/JavaJacksonJsonMarshaller.scala @@ -28,7 +28,6 @@ object JavaJacksonJsonMarshaller { def partitionTR: TypeReference[Partition] = new TypeReference[Partition] {} def cursorTR: TypeReference[Cursor] = new TypeReference[Cursor] {} def eventTypeSchemaTR: TypeReference[EventTypeSchema] = new TypeReference[EventTypeSchema] {} - def eventValidationStrategyTR: TypeReference[EventValidationStrategy] = new TypeReference[EventValidationStrategy] {} def partitionResolutionStrategyTR: TypeReference[PartitionStrategy] = new TypeReference[PartitionStrategy] {} def eventEnrichmentStrategyTR: TypeReference[EventEnrichmentStrategy] = new TypeReference[EventEnrichmentStrategy] {} def dataChangeEventQualifierTR: TypeReference[DataChangeEventQualifier] = new TypeReference[DataChangeEventQualifier] {} @@ -44,7 +43,6 @@ object JavaJacksonJsonMarshaller { //Lists def listOfPartitionStrategyTR: TypeReference[java.util.List[PartitionStrategy]] = new TypeReference[java.util.List[PartitionStrategy]] {} - def listOfEventValidationStrategyTR: TypeReference[java.util.List[EventValidationStrategy]] = new TypeReference[java.util.List[EventValidationStrategy]] {} def listOfEventEnrichmentStrategyTR: TypeReference[java.util.List[EventEnrichmentStrategy]] = new TypeReference[java.util.List[EventEnrichmentStrategy]] {} def listOfEventTypeTR: TypeReference[java.util.List[EventType]] = new TypeReference[java.util.List[EventType]] {} def listOfPartitionTR: TypeReference[java.util.List[Partition]] = new TypeReference[java.util.List[Partition]] {} diff --git a/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala index 7bb5f66..b1d9565 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala @@ -16,7 +16,6 @@ import org.zalando.nakadi.client.scala.model.Event import org.zalando.nakadi.client.scala.model.EventEnrichmentStrategy import org.zalando.nakadi.client.scala.model.EventStreamBatch import org.zalando.nakadi.client.scala.model.EventType -import org.zalando.nakadi.client.scala.model.EventValidationStrategy import org.zalando.nakadi.client.scala.model.JacksonJsonMarshaller import org.zalando.nakadi.client.scala.model.Metrics import org.zalando.nakadi.client.scala.model.Partition @@ -74,10 +73,6 @@ class ClientImpl(connection: Connection, subscriber: SubscriptionHandler, charSe logFutureEither(connection.get(URI_PARTITIONS_BY_EVENT_TYPE.format(name)).flatMap(in => mapToEither(in)(deserializer(listOfPartitionTR)))) } - def getValidationStrategies(): Future[Either[ClientError, Option[Seq[EventValidationStrategy.Value]]]] = { - logFutureEither(connection.get(URI_VALIDATION_STRATEGIES).flatMap(mapToEither(_)(deserializer(listOfEventValidationStrategyTR)))) - } - def getEnrichmentStrategies(): Future[Either[ClientError, Option[Seq[EventEnrichmentStrategy.Value]]]] = { logFutureEither(connection.get(URI_ENRICHMENT_STRATEGIES).flatMap(mapToEither(_)(deserializer(listOfEventEnrichmentStrategyTR)))) } diff --git a/client/src/main/scala/org/zalando/nakadi/client/scala/model/ScalaJacksonJsonMarshaller.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/model/ScalaJacksonJsonMarshaller.scala index d49ed94..cb5351c 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/scala/model/ScalaJacksonJsonMarshaller.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/scala/model/ScalaJacksonJsonMarshaller.scala @@ -31,7 +31,6 @@ object JacksonJsonMarshaller { implicit def partitionTR: TypeReference[Partition] = new TypeReference[Partition] {} implicit def cursorTR: TypeReference[Cursor] = new TypeReference[Cursor] {} implicit def eventTypeSchemaTR: TypeReference[EventTypeSchema] = new TypeReference[EventTypeSchema] {} - implicit def eventValidationStrategyTR: TypeReference[EventValidationStrategy.Value] = new TypeReference[EventValidationStrategy.Value] {} implicit def partitionResolutionStrategyTR: TypeReference[PartitionStrategy.Value] = new TypeReference[PartitionStrategy.Value] {} implicit def eventEnrichmentStrategyTR: TypeReference[EventEnrichmentStrategy.Value] = new TypeReference[EventEnrichmentStrategy.Value] {} implicit def dataChangeEventQualifierTR: TypeReference[DataChangeEventQualifier] = new TypeReference[DataChangeEventQualifier] {} @@ -47,7 +46,6 @@ object JacksonJsonMarshaller { //Lists implicit def listOfPartitionStrategyTR: TypeReference[Seq[PartitionStrategy.Value]] = new TypeReference[Seq[PartitionStrategy.Value]] {} - implicit def listOfEventValidationStrategyTR: TypeReference[Seq[EventValidationStrategy.Value]] = new TypeReference[Seq[EventValidationStrategy.Value]] {} implicit def listOfEventEnrichmentStrategyTR: TypeReference[Seq[EventEnrichmentStrategy.Value]] = new TypeReference[Seq[EventEnrichmentStrategy.Value]] {} implicit def listOfEventTypeTR: TypeReference[Seq[EventType]] = new TypeReference[Seq[EventType]] {} implicit def listOfPartitionTR: TypeReference[Seq[Partition]] = new TypeReference[Seq[Partition]] {} diff --git a/client/src/test/scala/org/zalando/nakadi/client/scala/SerializerDeserializerTest.scala b/client/src/test/scala/org/zalando/nakadi/client/scala/SerializerDeserializerTest.scala index aee1798..a9fb64e 100644 --- a/client/src/test/scala/org/zalando/nakadi/client/scala/SerializerDeserializerTest.scala +++ b/client/src/test/scala/org/zalando/nakadi/client/scala/SerializerDeserializerTest.scala @@ -14,7 +14,7 @@ import org.zalando.nakadi.client.Deserializer import org.zalando.nakadi.client.Serializer /** - * Tests the Marshalling and Umarshalling of the same object in a single run. It tests in this sequence: 1.Marshall and 2.Unmarshall.
          + * Tests the Marshalling and Umarshalling of the same json object. It tests in this sequence: 1.Marshall and 2.Unmarshall.
          * This is just a simple test that should break when Custom * Marshallers/Unmarshallers are used and produce different * unexpected results. @@ -48,9 +48,6 @@ import JacksonJsonMarshaller._ s"$testName(eventTypeSchema)" in { checkSerializationDeserializationProcess("eventTypeSchema", eventTypeSchema) } - s"$testName(eventValidationStrategy)" in { - checkSerializationDeserializationProcess("eventValidationStrategy", eventValidationStrategy) - } s"$testName(partitionResolutionStrategy)" in { checkSerializationDeserializationProcess("partitionResolutionStrategy", partitionResolutionStrategy) } diff --git a/client/src/test/scala/org/zalando/nakadi/client/utils/TestScalaEntity.scala b/client/src/test/scala/org/zalando/nakadi/client/utils/TestScalaEntity.scala index 7d8a677..7a90871 100644 --- a/client/src/test/scala/org/zalando/nakadi/client/utils/TestScalaEntity.scala +++ b/client/src/test/scala/org/zalando/nakadi/client/utils/TestScalaEntity.scala @@ -1,6 +1,24 @@ package org.zalando.nakadi.client.utils -import org.zalando.nakadi.client.scala.model._ +import org.zalando.nakadi.client.scala.model.BatchItemPublishingStatus +import org.zalando.nakadi.client.scala.model.BatchItemResponse +import org.zalando.nakadi.client.scala.model.BatchItemStep +import org.zalando.nakadi.client.scala.model.Cursor +import org.zalando.nakadi.client.scala.model.DataChangeEvent +import org.zalando.nakadi.client.scala.model.DataOperation +import org.zalando.nakadi.client.scala.model.Event +import org.zalando.nakadi.client.scala.model.EventEnrichmentStrategy +import org.zalando.nakadi.client.scala.model.EventMetadata +import org.zalando.nakadi.client.scala.model.EventStreamBatch +import org.zalando.nakadi.client.scala.model.EventType +import org.zalando.nakadi.client.scala.model.EventTypeCategory +import org.zalando.nakadi.client.scala.model.EventTypeSchema +import org.zalando.nakadi.client.scala.model.EventTypeStatistics +import org.zalando.nakadi.client.scala.model.Metrics +import org.zalando.nakadi.client.scala.model.Partition +import org.zalando.nakadi.client.scala.model.PartitionStrategy +import org.zalando.nakadi.client.scala.model.Problem +import org.zalando.nakadi.client.scala.model.SchemaType object TestScalaEntity { // @@ -10,14 +28,13 @@ object TestScalaEntity { val partition = new Partition("0", "132", "4423") val cursor = new Cursor("0", "120") val eventTypeSchema = new EventTypeSchema(SchemaType.JSON, "schema") - val eventValidationStrategy = EventValidationStrategy.SCHEMA_VALIDATION val partitionResolutionStrategy = PartitionStrategy.HASH val eventEnrichmentStrategy = EventEnrichmentStrategy.METADATA //Complex objects val eventTypeStatistics = new EventTypeStatistics(9281002, 19283, 21, 312) val eventType = new EventType("name", "owner", - EventTypeCategory.BUSINESS, List(EventValidationStrategy.SCHEMA_VALIDATION) + EventTypeCategory.BUSINESS , List(EventEnrichmentStrategy.METADATA), Some(partitionResolutionStrategy), eventTypeSchema, List("dataKeyFields"), List("partitioningKeyFields"), Option(eventTypeStatistics)) diff --git a/it/src/main/java/org/zalando/nakadi/client/examples/java/EventCreationExample.java b/it/src/main/java/org/zalando/nakadi/client/examples/java/EventCreationExample.java index e14c9e1..cb1f2c4 100644 --- a/it/src/main/java/org/zalando/nakadi/client/examples/java/EventCreationExample.java +++ b/it/src/main/java/org/zalando/nakadi/client/examples/java/EventCreationExample.java @@ -8,7 +8,6 @@ import org.zalando.nakadi.client.java.Client; import org.zalando.nakadi.client.java.enumerator.EventEnrichmentStrategy; import org.zalando.nakadi.client.java.enumerator.EventTypeCategory; -import org.zalando.nakadi.client.java.enumerator.EventValidationStrategy; import org.zalando.nakadi.client.java.enumerator.PartitionStrategy; import org.zalando.nakadi.client.java.enumerator.SchemaType; import org.zalando.nakadi.client.java.model.Event; @@ -51,8 +50,6 @@ public EventType createEventType(String name, EventTypeSchema eventTypeSchema) { String owningApplication = "team-laas"; EventTypeCategory category = EventTypeCategory.UNDEFINED; - List validationStrategies = Lists - .newArrayList(); List enrichmentStrategies = Lists .newArrayList(); PartitionStrategy partitionStrategy = PartitionStrategy.RANDOM; @@ -63,7 +60,6 @@ public EventType createEventType(String name, return new EventType(name, // owningApplication, // category, // - validationStrategies, // enrichmentStrategies, // partitionStrategy, // eventTypeSchema, // diff --git a/it/src/main/java/org/zalando/nakadi/client/java/test/factory/EventGenerator.java b/it/src/main/java/org/zalando/nakadi/client/java/test/factory/EventGenerator.java index 67f0af9..281c868 100644 --- a/it/src/main/java/org/zalando/nakadi/client/java/test/factory/EventGenerator.java +++ b/it/src/main/java/org/zalando/nakadi/client/java/test/factory/EventGenerator.java @@ -4,7 +4,6 @@ import org.zalando.nakadi.client.java.enumerator.EventEnrichmentStrategy; import org.zalando.nakadi.client.java.enumerator.EventTypeCategory; -import org.zalando.nakadi.client.java.enumerator.EventValidationStrategy; import org.zalando.nakadi.client.java.enumerator.PartitionStrategy; import org.zalando.nakadi.client.java.model.Event; import org.zalando.nakadi.client.java.model.EventType; @@ -32,8 +31,6 @@ public interface EventGenerator { EventTypeCategory getCategory(); - List getValidationStrategies(); - List getEnrichmentStrategies(); PartitionStrategy getPartitionStrategy(); diff --git a/it/src/main/java/org/zalando/nakadi/client/java/test/factory/EventGeneratorBuilder.java b/it/src/main/java/org/zalando/nakadi/client/java/test/factory/EventGeneratorBuilder.java index de0fe7e..4b3d71f 100644 --- a/it/src/main/java/org/zalando/nakadi/client/java/test/factory/EventGeneratorBuilder.java +++ b/it/src/main/java/org/zalando/nakadi/client/java/test/factory/EventGeneratorBuilder.java @@ -5,7 +5,6 @@ import org.zalando.nakadi.client.java.enumerator.EventEnrichmentStrategy; import org.zalando.nakadi.client.java.enumerator.EventTypeCategory; -import org.zalando.nakadi.client.java.enumerator.EventValidationStrategy; import org.zalando.nakadi.client.java.enumerator.PartitionStrategy; import org.zalando.nakadi.client.java.enumerator.SchemaType; import org.zalando.nakadi.client.java.model.Event; @@ -21,7 +20,6 @@ public abstract class EventGeneratorBuilder { private EventType eventType = null; private String owner = "Nakadi-klients(java-integration-test-suite)"; private EventTypeCategory category = EventTypeCategory.UNDEFINED; - private List validationStrategies = Collections.emptyList(); private List enrichmentStrategies = Collections.emptyList(); private PartitionStrategy partitionStrategy = PartitionStrategy.RANDOM; private EventTypeSchema schemaType; @@ -64,10 +62,7 @@ public EventGeneratorBuilder withCategory(EventTypeCategory category) { return this; } - public EventGeneratorBuilder withValidationStrategies(List validationStrategies) { - this.validationStrategies = validationStrategies; - return this; - } + public EventGeneratorBuilder withEnrichmentStrategies(List enrichmentStrategies) { this.enrichmentStrategies = enrichmentStrategies; @@ -133,7 +128,6 @@ public EventType getEventType() { return new EventType(gen.getEventTypeName(), // gen.getOwner(), // gen.getCategory(),// - gen.getValidationStrategies(), // gen.getEnrichmentStrategies(),// gen.getPartitionStrategy(), // gen.getSchemaType(), // @@ -152,11 +146,6 @@ public EventTypeCategory getCategory() { return category; } - @Override - public List getValidationStrategies() { - return validationStrategies; - } - @Override public List getEnrichmentStrategies() { return enrichmentStrategies; @@ -233,10 +222,7 @@ protected EventTypeCategory getCategory() { return category; } - protected List getValidationStrategies() { - return validationStrategies; - } - + protected List getEnrichmentStrategies() { return enrichmentStrategies; } diff --git a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala index 45f981f..ac1ed08 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala @@ -49,7 +49,6 @@ object EventCreationExample extends App { val owner = "team-laas" val category = EventTypeCategory.UNDEFINED // We want just to pass data without through Nakadi, simple schema-validation is enough! - val validationStrategies = Nil // Validation strategies are not defined yet! val enrichmentStrategies = Nil val partitionStrategy = Some(PartitionStrategy.RANDOM) val dataKeyFields = Nil @@ -58,7 +57,6 @@ object EventCreationExample extends App { val eventType = new EventType(eventTypeName, owner, category, - validationStrategies, enrichmentStrategies, partitionStrategy, eventTypeSchema, diff --git a/it/src/main/scala/org/zalando/nakadi/client/scala/TestFactory.scala b/it/src/main/scala/org/zalando/nakadi/client/scala/TestFactory.scala index b2978ef..6082669 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/scala/TestFactory.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/scala/TestFactory.scala @@ -60,7 +60,7 @@ trait ModelFactory { owningApplication: String = "nakadi-klients"): EventType = { new EventType(name, // owningApplication, // - EventTypeCategory.UNDEFINED, Nil, Nil, // + EventTypeCategory.UNDEFINED, Nil, // Some(PartitionStrategy.RANDOM), eventTypeSchema, // Nil, paritionKeyFields, None) } diff --git a/it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/EventGenerator.scala b/it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/EventGenerator.scala index 3deb32d..a7bcfa4 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/EventGenerator.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/EventGenerator.scala @@ -4,7 +4,6 @@ import org.zalando.nakadi.client.scala.model.Event import org.zalando.nakadi.client.scala.model.EventEnrichmentStrategy import org.zalando.nakadi.client.scala.model.EventType import org.zalando.nakadi.client.scala.model.EventTypeCategory -import org.zalando.nakadi.client.scala.model.EventValidationStrategy import org.zalando.nakadi.client.scala.model._ import org.zalando.nakadi.client.scala.model.EventTypeStatistics import org.zalando.nakadi.client.scala.model.EventTypeSchema @@ -50,7 +49,6 @@ trait EventGenerator { new EventType(eventTypeName, // owner, // category, // - validationStrategies, // enrichmentStrategies, // partitionStrategy, // schemaType, // @@ -72,10 +70,6 @@ trait EventGenerator { */ def category: EventTypeCategory.Value = EventTypeCategory.UNDEFINED - /** - * Returns the validationStrategies value. Default = Nil - */ - def validationStrategies: Seq[EventValidationStrategy.Value] = Nil /** * Returns the enrichmentStrategies value. Default = Nil */ diff --git a/it/src/test/java/org/zalando/client/java/SimpleEventTest.java b/it/src/test/java/org/zalando/client/java/SimpleEventTest.java index 1a2681a..432db48 100644 --- a/it/src/test/java/org/zalando/client/java/SimpleEventTest.java +++ b/it/src/test/java/org/zalando/client/java/SimpleEventTest.java @@ -41,7 +41,7 @@ public void shutdown() throws InterruptedException, ExecutionException { @Test public void handle404Graciously() throws InterruptedException, ExecutionException { - + EventGenerator gen = new MySimpleEventGenerator()// .withEventTypeId("SimpleEventTest-handle404Graciously")// .build(); @@ -109,7 +109,7 @@ public void unsubscribedListenerShouldNotReceiveAnyEvents() throws InterruptedEx flowId); client.subscribe(it.getGen().getEventTypeName(), parameters, listener, typeRef); client.unsubscribe(it.getGen().getEventTypeName(), Optional.of("0"), listener); - List createdEvents = it.publishEvents(nrOfEvents); + it.publishEvents(nrOfEvents); Thread.sleep(5000); assertEquals("Unsubscribed listener must not receive events", 0, listener.getReceivedEvents().size()); } @@ -135,7 +135,6 @@ public void validateCreatedEventType() throws InterruptedException { assertEquals(eventType.getSchema().getSchema(), originalEventType.getSchema().getSchema()); assertEquals(eventType.getSchema().getType(), originalEventType.getSchema().getType()); assertEquals(eventType.getStatistics(), originalEventType.getStatistics()); - assertEquals(eventType.getValidationStrategies(), originalEventType.getValidationStrategies()); } @Test diff --git a/it/src/test/scala/org/zalando/nakadi/client/scala/SimpleEventTest.scala b/it/src/test/scala/org/zalando/nakadi/client/scala/SimpleEventTest.scala index 1c093f0..b82e32e 100644 --- a/it/src/test/scala/org/zalando/nakadi/client/scala/SimpleEventTest.scala +++ b/it/src/test/scala/org/zalando/nakadi/client/scala/SimpleEventTest.scala @@ -10,7 +10,6 @@ import org.zalando.nakadi.client.scala.test.factory.events.MySimpleEvent import org.zalando.nakadi.client.scala.test.factory.events.SimpleEventListener import org.zalando.nakadi.client.scala.model.PartitionStrategy import org.scalatest.BeforeAndAfterAll -import org.zalando.nakadi.client.scala.model.EventValidationStrategy import org.zalando.nakadi.client.scala.model.EventEnrichmentStrategy class SimpleEventTest extends WordSpec with Matchers with BeforeAndAfterAll { @@ -119,7 +118,6 @@ class SimpleEventTest extends WordSpec with Matchers with BeforeAndAfterAll { eventType.partitionStrategy shouldBe it.eventType.partitionStrategy eventType.schema shouldBe it.eventType.schema eventType.statistics shouldBe it.eventType.statistics - eventType.validationStrategies shouldBe null eventType.enrichmentStrategies shouldBe it.eventType.enrichmentStrategies eventType.partitionKeyFields shouldBe it.eventType.partitionKeyFields } From 04a013d520799e9c6f414d05cf6484de3da22b0e Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Thu, 23 Jun 2016 15:45:12 +0200 Subject: [PATCH 103/183] techjira:LAAS-60 Removed ValidationStrategies. Fixes #75 --- it/src/test/java/org/zalando/client/java/SimpleEventTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/it/src/test/java/org/zalando/client/java/SimpleEventTest.java b/it/src/test/java/org/zalando/client/java/SimpleEventTest.java index 432db48..646cc2c 100644 --- a/it/src/test/java/org/zalando/client/java/SimpleEventTest.java +++ b/it/src/test/java/org/zalando/client/java/SimpleEventTest.java @@ -2,7 +2,6 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; import java.util.List; import java.util.Optional; From 456fcb51299468af86ec68b82d37382773230aaa Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Thu, 23 Jun 2016 17:17:49 +0200 Subject: [PATCH 104/183] techjira:LAAS-60 Json Data from Metrics endpoint can now be parsed into Metrics object. Fixes #71 --- .../nakadi/client/java/model/Metrics.java | 18 ++++++---- .../nakadi/client/scala/model/Model.scala | 2 +- .../nakadi/client/utils/TestScalaEntity.scala | 26 +++++++------- .../client/java/ClientIntegrationTest.java | 35 +++++++++++++++++++ .../client/scala/ClientIntegrationTest.scala | 10 +++--- 5 files changed, 65 insertions(+), 26 deletions(-) create mode 100644 it/src/test/java/org/zalando/client/java/ClientIntegrationTest.java diff --git a/api/src/main/java/org/zalando/nakadi/client/java/model/Metrics.java b/api/src/main/java/org/zalando/nakadi/client/java/model/Metrics.java index e7f7629..d942220 100644 --- a/api/src/main/java/org/zalando/nakadi/client/java/model/Metrics.java +++ b/api/src/main/java/org/zalando/nakadi/client/java/model/Metrics.java @@ -6,20 +6,26 @@ import com.fasterxml.jackson.annotation.JsonProperty; public class Metrics { - private final Map metrics; + private final String version; + private final Map gauges; @JsonCreator - public Metrics(@JsonProperty Map metrics) { - this.metrics = metrics; + public Metrics(@JsonProperty("version") String version, @JsonProperty("gauges") Map gauges) { + this.version = version; + this.gauges = gauges; } - public Map getMetrics() { - return metrics; + public String getVersion() { + return version; + } + + public Map getGauges() { + return gauges; } @Override public String toString() { - return "Metrics [metrics=" + metrics + "]"; + return "Metrics [version=" + version + ", gauges=" + gauges + "]"; } } diff --git a/api/src/main/scala/org/zalando/nakadi/client/scala/model/Model.scala b/api/src/main/scala/org/zalando/nakadi/client/scala/model/Model.scala index 3d12ea4..9befe22 100644 --- a/api/src/main/scala/org/zalando/nakadi/client/scala/model/Model.scala +++ b/api/src/main/scala/org/zalando/nakadi/client/scala/model/Model.scala @@ -97,7 +97,7 @@ case class Problem( detail: Option[String], instance: Option[String]) -case class Metrics(metrics: Map[String, Any]) +case class Metrics(version:String,gauges: Map[String, Any]) /** * Partition information. Can be helpful when trying to start a stream using an unmanaged API. This information is not related to the state of the consumer clients. diff --git a/client/src/test/scala/org/zalando/nakadi/client/utils/TestScalaEntity.scala b/client/src/test/scala/org/zalando/nakadi/client/utils/TestScalaEntity.scala index 7a90871..9e6059f 100644 --- a/client/src/test/scala/org/zalando/nakadi/client/utils/TestScalaEntity.scala +++ b/client/src/test/scala/org/zalando/nakadi/client/utils/TestScalaEntity.scala @@ -23,29 +23,29 @@ import org.zalando.nakadi.client.scala.model.SchemaType object TestScalaEntity { // // Simple bjects composed out of scalar typers only (without dependency to other Object-models) - val problem = new Problem("problemType", "title", 312, Option("detail"), Option("instance")) - val metrics = new Metrics(Map("metrics" -> "test")) - val partition = new Partition("0", "132", "4423") - val cursor = new Cursor("0", "120") - val eventTypeSchema = new EventTypeSchema(SchemaType.JSON, "schema") + val problem = Problem("problemType", "title", 312, Option("detail"), Option("instance")) + val metrics = Metrics("2.0.1",Map("metrics" -> "test")) + val partition = Partition("0", "132", "4423") + val cursor = Cursor("0", "120") + val eventTypeSchema = EventTypeSchema(SchemaType.JSON, "schema") val partitionResolutionStrategy = PartitionStrategy.HASH val eventEnrichmentStrategy = EventEnrichmentStrategy.METADATA //Complex objects - val eventTypeStatistics = new EventTypeStatistics(9281002, 19283, 21, 312) - val eventType = new EventType("name", "owner", + val eventTypeStatistics = EventTypeStatistics(9281002, 19283, 21, 312) + val eventType = EventType("name", "owner", EventTypeCategory.BUSINESS , List(EventEnrichmentStrategy.METADATA), Some(partitionResolutionStrategy), eventTypeSchema, List("dataKeyFields"), List("partitioningKeyFields"), Option(eventTypeStatistics)) - val eventMetadata = new EventMetadata("eid", Option(eventType.name), "occurredAt", Option("receivedAt"), List("parentEids"), Option("flowId"), Option("partition")) + val eventMetadata = EventMetadata("eid", Option(eventType.name), "occurredAt", Option("receivedAt"), List("parentEids"), Option("flowId"), Option("partition")) case class MyEvent(name: String, metadata: Option[EventMetadata]) extends Event - val myEvent = new MyEvent("test", Some(eventMetadata)) - val eventStreamBatch = new EventStreamBatch[MyEvent](cursor, Option(List(myEvent))) - val batchItemResponse = new BatchItemResponse(Option("eid"), BatchItemPublishingStatus.SUBMITTED, Option(BatchItemStep.PUBLISHING), Option("detail")) + val myEvent = MyEvent("test", Some(eventMetadata)) + val eventStreamBatch = EventStreamBatch[MyEvent](cursor, Option(List(myEvent))) + val batchItemResponse = BatchItemResponse(Option("eid"), BatchItemPublishingStatus.SUBMITTED, Option(BatchItemStep.PUBLISHING), Option("detail")) // custom event case class CommissionEntity(id: String, ql: List[String]) - val commissionEntity = new CommissionEntity("id2", List("ql1", "ql2")) - val dataChangeEvent = new DataChangeEvent[CommissionEntity](commissionEntity, "Critical", DataOperation.DELETE, Some(eventMetadata)) + val commissionEntity = CommissionEntity("id2", List("ql1", "ql2")) + val dataChangeEvent = DataChangeEvent[CommissionEntity](commissionEntity, "Critical", DataOperation.DELETE, Some(eventMetadata)) } \ No newline at end of file diff --git a/it/src/test/java/org/zalando/client/java/ClientIntegrationTest.java b/it/src/test/java/org/zalando/client/java/ClientIntegrationTest.java new file mode 100644 index 0000000..374fae4 --- /dev/null +++ b/it/src/test/java/org/zalando/client/java/ClientIntegrationTest.java @@ -0,0 +1,35 @@ +package org.zalando.client.java; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.util.Optional; +import java.util.concurrent.ExecutionException; + +import org.junit.After; +import org.junit.Test; +import org.zalando.nakadi.client.java.Client; +import org.zalando.nakadi.client.java.model.Metrics; +import org.zalando.nakadi.client.scala.ClientFactory; + +public class ClientIntegrationTest { + private Client client = ClientFactory.getJavaClient(); + + @After + public void shutdown() throws InterruptedException, ExecutionException { + client.stop(); + } + + @Test + public void getMetrics() throws InterruptedException, ExecutionException { + + Optional result = client.getMetrics().get(); + assertTrue("Metrics should be returned", result.isPresent()); + + Metrics metrics = result.get(); + assertNotNull("Version should be available", metrics.getVersion()); + + assertTrue("Gauges should not be empty", metrics.getGauges().size() > 0); + + } +} diff --git a/it/src/test/scala/org/zalando/nakadi/client/scala/ClientIntegrationTest.scala b/it/src/test/scala/org/zalando/nakadi/client/scala/ClientIntegrationTest.scala index ab3d206..92ddb28 100644 --- a/it/src/test/scala/org/zalando/nakadi/client/scala/ClientIntegrationTest.scala +++ b/it/src/test/scala/org/zalando/nakadi/client/scala/ClientIntegrationTest.scala @@ -2,7 +2,6 @@ package org.zalando.nakadi.client.scala import org.scalatest.Matchers import org.scalatest.WordSpec import org.zalando.nakadi.client.scala.model.Cursor -import org.zalando.nakadi.client.scala.model.JacksonJsonMarshaller import org.zalando.nakadi.client.scala.model.PartitionStrategyType import org.zalando.nakadi.client.scala.test.factory.EventIntegrationHelper import org.zalando.nakadi.client.scala.test.factory.events.MySimpleEvent @@ -17,7 +16,6 @@ class ClientIntegrationTest extends WordSpec with Matchers with BeforeAndAfterAl import org.scalatest.Matchers._ import ClientFactory._ - import JacksonJsonMarshaller._ import MySimpleEvent._ val client = ClientFactory.getScalaClient() @@ -34,9 +32,8 @@ class ClientIntegrationTest extends WordSpec with Matchers with BeforeAndAfterAl val Right(metricsOpt) = result metricsOpt.isDefined shouldBe true val Some(metrics) = metricsOpt - println(" >>>> " + metrics) - //TODO Deserialization of Metrics is failing - // metrics.metrics.size > 0 + metrics.version shouldNot be (null) + metrics.gauges.size should be > 0 } "GET /event-types" in { @@ -45,7 +42,8 @@ class ClientIntegrationTest extends WordSpec with Matchers with BeforeAndAfterAl val Right(eventTypesOpt) = result eventTypesOpt.isDefined shouldBe true val Some(eventTypes) = eventTypesOpt + eventTypes.size should (equal(0) or (be > 0)) } - + } From d0ae92733f66021a875b766a37388f1e1a099ab5 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Mon, 27 Jun 2016 16:26:08 +0200 Subject: [PATCH 105/183] techjira:LAAS-60 Fixed broken reconnection logic. Fixes #76 --- client/src/main/resources/reference.conf | 6 +++ .../nakadi/client/actor/ConsumingActor.scala | 14 +++--- .../client/actor/SubscriptionHolder.scala | 49 ++++++++++++++----- .../client/actor/SupervisingActor.scala | 49 +++++++++---------- 4 files changed, 73 insertions(+), 45 deletions(-) diff --git a/client/src/main/resources/reference.conf b/client/src/main/resources/reference.conf index a750c17..4dc06a2 100644 --- a/client/src/main/resources/reference.conf +++ b/client/src/main/resources/reference.conf @@ -1,6 +1,12 @@ // // reference.conf // + +akka { + loggers = ["akka.event.slf4j.Slf4jLogger"] + loglevel = "DEBUG" +} + nakadi.client { // tbd. better names & organization into groups, maybe? diff --git a/client/src/main/scala/org/zalando/nakadi/client/actor/ConsumingActor.scala b/client/src/main/scala/org/zalando/nakadi/client/actor/ConsumingActor.scala index db5e02b..a0b5c5d 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/actor/ConsumingActor.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/actor/ConsumingActor.scala @@ -55,7 +55,7 @@ class ConsumingActor(subscription: SubscriptionKey, override def receive: Receive = { case OnNext(msg: ByteString) => val message = msg.utf8String - log.debug("Event - cursor {} - {} - msg {}", lastCursor, subscription, message) + log.info("Event - prevCursor [{}] - [{}] - msg [{}]", lastCursor, subscription, message) handler.handleOnReceive(subscription.toString(), message) match { case Right(cursor) => lastCursor = Some(cursor) @@ -63,16 +63,16 @@ class ConsumingActor(subscription: SubscriptionKey, case Left(error) => log.error(error.error.getMessage) } case OnError(err: Throwable) => - log.error("onError - cursor {} - {} - error {}", lastCursor, subscription, err.getMessage) + log.error("onError - cursor [{}] - [{}] - error [{}]", lastCursor, subscription, err.getMessage) context.stop(self) case OnComplete => - // When using stream_limit, the server stops the connection. - log.info("onComplete - cursor {} - {}", lastCursor, subscription) - context.parent ! UnsubscribeMsg + log.info("onComplete - connection closed by server - cursor [{}] - [{}]", lastCursor, subscription) + context.stop(self) case Terminated => - log.info("Received Terminated msg - subscription {} with listener-id {} ", subscription, handler.id()) + log.info("Received Terminated msg - subscription [{}] with listener-id [{}] ", subscription, handler.id()) + context.stop(self) case a => - log.error("Could not handle message: {}", a) + log.error("Could not handle message: [{}]", a) context.stop(self) } diff --git a/client/src/main/scala/org/zalando/nakadi/client/actor/SubscriptionHolder.scala b/client/src/main/scala/org/zalando/nakadi/client/actor/SubscriptionHolder.scala index db973f4..15d7cca 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/actor/SubscriptionHolder.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/actor/SubscriptionHolder.scala @@ -5,23 +5,25 @@ import org.zalando.nakadi.client.scala.model.Cursor import akka.actor.ActorRef import org.slf4j.LoggerFactory import com.typesafe.scalalogging.Logger +import com.google.common.base.Preconditions /** - * Class that keeps track of the subscriptions + * Tracks subscriptions of SubscriptionEntries and Cursors. */ trait SubscriptionHolder { import SupervisingActor._ def addCursor(key: SubscriptionKey, cursor: Option[Cursor]): Unit - def addSubscription(key: SubscriptionKey, key2: ActorRef, entry: SubscriptionEntry): Unit + def add(key: SubscriptionKey, key2: ActorRef, entry: SubscriptionEntry): Unit def entry(key: SubscriptionKey): Option[SubscriptionEntry] def entryByActor(actor: ActorRef): Option[SubscriptionEntry] def cursorByActor(actor: ActorRef): Option[Cursor] - def unsubscribe(key: SubscriptionKey): Unit - def activeSize: Int + def remove(key: SubscriptionKey): Unit + def size: Int } class SubscriptionHolderImpl extends SubscriptionHolder { import SupervisingActor._ + import Preconditions._ private var subscriptions: Map[SubscriptionKey, SubscriptionEntry] = Map() //EventTypeName+Partition private var cursors: Map[SubscriptionKey, Option[Cursor]] = Map() private var actors: Map[String, SubscriptionKey] = Map() @@ -31,13 +33,40 @@ class SubscriptionHolderImpl extends SubscriptionHolder { cursors = cursors + ((key, cursor)) } - def addSubscription(key: SubscriptionKey, key2: ActorRef, entry: SubscriptionEntry) = { + def add(key: SubscriptionKey, key2: ActorRef, entry: SubscriptionEntry) = { + checkNotNull(key) + checkNotNull(key2) + checkNotNull(entry) subscriptions = subscriptions + ((key, entry)) actors = actors + ((key2.path.toString(), key)) } - def unsubscribe(key: SubscriptionKey): Unit = { - subscriptions = subscriptions - (key) + def remove(key: SubscriptionKey): Unit = { + removeCursor(key) + + entry(key) match { + case None => + logger.warn(s"Subscription [$key] not found!") + case Some(SubscriptionEntry(subscription, actor)) => { + removeSubscriptionKey(actor) + subscriptions = subscriptions - (key) + logger.info(s"Removed subscription [$subscription] for key [$key]!") + } + } + } + + private def removeCursor(key: SubscriptionKey): Unit = cursors.get(key) match { + case None => logger.warn(s"Cursor subscription for $key not found!") + case Some(cursor) => + cursors = cursors - (key) + logger.info(s"Removed cursor [$cursor] with subscription [$key]!") + } + + private def removeSubscriptionKey(actor: ActorRef): Unit = actors.get(actor.path.toString()) match { + case None => logger.warn(s"SubscriptionKey for actor [${actor.path.toString()}] not found!") + case Some(key) => + actors = actors - actor.path.toString() + logger.info(s"Removed subscriptionKey [$key] for actor [${actor.path.toString()}]!") } def entry(key: SubscriptionKey): Option[SubscriptionEntry] = { @@ -47,7 +76,7 @@ class SubscriptionHolderImpl extends SubscriptionHolder { def entryByActor(actor: ActorRef): Option[SubscriptionEntry] = actors.get(actor.path.toString()).flatMap(x => subscriptions.get(x)) - def activeSize: Int = { + def size: Int = { subscriptions.size } @@ -55,10 +84,6 @@ class SubscriptionHolderImpl extends SubscriptionHolder { actors = actors + ((actor.path.toString(), key)) } - def key(actor: ActorRef): Option[SubscriptionKey] = { - actors.get(actor.path.toString()) - } - def cursorByActor(actor: ActorRef): Option[Cursor] = { actors.get(actor.path.toString()).flatMap(x => cursors.get(x).flatMap(a => a)) } diff --git a/client/src/main/scala/org/zalando/nakadi/client/actor/SupervisingActor.scala b/client/src/main/scala/org/zalando/nakadi/client/actor/SupervisingActor.scala index eac7bb7..545d59c 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/actor/SupervisingActor.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/actor/SupervisingActor.scala @@ -58,51 +58,51 @@ class SupervisingActor(val connection: Connection, val subscriptionHandler: Subs def receive: Receive = { case OffsetMsg(cursor, subKey) => - log.info("Received cursor {} - subKey {}", cursor, subKey) + log.info("Received cursor [{}] - subKey [{}]", cursor, subKey) subscriptions.addCursor(subKey, Some(cursor)) case subscrition: SubscribeMsg => - log.info("New subscription {}", subscrition) + log.info("New subscription [{}]", subscrition) subscribe(subscrition) case unsubscription: UnsubscribeMsg => - log.info("Number of subscriptions before unsubscribing {}", subscriptions.activeSize) + log.debug("Number of subscriptions before unsubscribing [{}]", subscriptions.size) unsubscribe(unsubscription) - log.info("Number of subscriptions after unsubscribing {}", subscriptions.activeSize) + log.info("Number of subscriptions after unsubscribing [{}]", subscriptions.size) case Terminated(terminatedActor) => - log.info(s"ConsumingActor terminated {}", terminatedActor.path.name) + log.info(s"Actor [{}] terminated", terminatedActor.path.name) subscriptions.entryByActor(terminatedActor) match { case Some(SubscriptionEntry(SubscribeMsg(eventTypeName, endpoint, Some(Cursor(partition, offset)), handler), actor: ActorRef)) => + val cursor = subscriptions.cursorByActor(terminatedActor) val unsubscription = UnsubscribeMsg(eventTypeName, Option(partition), handler.id()) unsubscribe(unsubscription) - val cursor = subscriptions.cursorByActor(terminatedActor) - val subscription = SubscribeMsg(eventTypeName, endpoint, cursor, handler) - subscribe(subscription) + val newSubscription = SubscribeMsg(eventTypeName, endpoint, cursor, handler) + subscribe(newSubscription) case Some(SubscriptionEntry(SubscribeMsg(eventTypeName, endpoint, None, handler), actor: ActorRef)) => + val cursor = subscriptions.cursorByActor(terminatedActor) val unsubscription = UnsubscribeMsg(eventTypeName, None, handler.id()) unsubscribe(unsubscription) - val cursor = subscriptions.cursorByActor(terminatedActor) - val subscription = SubscribeMsg(eventTypeName, endpoint, cursor, handler) - subscribe(subscription) + val newSubscription = SubscribeMsg(eventTypeName, endpoint, cursor, handler) + subscribe(newSubscription) case None => - log.warning("Did not find any SubscriptionKey for {}", terminatedActor.path.toString()) + log.warning("Did not find any SubscriptionKey for [{}]", terminatedActor.path.toString()) case e => - log.warning("None exhaustive match! {}", e) + log.error("Received unexpected message! [{}]", e) } } def subscribe(subscribe: SubscribeMsg) = { subscriptionCounter += 1 val SubscribeMsg(eventTypeName, endpoint, optCursor, eventHandler) = subscribe - log.info("Subscription nr {} - cursor {} - eventType {} - listener {}", subscriptionCounter, optCursor, eventTypeName, eventHandler.id()) + log.info("Subscription nr [{}] - cursor [{}] - eventType [{}] - listener [{}]", subscriptionCounter, optCursor, eventTypeName, eventHandler.id()) - val subKey: SubscriptionKey = optCursor match { + val subscriptionKey: SubscriptionKey = optCursor match { case Some(Cursor(partition, _)) => SubscriptionKey(eventTypeName, Some(partition)) case None => SubscriptionKey(eventTypeName, None) } //Create the Consumer - val consumingActor = context.actorOf(Props(classOf[ConsumingActor], subKey, eventHandler), "EventConsumingActor-" + subscriptionCounter) + val consumingActor = context.actorOf(Props(classOf[ConsumingActor], subscriptionKey, eventHandler), "ConsumingActor-" + subscriptionCounter) - context.watch(consumingActor) //If the streaming is ending!! + context.watch(consumingActor) val subEntry: SubscriptionEntry = SubscriptionEntry(subscribe, consumingActor) @@ -111,23 +111,20 @@ class SupervisingActor(val connection: Connection, val subscriptionHandler: Subs //Create the pipeline subscriptionHandler.createPipeline(optCursor, consumingActor, endpoint, eventHandler) - subscriptions.addSubscription(subKey, consumingActor, subEntry) - subscriptions.addCursor(subKey, optCursor) + subscriptions.add(subscriptionKey, consumingActor, subEntry) + subscriptions.addCursor(subscriptionKey, optCursor) } - def unsubscribe(unsubscription: UnsubscribeMsg): Boolean = { + def unsubscribe(unsubscription: UnsubscribeMsg): Unit = { val UnsubscribeMsg(eventTypeName, partition, eventHandlerId) = unsubscription val key: SubscriptionKey = SubscriptionKey(eventTypeName, partition) - log.info("Unsubscribe({}) ", unsubscription) subscriptions.entry(key) match { - case Some(SubscriptionEntry(handler, actor)) => - log.info("Unsubscribing Listener : {} from actor: {}", handler, actor) + case Some(SubscriptionEntry(SubscribeMsg(eventTypeName, endpoint, cursor, handler), actor: ActorRef)) => + log.info("Unsubscribing Listener : [{}] from actor: [{}]", handler.id(), actor.path) + subscriptions.remove(key) actor ! PoisonPill - subscriptions.unsubscribe(key) - true case None => log.warning("Listener not found for {}", unsubscription) - false } } From d7e14325dd568b1af5e1dd497ba2598f786ab497 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Mon, 27 Jun 2016 16:29:02 +0200 Subject: [PATCH 106/183] techjira:LAAS-60 Adding logging dependencies. #38 --- project/Dependencies.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/project/Dependencies.scala b/project/Dependencies.scala index 9779b50..a550dfa 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -21,6 +21,7 @@ object Dependencies { "com.typesafe.akka" %% "akka-actor" % akkaVersion, "com.typesafe.akka" %% "akka-http-experimental" % akkaVersion, "com.typesafe.akka" %% "akka-stream" % akkaVersion, + "com.typesafe.akka" % "akka-slf4j_2.11" % "2.4.7", "com.typesafe.akka" %% "akka-testkit" % akkaVersion % "test", "org.scalatest" %% "scalatest" % "2.2.6" % "test", "com.google.code.findbugs" % "jsr305" % "1.3.9" % "test", From 46af4160941c528007e28e8eda0d88fad03e37a0 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Tue, 28 Jun 2016 10:22:07 +0200 Subject: [PATCH 107/183] techjira:LAAS-60 Adding Example of DataChangeEvent #54 --- .../java/test/event/dce/payment/Money.java | 26 ++++++++ .../event/dce/payment/PaymentCreated.java | 44 ++++++++++++++ .../dce/payment/PaymentEventGenerator.java | 55 +++++++++++++++++ .../generator}/EventGenerator.java | 2 +- .../generator}/EventGeneratorBuilder.java | 10 +++- .../generator}/EventIntegrationHelper.java | 2 +- .../simple}/MySimpleEvent.java | 2 +- .../simple}/MySimpleEventGenerator.java | 10 ++-- .../simple}/SimpleEventListener.java | 4 +- .../examples/scala/EventCreationExample.scala | 2 +- .../examples/scala/EventListenerExample.scala | 23 ++++--- .../nakadi/client/scala/ClientFactory.scala | 16 ++--- .../PaymentCreatedDataChangeEventTest.java | 60 +++++++++++++++++++ .../zalando/client/java/SimpleEventTest.java | 10 ++-- project/Dependencies.scala | 3 +- 15 files changed, 235 insertions(+), 34 deletions(-) create mode 100644 it/src/main/java/org/zalando/nakadi/client/java/test/event/dce/payment/Money.java create mode 100644 it/src/main/java/org/zalando/nakadi/client/java/test/event/dce/payment/PaymentCreated.java create mode 100644 it/src/main/java/org/zalando/nakadi/client/java/test/event/dce/payment/PaymentEventGenerator.java rename it/src/main/java/org/zalando/nakadi/client/java/test/{factory => event/generator}/EventGenerator.java (94%) rename it/src/main/java/org/zalando/nakadi/client/java/test/{factory => event/generator}/EventGeneratorBuilder.java (95%) rename it/src/main/java/org/zalando/nakadi/client/java/test/{factory => event/generator}/EventIntegrationHelper.java (97%) rename it/src/main/java/org/zalando/nakadi/client/java/test/{factory/events => event/simple}/MySimpleEvent.java (87%) rename it/src/main/java/org/zalando/nakadi/client/java/test/{factory => event/simple}/MySimpleEventGenerator.java (66%) rename it/src/main/java/org/zalando/nakadi/client/java/test/{factory/events => event/simple}/SimpleEventListener.java (94%) create mode 100644 it/src/test/java/org/zalando/client/java/PaymentCreatedDataChangeEventTest.java diff --git a/it/src/main/java/org/zalando/nakadi/client/java/test/event/dce/payment/Money.java b/it/src/main/java/org/zalando/nakadi/client/java/test/event/dce/payment/Money.java new file mode 100644 index 0000000..a0dc3c1 --- /dev/null +++ b/it/src/main/java/org/zalando/nakadi/client/java/test/event/dce/payment/Money.java @@ -0,0 +1,26 @@ +package org.zalando.nakadi.client.java.test.event.dce.payment; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +public class Money { + + private double amount; + private String currency; + + @JsonCreator + public Money(@JsonProperty("amount") double amount, // + @JsonProperty("currency") String currency) { + this.amount = amount; + this.currency = currency; + } + + public double getAmount() { + return amount; + } + + public String getCurrency() { + return currency; + } + +} diff --git a/it/src/main/java/org/zalando/nakadi/client/java/test/event/dce/payment/PaymentCreated.java b/it/src/main/java/org/zalando/nakadi/client/java/test/event/dce/payment/PaymentCreated.java new file mode 100644 index 0000000..42fe459 --- /dev/null +++ b/it/src/main/java/org/zalando/nakadi/client/java/test/event/dce/payment/PaymentCreated.java @@ -0,0 +1,44 @@ +package org.zalando.nakadi.client.java.test.event.dce.payment; + +import java.net.URI; + +import com.fasterxml.jackson.annotation.JsonProperty; + +public class PaymentCreated { + + private String orderNumber; + + private String salesChannelId; + + private Money paymentBalance; + + private URI paymentTokenReference; + + public PaymentCreated( + @JsonProperty("order_number") String orderNumber, // + @JsonProperty("sales_channel_id") String salesChannelId, // + @JsonProperty("payment_balance") Money paymentBalance, // + @JsonProperty("payment_token_reference") URI paymentTokenReference) { + this.orderNumber = orderNumber; + this.salesChannelId = salesChannelId; + this.paymentBalance = paymentBalance; + this.paymentTokenReference = paymentTokenReference; + } + + public String getOrderNumber() { + return orderNumber; + } + + public String getSalesChannelId() { + return salesChannelId; + } + + public Money getPaymentBalance() { + return paymentBalance; + } + + public URI getPaymentTokenReference() { + return paymentTokenReference; + } + +} \ No newline at end of file diff --git a/it/src/main/java/org/zalando/nakadi/client/java/test/event/dce/payment/PaymentEventGenerator.java b/it/src/main/java/org/zalando/nakadi/client/java/test/event/dce/payment/PaymentEventGenerator.java new file mode 100644 index 0000000..a90b90d --- /dev/null +++ b/it/src/main/java/org/zalando/nakadi/client/java/test/event/dce/payment/PaymentEventGenerator.java @@ -0,0 +1,55 @@ +package org.zalando.nakadi.client.java.test.event.dce.payment; + +import java.net.URI; +import java.net.URISyntaxException; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.util.UUID; + +import org.apache.commons.lang.RandomStringUtils; +import org.slf4j.LoggerFactory; +import org.zalando.nakadi.client.java.enumerator.DataOperation; +import org.zalando.nakadi.client.java.model.DataChangeEvent; +import org.zalando.nakadi.client.java.model.Event; +import org.zalando.nakadi.client.java.model.EventMetadata; +import org.zalando.nakadi.client.java.test.event.generator.EventGeneratorBuilder; + +public class PaymentEventGenerator extends EventGeneratorBuilder { + private final String id = RandomStringUtils.randomAlphanumeric(12); + private int tokenNr = 3; + private org.slf4j.Logger log = LoggerFactory.getLogger(this.getClass()); + + protected Event getNewEvent() { + Money money = new Money(120.00, "USD"); + PaymentCreated payment = null; + try { + payment = new PaymentCreated(// + randomNumeric(5), // + "Channel_1",// + money, // + new URI("https://zalando.test/token-" + randomNumeric(tokenNr) + "-" + randomNumeric(tokenNr))); + String dataType = getEventTypeName(); + DataOperation dataOperation = DataOperation.CREATE; + EventMetadata metadata = new EventMetadata(UUID.randomUUID().toString(),null, + DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(ZonedDateTime.now()), + null, null, null, null); + return new DataChangeEvent(payment, dataType, dataOperation, metadata); + } catch (URISyntaxException e) { + log.error("An error occurred", e.getMessage()); + throw new RuntimeException("An error occurred",e); + } + + } + + @Override + protected String getEventTypeName() { + return getEventTypeId() + id; + } + + @Override + protected String getSchemaDefinition() { + return "{ 'properties': { 'order_number': { 'type': 'string' }, 'sales_channel_id': { 'type': 'string' }, 'payment_balance': { 'type': 'object', 'properties': { 'amount': { 'type': 'number' }, 'currency': { 'type': 'string' } } }, 'payment_token_reference': { 'type': 'string' } }}" + .replaceAll("'", "\""); + } + +} diff --git a/it/src/main/java/org/zalando/nakadi/client/java/test/factory/EventGenerator.java b/it/src/main/java/org/zalando/nakadi/client/java/test/event/generator/EventGenerator.java similarity index 94% rename from it/src/main/java/org/zalando/nakadi/client/java/test/factory/EventGenerator.java rename to it/src/main/java/org/zalando/nakadi/client/java/test/event/generator/EventGenerator.java index 281c868..645685f 100644 --- a/it/src/main/java/org/zalando/nakadi/client/java/test/factory/EventGenerator.java +++ b/it/src/main/java/org/zalando/nakadi/client/java/test/event/generator/EventGenerator.java @@ -1,4 +1,4 @@ -package org.zalando.nakadi.client.java.test.factory; +package org.zalando.nakadi.client.java.test.event.generator; import java.util.List; diff --git a/it/src/main/java/org/zalando/nakadi/client/java/test/factory/EventGeneratorBuilder.java b/it/src/main/java/org/zalando/nakadi/client/java/test/event/generator/EventGeneratorBuilder.java similarity index 95% rename from it/src/main/java/org/zalando/nakadi/client/java/test/factory/EventGeneratorBuilder.java rename to it/src/main/java/org/zalando/nakadi/client/java/test/event/generator/EventGeneratorBuilder.java index 4b3d71f..b0e9a8d 100644 --- a/it/src/main/java/org/zalando/nakadi/client/java/test/factory/EventGeneratorBuilder.java +++ b/it/src/main/java/org/zalando/nakadi/client/java/test/event/generator/EventGeneratorBuilder.java @@ -1,8 +1,9 @@ -package org.zalando.nakadi.client.java.test.factory; +package org.zalando.nakadi.client.java.test.event.generator; import java.util.Collections; import java.util.List; +import org.apache.commons.lang.RandomStringUtils; import org.zalando.nakadi.client.java.enumerator.EventEnrichmentStrategy; import org.zalando.nakadi.client.java.enumerator.EventTypeCategory; import org.zalando.nakadi.client.java.enumerator.PartitionStrategy; @@ -242,5 +243,12 @@ protected List getPartitionKeyFields() { protected EventTypeStatistics getStatistics() { return statistics; } + + public String randomNumeric(int size) { + return RandomStringUtils.randomNumeric(size); + } + public String randomAlphanumeric(int size) { + return RandomStringUtils.randomAlphanumeric(size); + } } diff --git a/it/src/main/java/org/zalando/nakadi/client/java/test/factory/EventIntegrationHelper.java b/it/src/main/java/org/zalando/nakadi/client/java/test/event/generator/EventIntegrationHelper.java similarity index 97% rename from it/src/main/java/org/zalando/nakadi/client/java/test/factory/EventIntegrationHelper.java rename to it/src/main/java/org/zalando/nakadi/client/java/test/event/generator/EventIntegrationHelper.java index 2c2bd1c..b3f0938 100644 --- a/it/src/main/java/org/zalando/nakadi/client/java/test/factory/EventIntegrationHelper.java +++ b/it/src/main/java/org/zalando/nakadi/client/java/test/event/generator/EventIntegrationHelper.java @@ -1,4 +1,4 @@ -package org.zalando.nakadi.client.java.test.factory; +package org.zalando.nakadi.client.java.test.event.generator; import java.util.ArrayList; import java.util.List; diff --git a/it/src/main/java/org/zalando/nakadi/client/java/test/factory/events/MySimpleEvent.java b/it/src/main/java/org/zalando/nakadi/client/java/test/event/simple/MySimpleEvent.java similarity index 87% rename from it/src/main/java/org/zalando/nakadi/client/java/test/factory/events/MySimpleEvent.java rename to it/src/main/java/org/zalando/nakadi/client/java/test/event/simple/MySimpleEvent.java index a2da0c6..ba7dc08 100644 --- a/it/src/main/java/org/zalando/nakadi/client/java/test/factory/events/MySimpleEvent.java +++ b/it/src/main/java/org/zalando/nakadi/client/java/test/event/simple/MySimpleEvent.java @@ -1,4 +1,4 @@ -package org.zalando.nakadi.client.java.test.factory.events; +package org.zalando.nakadi.client.java.test.event.simple; import org.zalando.nakadi.client.java.model.Event; diff --git a/it/src/main/java/org/zalando/nakadi/client/java/test/factory/MySimpleEventGenerator.java b/it/src/main/java/org/zalando/nakadi/client/java/test/event/simple/MySimpleEventGenerator.java similarity index 66% rename from it/src/main/java/org/zalando/nakadi/client/java/test/factory/MySimpleEventGenerator.java rename to it/src/main/java/org/zalando/nakadi/client/java/test/event/simple/MySimpleEventGenerator.java index c7e005b..61cdc0f 100644 --- a/it/src/main/java/org/zalando/nakadi/client/java/test/factory/MySimpleEventGenerator.java +++ b/it/src/main/java/org/zalando/nakadi/client/java/test/event/simple/MySimpleEventGenerator.java @@ -1,23 +1,25 @@ -package org.zalando.nakadi.client.java.test.factory; +package org.zalando.nakadi.client.java.test.event.simple; import org.apache.commons.lang.RandomStringUtils; import org.zalando.nakadi.client.java.model.Event; -import org.zalando.nakadi.client.java.test.factory.events.MySimpleEvent; +import org.zalando.nakadi.client.java.test.event.generator.EventGeneratorBuilder; public class MySimpleEventGenerator extends EventGeneratorBuilder { - private final String id= RandomStringUtils.randomAlphanumeric(12); + private final String id = RandomStringUtils.randomAlphanumeric(12); + protected Event getNewEvent() { return new MySimpleEvent(getNewId()); } @Override protected String getEventTypeName() { - return getEventTypeId() +id; + return getEventTypeId() + id; } @Override protected String getSchemaDefinition() { return "{ 'properties': { 'order_number': { 'type': 'string' } } }".replaceAll("'", "\""); + } } diff --git a/it/src/main/java/org/zalando/nakadi/client/java/test/factory/events/SimpleEventListener.java b/it/src/main/java/org/zalando/nakadi/client/java/test/event/simple/SimpleEventListener.java similarity index 94% rename from it/src/main/java/org/zalando/nakadi/client/java/test/factory/events/SimpleEventListener.java rename to it/src/main/java/org/zalando/nakadi/client/java/test/event/simple/SimpleEventListener.java index b9b9541..9ae9d3c 100644 --- a/it/src/main/java/org/zalando/nakadi/client/java/test/factory/events/SimpleEventListener.java +++ b/it/src/main/java/org/zalando/nakadi/client/java/test/event/simple/SimpleEventListener.java @@ -1,4 +1,4 @@ -package org.zalando.nakadi.client.java.test.factory.events; +package org.zalando.nakadi.client.java.test.event.simple; import java.util.ArrayList; import java.util.List; @@ -20,7 +20,7 @@ public SimpleEventListener(){ @Override public String getId() { - return "MySimpleEventListner"; + return "MySimpleEventListner"; } @Override diff --git a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala index ac1ed08..2e7618c 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala @@ -72,7 +72,7 @@ object EventCreationExample extends App { // 4. Publish the EventType // System.exit(0) var counter = 0 - for (n <- 1 to 5000) { + for (n <- 1 to 500) { val event = new MeetingsEvent("2016-04-28T13:28:15+00:00", "Hackaton") var events = ListBuffer[MeetingsEvent]() for (a <- 1 to 100) { diff --git a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala index 27b580f..357a848 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala @@ -18,7 +18,7 @@ class EventCounterListener(val id: String) extends Listener[MeetingsEvent] { val log = Logger(LoggerFactory.getLogger(this.getClass)) private var eventCount: AtomicLong = new AtomicLong(0); private var callerCount: AtomicLong = new AtomicLong(0); - + private var slept = false def onError(sourceUrl: String, error: Option[ClientError]): Unit = { println("Error %s %s".format(sourceUrl, error)) } @@ -29,6 +29,11 @@ class EventCounterListener(val id: String) extends Listener[MeetingsEvent] { log.info(s"Received " + events.size.toLong) log.info(s"Has a total of $eventCount events") log.info("#####################################") + if (!slept) { + Thread.sleep(65000) + slept = true + } + log.info(s"Proccessed cursor $cursor") } def onSubscribed(endpoint: String, cursor: Option[Cursor]): Unit = { @@ -56,16 +61,16 @@ object EventListenerExample extends App { * Create the Parameters with the cursor. */ - val cursor = Cursor("0", "0") +// val cursor = Cursor("0", "400") val parameters = new StreamParameters( - cursor = Some(cursor) // - , batchLimit = Some(250) // Maximum number of `Event`s in each chunk (and therefore per partition) of the stream. - , streamLimit = Some(500) // Maximum number of `Event`s to stream (over all partitions being streamed in this +// cursor = Some(cursor) // +// , batchLimit = Some(10) // Maximum number of `Event`s in each chunk (and therefore per partition) of the stream. + // , streamLimit = Some(500) // Maximum number of `Event`s to stream (over all partitions being streamed in this //connection). - , batchFlushTimeout = Some(5) // Maximum time in seconds to wait for the flushing of each chunk (per partition). + // , batchFlushTimeout = Some(5) // Maximum time in seconds to wait for the flushing of each chunk (per partition). // ,streamKeepAliveLimit=Some(4) - , streamTimeout = Some(7) + // , streamTimeout = Some(1) ) /** @@ -79,9 +84,9 @@ object EventListenerExample extends App { val eventTypeName = "Example-2000" val result = client.subscribe(eventTypeName, parameters, listener) -// Thread.sleep(3000) + Thread.sleep(30000) // client.stop() - // client.unsubscribe(eventTypeName,"0", listener) + client.unsubscribe(eventTypeName, Some("0"), listener) // client.subscribe(eventTypeName, parameters, listener) } \ No newline at end of file diff --git a/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala b/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala index a54eb81..b29cfb7 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala @@ -6,16 +6,16 @@ import java.util.function.Supplier object ClientFactory { import sys.process._ import scala.language.postfixOps - def OAuth2Token(): Option[() => String] = Option(() => "********-****-****-****-************") + def OAuth2Token(): Option[() => String] = Option(() => "25c71f66-f93a-409d-8983-8888dfac0942") def getJavaClient() = builder().buildJavaClient(); def getScalaClient() = builder().build() private def builder() = { - // useTest() - // useStaging() - useLocal() +// useSandbox() + useStaging() +// useLocal() } private def useLocal() = { new ClientBuilder() // @@ -24,16 +24,16 @@ object ClientFactory { .withSecuredConnection(false) // s .withVerifiedSslCertificate(false) // s } - private def useTest() = { + private def useSandbox() = { ClientBuilder() - .withHost("nakadi********.**********.********") + .withHost("nakadi-sandbox.aruha-test.zalan.do") .withPort(443) .withSecuredConnection(true) //s .withVerifiedSslCertificate(false) //s .withTokenProvider(ClientFactory.OAuth2Token()) } private def useStaging() = { - useTest() - .withHost("nakadi********.**********.********") + useSandbox() + .withHost("nakadi-staging.aruha-test.zalan.do") } } \ No newline at end of file diff --git a/it/src/test/java/org/zalando/client/java/PaymentCreatedDataChangeEventTest.java b/it/src/test/java/org/zalando/client/java/PaymentCreatedDataChangeEventTest.java new file mode 100644 index 0000000..60ba0cb --- /dev/null +++ b/it/src/test/java/org/zalando/client/java/PaymentCreatedDataChangeEventTest.java @@ -0,0 +1,60 @@ +package org.zalando.client.java; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.util.Optional; +import java.util.concurrent.ExecutionException; + +import org.junit.After; +import org.junit.Test; +import org.zalando.nakadi.client.java.Client; +import org.zalando.nakadi.client.java.model.DataChangeEvent; +import org.zalando.nakadi.client.java.model.EventStreamBatch; +import org.zalando.nakadi.client.java.model.EventType; +import org.zalando.nakadi.client.java.test.event.dce.payment.PaymentCreated; +import org.zalando.nakadi.client.java.test.event.dce.payment.PaymentEventGenerator; +import org.zalando.nakadi.client.java.test.event.generator.EventGenerator; +import org.zalando.nakadi.client.java.test.event.generator.EventIntegrationHelper; +import org.zalando.nakadi.client.scala.ClientFactory; + +import com.fasterxml.jackson.core.type.TypeReference; + +public class PaymentCreatedDataChangeEventTest { + + private Client client = ClientFactory.getJavaClient();; + + private TypeReference>> typeRef = new TypeReference>>() { + }; + + @After + public void shutdown() throws InterruptedException, ExecutionException { + client.stop(); + } + + @Test + public void createSchema() throws InterruptedException, ExecutionException { + + EventGenerator gen = new PaymentEventGenerator()// + .withEventTypeId("PaymentCreatedDataChangeEventTest-createSchema")// + .build(); + EventIntegrationHelper it = new EventIntegrationHelper(gen, client); + assertTrue(it.createEventType()); + assertTrue(it.getEventType().isPresent()); + Optional result = client.getEventType("none-existing-event-type-name").get(); + assertEquals(result.isPresent(), false); + } + @Test + public void createEventType() throws InterruptedException, ExecutionException { + + EventGenerator gen = new PaymentEventGenerator()// + .withEventTypeId("PaymentCreatedDataChangeEventTest-createEventType")// + .build(); + EventIntegrationHelper it = new EventIntegrationHelper(gen, client); + assertTrue(it.createEventType()); + assertTrue(it.getEventType().isPresent()); + + it.publishEvents(12); + } + +} diff --git a/it/src/test/java/org/zalando/client/java/SimpleEventTest.java b/it/src/test/java/org/zalando/client/java/SimpleEventTest.java index 646cc2c..f37cf4e 100644 --- a/it/src/test/java/org/zalando/client/java/SimpleEventTest.java +++ b/it/src/test/java/org/zalando/client/java/SimpleEventTest.java @@ -16,11 +16,11 @@ import org.zalando.nakadi.client.java.model.Event; import org.zalando.nakadi.client.java.model.EventStreamBatch; import org.zalando.nakadi.client.java.model.EventType; -import org.zalando.nakadi.client.java.test.factory.EventGenerator; -import org.zalando.nakadi.client.java.test.factory.EventIntegrationHelper; -import org.zalando.nakadi.client.java.test.factory.MySimpleEventGenerator; -import org.zalando.nakadi.client.java.test.factory.events.MySimpleEvent; -import org.zalando.nakadi.client.java.test.factory.events.SimpleEventListener; +import org.zalando.nakadi.client.java.test.event.generator.EventGenerator; +import org.zalando.nakadi.client.java.test.event.generator.EventIntegrationHelper; +import org.zalando.nakadi.client.java.test.event.simple.MySimpleEvent; +import org.zalando.nakadi.client.java.test.event.simple.MySimpleEventGenerator; +import org.zalando.nakadi.client.java.test.event.simple.SimpleEventListener; import org.zalando.nakadi.client.scala.ClientFactory; import com.fasterxml.jackson.core.type.TypeReference; diff --git a/project/Dependencies.scala b/project/Dependencies.scala index a550dfa..64ed678 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -36,7 +36,8 @@ object Dependencies { "org.zalando.stups" % "tokens" % "0.9.9", "org.apache.httpcomponents" % "httpclient" % "4.5.2", "org.scalatest" %% "scalatest" % "2.2.6", - "commons-lang" % "commons-lang" % "2.6" + "commons-lang" % "commons-lang" % "2.6", + "org.zalando" % "jackson-datatype-money" % "0.6.0" ) } } From 6ee86b1a081fd13aeb9448dc4f5169b9a0993af7 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Tue, 28 Jun 2016 10:45:15 +0200 Subject: [PATCH 108/183] techjira:LAAS-60 Adding Example of DataChangeEvent #54 --- .../java/test/event/generator/EventIntegrationHelper.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/it/src/main/java/org/zalando/nakadi/client/java/test/event/generator/EventIntegrationHelper.java b/it/src/main/java/org/zalando/nakadi/client/java/test/event/generator/EventIntegrationHelper.java index b3f0938..392df8d 100644 --- a/it/src/main/java/org/zalando/nakadi/client/java/test/event/generator/EventIntegrationHelper.java +++ b/it/src/main/java/org/zalando/nakadi/client/java/test/event/generator/EventIntegrationHelper.java @@ -72,9 +72,7 @@ public List publishEvents(Integer nrOfEvents) throws InterruptedException List events = new ArrayList(); IntStream.range(0, nrOfEvents).forEach(nr -> events.add(gen.getNewEvent())); client.publishEvents(eventType.getName(), events).get(); - log.info("#######"); - log.info("#######" + events.size()); - log.info("#######"); + log.info("Events published" + events.size()); return events; } From de84ceb851fab26e9f9d16ebcdb5d0cb88eb9873 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Tue, 28 Jun 2016 11:32:06 +0200 Subject: [PATCH 109/183] techjira:LAAS-60 Adding Example of DataChangeEvent #54 --- .../org/zalando/nakadi/client/scala/ClientFactory.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala b/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala index b29cfb7..ea84bb3 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala @@ -6,7 +6,7 @@ import java.util.function.Supplier object ClientFactory { import sys.process._ import scala.language.postfixOps - def OAuth2Token(): Option[() => String] = Option(() => "25c71f66-f93a-409d-8983-8888dfac0942") + def OAuth2Token(): Option[() => String] = Option(() => "****") def getJavaClient() = builder().buildJavaClient(); @@ -26,7 +26,7 @@ object ClientFactory { } private def useSandbox() = { ClientBuilder() - .withHost("nakadi-sandbox.aruha-test.zalan.do") + .withHost("") .withPort(443) .withSecuredConnection(true) //s .withVerifiedSslCertificate(false) //s @@ -34,6 +34,6 @@ object ClientFactory { } private def useStaging() = { useSandbox() - .withHost("nakadi-staging.aruha-test.zalan.do") + .withHost("") } } \ No newline at end of file From c175e62d0ac7c24e3d8ad47a4fa48ee6ada5c400 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Tue, 28 Jun 2016 12:53:39 +0200 Subject: [PATCH 110/183] techjira:LAAS-60 Removing reference to scala-loggin. Fixes #79 --- .../client/actor/SubscriptionHolder.scala | 4 +-- .../client/handler/SubscriptionHandler.scala | 6 ++--- .../client/java/JavaClientHandler.scala | 3 +-- .../model/JavaJacksonJsonMarshaller.scala | 3 +-- .../nakadi/client/scala/ClientImpl.scala | 3 +-- .../nakadi/client/scala/Connection.scala | 7 +++-- .../nakadi/client/scala/HttpFactory.scala | 1 - .../nakadi/client/scala/MessageHandler.scala | 5 ++-- .../model/ScalaJacksonJsonMarshaller.scala | 3 +-- .../nakadi/client/utils/ModelConverter.scala | 1 - .../examples/scala/EventCreationExample.scala | 4 +-- .../examples/scala/EventListenerExample.scala | 26 +++++++++---------- .../nakadi/client/scala/ClientFactory.scala | 10 +++---- .../test/factory/EventIntegrationHelper.scala | 5 ++-- project/Dependencies.scala | 1 - 15 files changed, 35 insertions(+), 47 deletions(-) diff --git a/client/src/main/scala/org/zalando/nakadi/client/actor/SubscriptionHolder.scala b/client/src/main/scala/org/zalando/nakadi/client/actor/SubscriptionHolder.scala index 15d7cca..f4bd2ff 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/actor/SubscriptionHolder.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/actor/SubscriptionHolder.scala @@ -4,8 +4,8 @@ import org.zalando.nakadi.client.scala.model.Cursor import akka.actor.ActorRef import org.slf4j.LoggerFactory -import com.typesafe.scalalogging.Logger import com.google.common.base.Preconditions +import org.slf4j.Logger /** * Tracks subscriptions of SubscriptionEntries and Cursors. @@ -27,7 +27,7 @@ class SubscriptionHolderImpl extends SubscriptionHolder { private var subscriptions: Map[SubscriptionKey, SubscriptionEntry] = Map() //EventTypeName+Partition private var cursors: Map[SubscriptionKey, Option[Cursor]] = Map() private var actors: Map[String, SubscriptionKey] = Map() - private val logger = Logger(LoggerFactory.getLogger(this.getClass)) + private val logger = LoggerFactory.getLogger(this.getClass) def addCursor(key: SubscriptionKey, cursor: Option[Cursor]): Unit = { cursors = cursors + ((key, cursor)) diff --git a/client/src/main/scala/org/zalando/nakadi/client/handler/SubscriptionHandler.scala b/client/src/main/scala/org/zalando/nakadi/client/handler/SubscriptionHandler.scala index 4868eba..d594eca 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/handler/SubscriptionHandler.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/handler/SubscriptionHandler.scala @@ -9,13 +9,12 @@ import org.slf4j.LoggerFactory import org.zalando.nakadi.client.actor.SupervisingActor import org.zalando.nakadi.client.actor.SupervisingActor.SubscribeMsg import org.zalando.nakadi.client.actor.SupervisingActor.UnsubscribeMsg +import org.zalando.nakadi.client.scala.ClientError import org.zalando.nakadi.client.scala.Connection import org.zalando.nakadi.client.scala.EventHandler import org.zalando.nakadi.client.scala.HttpFactory import org.zalando.nakadi.client.scala.model.Cursor -import com.typesafe.scalalogging.Logger - import akka.NotUsed import akka.actor.ActorRef import akka.actor.PoisonPill @@ -32,7 +31,6 @@ import akka.stream.scaladsl.Framing import akka.stream.scaladsl.Sink import akka.stream.scaladsl.Source import akka.util.ByteString -import org.zalando.nakadi.client.scala.ClientError trait SubscriptionHandler { /** @@ -54,7 +52,7 @@ class SubscriptionHandlerImpl(val connection: Connection) extends SubscriptionHa private val RECEIVE_BUFFER_SIZE = 40960 private val EVENT_DELIMITER = "\n" - val logger = Logger(LoggerFactory.getLogger(this.getClass)) + val logger = LoggerFactory.getLogger(this.getClass) def subscribe(eventTypeName: String, endpoint: String, cursor: Option[Cursor], eventHandler: EventHandler) = { supervisingActor ! SubscribeMsg(eventTypeName, endpoint, cursor, eventHandler) diff --git a/client/src/main/scala/org/zalando/nakadi/client/java/JavaClientHandler.scala b/client/src/main/scala/org/zalando/nakadi/client/java/JavaClientHandler.scala index 21d7868..3e54a2b 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/java/JavaClientHandler.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/java/JavaClientHandler.scala @@ -26,7 +26,6 @@ import org.zalando.nakadi.client.scala.{ StreamParameters => ScalaStreamParamete import org.zalando.nakadi.client.utils.FutureConversions import org.zalando.nakadi.client.utils.ModelConverter -import com.typesafe.scalalogging.Logger import akka.http.scaladsl.model.HttpHeader import akka.http.scaladsl.model.HttpMethods @@ -56,7 +55,7 @@ trait JavaClientHandler { } class JavaClientHandlerImpl(val connection: Connection, subscriber: SubscriptionHandler) extends JavaClientHandler { - val logger: Logger = Logger(LoggerFactory.getLogger(this.getClass)) + val logger = LoggerFactory.getLogger(this.getClass) import HttpFactory._ import GeneralConversions._ private implicit val mat = connection.materializer() diff --git a/client/src/main/scala/org/zalando/nakadi/client/java/model/JavaJacksonJsonMarshaller.scala b/client/src/main/scala/org/zalando/nakadi/client/java/model/JavaJacksonJsonMarshaller.scala index 34176b8..7e15fae 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/java/model/JavaJacksonJsonMarshaller.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/java/model/JavaJacksonJsonMarshaller.scala @@ -17,11 +17,10 @@ import com.fasterxml.jackson.databind.PropertyNamingStrategy import com.fasterxml.jackson.databind.SerializationFeature import com.fasterxml.jackson.databind.deser.DeserializationProblemHandler import com.fasterxml.jackson.module.scala.DefaultScalaModule -import com.typesafe.scalalogging.Logger import com.fasterxml.jackson.databind.annotation.JsonSerialize.Inclusion object JavaJacksonJsonMarshaller { - val logger = Logger(LoggerFactory.getLogger(this.getClass)) + val logger = LoggerFactory.getLogger(this.getClass) // All TypeReferences def problemTR: TypeReference[Problem] = new TypeReference[Problem] {} def metricsTR: TypeReference[Metrics] = new TypeReference[Metrics] {} diff --git a/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala index b1d9565..e2455c0 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala @@ -23,7 +23,6 @@ import org.zalando.nakadi.client.scala.model.PartitionStrategy import org.zalando.nakadi.client.utils.Uri import com.fasterxml.jackson.core.`type`.TypeReference -import com.typesafe.scalalogging.Logger import akka.http.scaladsl.model.HttpResponse import akka.http.scaladsl.model.StatusCodes @@ -35,7 +34,7 @@ class ClientImpl(connection: Connection, subscriber: SubscriptionHandler, charSe import HttpFactory._ implicit val materializer = connection.materializer() - private val logger = Logger(LoggerFactory.getLogger(this.getClass)) + private val logger = LoggerFactory.getLogger(this.getClass) def getMetrics(): Future[Either[ClientError, Option[Metrics]]] = { logFutureEither(connection.get(URI_METRICS).flatMap(mapToEither(_)(deserializer(metricsTR)))) diff --git a/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala index bb9d1fc..a3dc888 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala @@ -14,7 +14,6 @@ import org.zalando.nakadi.client.java.JavaClientHandlerImpl import org.zalando.nakadi.client.java.model.{ Event => JEvent } import org.zalando.nakadi.client.scala.model.Event import org.zalando.nakadi.client.Serializer -import com.typesafe.scalalogging.Logger import HttpFactory.TokenProvider import akka.actor.Actor @@ -110,7 +109,7 @@ sealed class ConnectionImpl(val host: String, val port: Int, val tokenProvider: private implicit val http = Http(actorSystem) private val actors: Map[String, Actor] = Map() - val logger = Logger(LoggerFactory.getLogger(this.getClass)) + val logger = LoggerFactory.getLogger(this.getClass) def requestFlow(): Flow[HttpRequest, HttpResponse, Future[Http.OutgoingConnection]] = newSslContext(securedConnection, verifySSlCertificate) match { case Some(result) => http.outgoingConnectionHttps(host, port, result) @@ -130,13 +129,13 @@ sealed class ConnectionImpl(val host: String, val port: Int, val tokenProvider: def put[T](endpoint: String, model: T)(implicit serializer: Serializer[T]): Future[HttpResponse] = { val entity = serializer.to(model) - logger.info("Put: {} - Data: {}", endpoint, entity) + logger.info(s"Put: $endpoint - Data: $entity") executeCall(withHttpRequest(endpoint, HttpMethods.PUT, Nil, tokenProvider, None)) } def post[T](endpoint: String, model: T)(implicit serializer: Serializer[T]): Future[HttpResponse] = { val entity = serializer.to(model) - logger.info("Post:{} - Data:{}", endpoint, entity) + logger.info(s"Put: $endpoint - Data: $entity") executeCall(withHttpRequestAndPayload(endpoint, entity, HttpMethods.POST, tokenProvider)) } diff --git a/client/src/main/scala/org/zalando/nakadi/client/scala/HttpFactory.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/HttpFactory.scala index ba42521..b784a05 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/scala/HttpFactory.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/scala/HttpFactory.scala @@ -11,7 +11,6 @@ import akka.http.scaladsl.model.headers.{ Accept, RawHeader } import akka.http.scaladsl.model.MediaTypes.`application/json` import org.zalando.nakadi.client.scala.model.Cursor import org.slf4j.LoggerFactory -import com.typesafe.scalalogging.Logger // object HttpFactory { diff --git a/client/src/main/scala/org/zalando/nakadi/client/scala/MessageHandler.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/MessageHandler.scala index ff05872..8abe3c8 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/scala/MessageHandler.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/scala/MessageHandler.scala @@ -16,7 +16,6 @@ import org.zalando.nakadi.client.utils.ModelConverter._ import java.util.{ List => JList } import java.util.Optional import org.slf4j.LoggerFactory -import com.typesafe.scalalogging.Logger import jdk.nashorn.internal.runtime.regexp.joni.constants.Arguments import com.google.common.base.Preconditions._ @@ -33,7 +32,7 @@ trait EventHandler { } class ScalaEventHandlerImpl[S <: Event](des: Deserializer[EventStreamBatch[S]], listener: Listener[S]) extends EventHandler { - private val log = Logger(LoggerFactory.getLogger(this.getClass)) + private val log = LoggerFactory.getLogger(this.getClass) private def createException(msg: String) = new IllegalStateException(msg) checkArgument(listener != null, "Listener must not be null", null) checkArgument(des != null, "Deserializer must not be null", null) @@ -66,7 +65,7 @@ class ScalaEventHandlerImpl[S <: Event](des: Deserializer[EventStreamBatch[S]], } class JavaEventHandlerImpl[J <: JEvent](des: Deserializer[JEventStreamBatch[J]], listener: JListener[J]) extends EventHandler { - private val log = Logger(LoggerFactory.getLogger(this.getClass)) + private val log = LoggerFactory.getLogger(this.getClass) private def createException(msg: String) = new IllegalStateException(msg) checkArgument(listener != null, "Listener must not be null", null) checkArgument(des != null, "Deserializer must not be null", null) diff --git a/client/src/main/scala/org/zalando/nakadi/client/scala/model/ScalaJacksonJsonMarshaller.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/model/ScalaJacksonJsonMarshaller.scala index cb5351c..188e793 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/scala/model/ScalaJacksonJsonMarshaller.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/scala/model/ScalaJacksonJsonMarshaller.scala @@ -16,14 +16,13 @@ import com.fasterxml.jackson.databind.PropertyNamingStrategy import com.fasterxml.jackson.databind.SerializationFeature import com.fasterxml.jackson.databind.deser.DeserializationProblemHandler import com.fasterxml.jackson.module.scala.DefaultScalaModule -import com.typesafe.scalalogging.Logger import com.fasterxml.jackson.module.scala.IteratorModule import com.fasterxml.jackson.module.scala.OptionModule import com.fasterxml.jackson.module.scala.SeqModule import com.fasterxml.jackson.module.scala.MapModule object JacksonJsonMarshaller { - val logger = Logger(LoggerFactory.getLogger(this.getClass)) + val logger = LoggerFactory.getLogger(this.getClass) // All TypeReferences implicit def problemTR: TypeReference[Problem] = new TypeReference[Problem] {} diff --git a/client/src/main/scala/org/zalando/nakadi/client/utils/ModelConverter.scala b/client/src/main/scala/org/zalando/nakadi/client/utils/ModelConverter.scala index b35b12e..deead65 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/utils/ModelConverter.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/utils/ModelConverter.scala @@ -13,7 +13,6 @@ import org.zalando.nakadi.client.java.model.{ Cursor => JCursor } import java.util.Optional import scala.collection.JavaConversions._ import org.slf4j.LoggerFactory -import com.typesafe.scalalogging.Logger import org.zalando.nakadi.client.scala.model.EventStreamBatch object ModelConverter { diff --git a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala index 2e7618c..9ea8f87 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala @@ -72,10 +72,10 @@ object EventCreationExample extends App { // 4. Publish the EventType // System.exit(0) var counter = 0 - for (n <- 1 to 500) { + for (n <- 1 to 1) { val event = new MeetingsEvent("2016-04-28T13:28:15+00:00", "Hackaton") var events = ListBuffer[MeetingsEvent]() - for (a <- 1 to 100) { + for (a <- 1 to 1) { counter += 1 events += MeetingsEvent("2016-04-28T13:28:15+00:00", "Hackaton" + counter) } diff --git a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala index 357a848..3778f81 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala @@ -9,13 +9,12 @@ import EventCreationExample._ import com.fasterxml.jackson.core.`type`.TypeReference import java.util.concurrent.atomic.AtomicLong import org.slf4j.LoggerFactory -import com.typesafe.scalalogging.Logger /** * Your listener will have to implement the necessary */ class EventCounterListener(val id: String) extends Listener[MeetingsEvent] { - val log = Logger(LoggerFactory.getLogger(this.getClass)) + val log = LoggerFactory.getLogger(this.getClass) private var eventCount: AtomicLong = new AtomicLong(0); private var callerCount: AtomicLong = new AtomicLong(0); private var slept = false @@ -29,10 +28,10 @@ class EventCounterListener(val id: String) extends Listener[MeetingsEvent] { log.info(s"Received " + events.size.toLong) log.info(s"Has a total of $eventCount events") log.info("#####################################") - if (!slept) { - Thread.sleep(65000) - slept = true - } +// if (!slept) { +// Thread.sleep(65000) +// slept = true +// } log.info(s"Proccessed cursor $cursor") } @@ -61,16 +60,17 @@ object EventListenerExample extends App { * Create the Parameters with the cursor. */ -// val cursor = Cursor("0", "400") + val cursor = Cursor("0", "5000") val parameters = new StreamParameters( -// cursor = Some(cursor) // -// , batchLimit = Some(10) // Maximum number of `Event`s in each chunk (and therefore per partition) of the stream. + cursor = Some(cursor) // +// cursor = None // + , batchLimit = Some(100) // Maximum number of `Event`s in each chunk (and therefore per partition) of the stream. // , streamLimit = Some(500) // Maximum number of `Event`s to stream (over all partitions being streamed in this //connection). - // , batchFlushTimeout = Some(5) // Maximum time in seconds to wait for the flushing of each chunk (per partition). + , batchFlushTimeout = Some(5) // Maximum time in seconds to wait for the flushing of each chunk (per partition). // ,streamKeepAliveLimit=Some(4) - // , streamTimeout = Some(1) + , streamTimeout = Some(5) ) /** @@ -84,9 +84,9 @@ object EventListenerExample extends App { val eventTypeName = "Example-2000" val result = client.subscribe(eventTypeName, parameters, listener) - Thread.sleep(30000) +// Thread.sleep(30000) // client.stop() - client.unsubscribe(eventTypeName, Some("0"), listener) +// client.unsubscribe(eventTypeName, Some("0"), listener) // client.subscribe(eventTypeName, parameters, listener) } \ No newline at end of file diff --git a/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala b/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala index ea84bb3..8ca7864 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala @@ -6,7 +6,7 @@ import java.util.function.Supplier object ClientFactory { import sys.process._ import scala.language.postfixOps - def OAuth2Token(): Option[() => String] = Option(() => "****") + def OAuth2Token(): Option[() => String] = Option(() => "*") def getJavaClient() = builder().buildJavaClient(); @@ -14,8 +14,8 @@ object ClientFactory { private def builder() = { // useSandbox() - useStaging() -// useLocal() +// useStaging() + useLocal() } private def useLocal() = { new ClientBuilder() // @@ -26,7 +26,7 @@ object ClientFactory { } private def useSandbox() = { ClientBuilder() - .withHost("") + .withHost("nakadi-sandbox.aruha-test.zalan.do") .withPort(443) .withSecuredConnection(true) //s .withVerifiedSslCertificate(false) //s @@ -34,6 +34,6 @@ object ClientFactory { } private def useStaging() = { useSandbox() - .withHost("") + .withHost("nakadi-staging.aruha-test.zalan.do") } } \ No newline at end of file diff --git a/it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/EventIntegrationHelper.scala b/it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/EventIntegrationHelper.scala index eaed568..eadcfc8 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/EventIntegrationHelper.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/EventIntegrationHelper.scala @@ -16,9 +16,8 @@ import scala.concurrent.Await import scala.concurrent.duration.DurationInt import scala.concurrent.Future -import com.typesafe.scalalogging.Logger class EventIntegrationHelper(generator: EventGenerator, client: Client) extends WordSpec with Matchers { - private val log = Logger(LoggerFactory.getLogger(this.getClass)) + private val log = LoggerFactory.getLogger(this.getClass) val eventType = generator.eventType def createEventType(): Boolean = { @@ -72,7 +71,7 @@ class EventIntegrationHelper(generator: EventGenerator, client: Client) extends //Private methods private def wasItsuccessfull(in: Option[ClientError], msg: String): Boolean = in match { case Some(clientError) => - log.error("{} => ClientError: {}", generator.eventTypeId + "-" + msg, clientError) + log.error(s"${generator.eventTypeId} - ${msg} => ClientError: $clientError") false case None => true diff --git a/project/Dependencies.scala b/project/Dependencies.scala index 64ed678..44bf0cf 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -14,7 +14,6 @@ object Dependencies { Seq( "com.typesafe" % "config" % "1.3.0", "com.google.guava" % "guava" % "19.0", - "com.typesafe.scala-logging" %% "scala-logging" % "3.1.0", "com.fasterxml.jackson.core" % "jackson-core" % jacksonVersion, "com.fasterxml.jackson.module" %% "jackson-module-scala" % jacksonVersion, "ch.qos.logback" % "logback-classic" % "1.1.3", From ce7e6180845a79e345279eb8898cf19bb22a9d7e Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Tue, 28 Jun 2016 14:59:59 +0200 Subject: [PATCH 111/183] techjira:LAAS-60 Client does not reconnect when server closes because of limiting parameters. Fixes #76 --- client/src/main/resources/logback.xml | 4 +-- .../nakadi/client/actor/ConsumingActor.scala | 6 +--- .../client/actor/SupervisingActor.scala | 14 +++++---- .../client/handler/SubscriptionHandler.scala | 20 +++++++++---- .../examples/scala/EventCreationExample.scala | 4 +-- .../examples/scala/EventListenerExample.scala | 30 ++++++++++--------- project/Dependencies.scala | 2 +- 7 files changed, 44 insertions(+), 36 deletions(-) diff --git a/client/src/main/resources/logback.xml b/client/src/main/resources/logback.xml index c2d5a53..fae69b1 100644 --- a/client/src/main/resources/logback.xml +++ b/client/src/main/resources/logback.xml @@ -4,11 +4,11 @@ - %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + %date{ISO8601} %-5level %logger{36} [%thread] - %msg%n - + diff --git a/client/src/main/scala/org/zalando/nakadi/client/actor/ConsumingActor.scala b/client/src/main/scala/org/zalando/nakadi/client/actor/ConsumingActor.scala index a0b5c5d..cedab7d 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/actor/ConsumingActor.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/actor/ConsumingActor.scala @@ -35,14 +35,10 @@ import akka.actor.Terminated * */ -object ConsumingActor { -} - class ConsumingActor(subscription: SubscriptionKey, handler: EventHandler) extends Actor with ActorLogging with ActorSubscriber { import ModelConverter._ - import ConsumingActor._ var lastCursor: Option[Cursor] = null @@ -67,7 +63,7 @@ class ConsumingActor(subscription: SubscriptionKey, context.stop(self) case OnComplete => log.info("onComplete - connection closed by server - cursor [{}] - [{}]", lastCursor, subscription) - context.stop(self) + context.parent ! UnsubscribeMsg(subscription.eventTypeName,subscription.partition,handler.id()) case Terminated => log.info("Received Terminated msg - subscription [{}] with listener-id [{}] ", subscription, handler.id()) context.stop(self) diff --git a/client/src/main/scala/org/zalando/nakadi/client/actor/SupervisingActor.scala b/client/src/main/scala/org/zalando/nakadi/client/actor/SupervisingActor.scala index 545d59c..057f30e 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/actor/SupervisingActor.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/actor/SupervisingActor.scala @@ -42,7 +42,6 @@ object SupervisingActor { class SupervisingActor(val connection: Connection, val subscriptionHandler: SubscriptionHandler) extends Actor with ActorLogging { import SupervisingActor._ - import ConsumingActor._ val subscriptions: SubscriptionHolder = new SubscriptionHolderImpl() var subscriptionCounter = 0; override val supervisorStrategy: SupervisorStrategy = { @@ -61,12 +60,15 @@ class SupervisingActor(val connection: Connection, val subscriptionHandler: Subs log.info("Received cursor [{}] - subKey [{}]", cursor, subKey) subscriptions.addCursor(subKey, Some(cursor)) case subscrition: SubscribeMsg => - log.info("New subscription [{}]", subscrition) + val before = subscriptions.size subscribe(subscrition) + val after = subscriptions.size + log.info(s"SubscribeMsg - nr of subscriptions before [$before] - after [$after]") case unsubscription: UnsubscribeMsg => - log.debug("Number of subscriptions before unsubscribing [{}]", subscriptions.size) + val before = subscriptions.size unsubscribe(unsubscription) - log.info("Number of subscriptions after unsubscribing [{}]", subscriptions.size) + val after = subscriptions.size + log.info(s"UnsubscribeMsg - nr of subscriptions before [$before] - after [$after]") case Terminated(terminatedActor) => log.info(s"Actor [{}] terminated", terminatedActor.path.name) subscriptions.entryByActor(terminatedActor) match { @@ -83,7 +85,7 @@ class SupervisingActor(val connection: Connection, val subscriptionHandler: Subs val newSubscription = SubscribeMsg(eventTypeName, endpoint, cursor, handler) subscribe(newSubscription) case None => - log.warning("Did not find any SubscriptionKey for [{}]", terminatedActor.path.toString()) + log.warning("Did not find any SubscriptionKey for [{}]", terminatedActor.path.name) case e => log.error("Received unexpected message! [{}]", e) } @@ -120,7 +122,7 @@ class SupervisingActor(val connection: Connection, val subscriptionHandler: Subs val key: SubscriptionKey = SubscriptionKey(eventTypeName, partition) subscriptions.entry(key) match { case Some(SubscriptionEntry(SubscribeMsg(eventTypeName, endpoint, cursor, handler), actor: ActorRef)) => - log.info("Unsubscribing Listener : [{}] from actor: [{}]", handler.id(), actor.path) + log.info("Unsubscribing Listener : [{}] from actor: [{}]", handler.id(), actor.path.name) subscriptions.remove(key) actor ! PoisonPill case None => diff --git a/client/src/main/scala/org/zalando/nakadi/client/handler/SubscriptionHandler.scala b/client/src/main/scala/org/zalando/nakadi/client/handler/SubscriptionHandler.scala index d594eca..8d1dcfc 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/handler/SubscriptionHandler.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/handler/SubscriptionHandler.scala @@ -74,8 +74,8 @@ class SubscriptionHandlerImpl(val connection: Connection) extends SubscriptionHa //Setup a flow for the request val requestFlow = Flow[Option[Cursor]].via(requestCreator(url)) .via(connection.requestFlow()) - .buffer(RECEIVE_BUFFER_SIZE, OverflowStrategy.fail) - .via(requestRenderer) + .buffer(RECEIVE_BUFFER_SIZE, OverflowStrategy.backpressure) + .via(requestRenderer(eventHandler)) .withAttributes(ActorAttributes.supervisionStrategy(decider())) //create the pipeline @@ -85,7 +85,6 @@ class SubscriptionHandlerImpl(val connection: Connection) extends SubscriptionHa result.onComplete { case Success(requestSource) ⇒ - logger.info("Connection established with success!") case Failure(e) ⇒ val msg = "An exception occurred: " + e.getMessage eventHandler.handleOnError(url, Some(msg), e) @@ -102,9 +101,18 @@ class SubscriptionHandlerImpl(val connection: Connection) extends SubscriptionHa private def requestCreator(url: String): Flow[Option[Cursor], HttpRequest, NotUsed] = Flow[Option[Cursor]].map(withHttpRequest(url, _, None, connection.tokenProvider)) - private def requestRenderer: Flow[HttpResponse, Source[ByteString, Any], NotUsed] = - Flow[HttpResponse].filter(x => x.status.isSuccess()) - .map(_.entity.withSizeLimit(Long.MaxValue).dataBytes.via(delimiterFlow)) + private def requestRenderer(handler:EventHandler): Flow[HttpResponse, Source[ByteString, Any], NotUsed] = + Flow[HttpResponse].filter { x => + if (x.status.isSuccess()) { + logger.info("Connection established with success!") + x.status.isSuccess() + } else { + x.entity.toStrict(10.second).map { body => + logger.error(s"http-status: ${ x.status.intValue().toString()}, reason[${ x.status.reason()}], body:[${body.data.decodeString("UTF-8")}]") + } + false + } + }.map(_.entity.withSizeLimit(Long.MaxValue).dataBytes.via(delimiterFlow)) private def delimiterFlow = Flow[ByteString] .via(Framing.delimiter(ByteString(EVENT_DELIMITER), maximumFrameLength = RECEIVE_BUFFER_SIZE, allowTruncation = true)) diff --git a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala index 9ea8f87..0e93fea 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala @@ -72,10 +72,10 @@ object EventCreationExample extends App { // 4. Publish the EventType // System.exit(0) var counter = 0 - for (n <- 1 to 1) { + for (n <- 1 to 5000) { val event = new MeetingsEvent("2016-04-28T13:28:15+00:00", "Hackaton") var events = ListBuffer[MeetingsEvent]() - for (a <- 1 to 1) { + for (a <- 1 to 10) { counter += 1 events += MeetingsEvent("2016-04-28T13:28:15+00:00", "Hackaton" + counter) } diff --git a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala index 3778f81..9b65994 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala @@ -24,22 +24,22 @@ class EventCounterListener(val id: String) extends Listener[MeetingsEvent] { def onReceive(sourceUrl: String, cursor: Cursor, events: Seq[MeetingsEvent]): Unit = { eventCount.addAndGet(events.size.toLong) - log.info("#####################################") - log.info(s"Received " + events.size.toLong) - log.info(s"Has a total of $eventCount events") - log.info("#####################################") +// log.debug("#####################################") +// log.debug(s"Received " + events.size.toLong) + log.debug(s"Has a total of $eventCount events") +// log.debug("#####################################") // if (!slept) { -// Thread.sleep(65000) +// Thread.sleep(1000) // slept = true // } log.info(s"Proccessed cursor $cursor") } def onSubscribed(endpoint: String, cursor: Option[Cursor]): Unit = { - log.info("########## onSubscribed ############") - log.info("Endpoint " + endpoint) - log.info("Cursor " + cursor) - log.info("#####################################") + log.debug("########## onSubscribed ############") + log.debug("Endpoint " + endpoint) + log.debug("Cursor " + cursor) + log.debug("#####################################") } } @@ -60,17 +60,17 @@ object EventListenerExample extends App { * Create the Parameters with the cursor. */ - val cursor = Cursor("0", "5000") + val cursor = Cursor("0", "BEGIN") val parameters = new StreamParameters( cursor = Some(cursor) // // cursor = None // - , batchLimit = Some(100) // Maximum number of `Event`s in each chunk (and therefore per partition) of the stream. + , batchLimit = Some(400) // Maximum number of `Event`s in each chunk (and therefore per partition) of the stream. // , streamLimit = Some(500) // Maximum number of `Event`s to stream (over all partitions being streamed in this //connection). - , batchFlushTimeout = Some(5) // Maximum time in seconds to wait for the flushing of each chunk (per partition). +// , batchFlushTimeout = Some(10) // Maximum time in seconds to wait for the flushing of each chunk (per partition). // ,streamKeepAliveLimit=Some(4) - , streamTimeout = Some(5) + , streamTimeout = Some(0) ) /** @@ -84,7 +84,9 @@ object EventListenerExample extends App { val eventTypeName = "Example-2000" val result = client.subscribe(eventTypeName, parameters, listener) -// Thread.sleep(30000) +// Thread.sleep(10000) +// val listener2 = new EventCounterListener("Test2") +// client.subscribe(eventTypeName, parameters, listener2) // client.stop() // client.unsubscribe(eventTypeName, Some("0"), listener) // client.subscribe(eventTypeName, parameters, listener) diff --git a/project/Dependencies.scala b/project/Dependencies.scala index 44bf0cf..c5c7b59 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -16,7 +16,7 @@ object Dependencies { "com.google.guava" % "guava" % "19.0", "com.fasterxml.jackson.core" % "jackson-core" % jacksonVersion, "com.fasterxml.jackson.module" %% "jackson-module-scala" % jacksonVersion, - "ch.qos.logback" % "logback-classic" % "1.1.3", + "ch.qos.logback" % "logback-classic" % "1.1.7" % "runtime", "com.typesafe.akka" %% "akka-actor" % akkaVersion, "com.typesafe.akka" %% "akka-http-experimental" % akkaVersion, "com.typesafe.akka" %% "akka-stream" % akkaVersion, From ebd61ce465a2400868db40cb0856f296dd00645e Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Wed, 29 Jun 2016 09:30:45 +0200 Subject: [PATCH 112/183] techjira:LAAS-60 Development release 2.0.0-pre-alpha.16 --- project/Build.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/Build.scala b/project/Build.scala index f857788..c75ad33 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -57,7 +57,7 @@ lazy val client = withDefaults( project.settings( name := projectName, organization := "org.zalando.nakadi.client", - version := "2.0.0-pre-alpha.15", + version := "2.0.0-pre-alpha.16", crossPaths := false, scalaVersion := "2.11.8", publishTo := whereToPublishTo(isSnapshot.value), From 1d539a37e8ca3347e57652673b63dc06b3c7857c Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Wed, 29 Jun 2016 12:38:08 +0200 Subject: [PATCH 113/183] techjira:LAAS-60 Added scala-fmt wich must be used to keep the code tidy --- .../zalando/nakadi/client/Serialization.scala | 9 +- .../zalando/nakadi/client/scala/Client.scala | 243 +++++++------ .../nakadi/client/scala/Listener.scala | 7 +- .../client/scala/StreamParameters.scala | 15 +- .../nakadi/client/scala/model/Model.scala | 344 +++++++++--------- .../nakadi/client/actor/ConsumingActor.scala | 59 +-- .../client/actor/SubscriptionHolder.scala | 39 +- .../client/actor/SupervisingActor.scala | 99 +++-- .../client/handler/SubscriptionHandler.scala | 80 ++-- .../client/java/JavaClientHandler.scala | 178 ++++++--- .../model/JavaJacksonJsonMarshaller.scala | 85 +++-- .../nakadi/client/scala/ClientImpl.scala | 167 ++++++--- .../nakadi/client/scala/Connection.scala | 167 ++++++--- .../nakadi/client/scala/HttpFactory.scala | 93 +++-- .../nakadi/client/scala/MessageHandler.scala | 62 ++-- .../model/ScalaJacksonJsonMarshaller.scala | 93 +++-- .../nakadi/client/utils/ClientBuilder.scala | 109 +++--- .../client/utils/FutureConversions.scala | 40 +- .../client/utils/GeneralConversions.scala | 24 +- .../nakadi/client/utils/ModelConverter.scala | 93 ++--- .../org/zalando/nakadi/client/utils/Uri.scala | 16 +- project/plugins.sbt | 2 + 22 files changed, 1233 insertions(+), 791 deletions(-) diff --git a/api/src/main/scala/org/zalando/nakadi/client/Serialization.scala b/api/src/main/scala/org/zalando/nakadi/client/Serialization.scala index 463bdac..a62f6ff 100644 --- a/api/src/main/scala/org/zalando/nakadi/client/Serialization.scala +++ b/api/src/main/scala/org/zalando/nakadi/client/Serialization.scala @@ -1,16 +1,15 @@ package org.zalando.nakadi.client /** - * - */ + * + */ trait Deserializer[T] { def from(from: String): T } /** - * - */ + * + */ trait Serializer[T] { def to(from: T): String } - diff --git a/api/src/main/scala/org/zalando/nakadi/client/scala/Client.scala b/api/src/main/scala/org/zalando/nakadi/client/scala/Client.scala index ecb0dd3..d6f2aef 100644 --- a/api/src/main/scala/org/zalando/nakadi/client/scala/Client.scala +++ b/api/src/main/scala/org/zalando/nakadi/client/scala/Client.scala @@ -7,148 +7,169 @@ import org.zalando.nakadi.client.scala.model._ import org.zalando.nakadi.client._ import com.fasterxml.jackson.core.`type`.TypeReference -case class ClientError(msg: String, status: Option[Integer] = None, exception: Option[Throwable] = None) +case class ClientError(msg: String, + status: Option[Integer] = None, + exception: Option[Throwable] = None) trait Client { /** - * Retrieve monitoring metrics - * - * {{{ - * curl --request GET /metrics - * }}} - */ + * Retrieve monitoring metrics + * + * {{{ + * curl --request GET /metrics + * }}} + */ def getMetrics(): Future[Either[ClientError, Option[Metrics]]] /** - * Returns a list of all registered EventTypes. - * - * {{{ - * curl --request GET /event-types - * }}} - * - */ + * Returns a list of all registered EventTypes. + * + * {{{ + * curl --request GET /event-types + * }}} + * + */ def getEventTypes(): Future[Either[ClientError, Option[Seq[EventType]]]] /** - * Creates a new EventType. - * - * {{{ - * curl --request POST -d @fileWithEventType /event-types - * }}} - * - * @param event - The EventType to create. - * - */ + * Creates a new EventType. + * + * {{{ + * curl --request POST -d @fileWithEventType /event-types + * }}} + * + * @param event - The EventType to create. + * + */ def createEventType(eventType: EventType): Future[Option[ClientError]] /** - * Returns the EventType identified by its name. - * {{{ - * curl --request GET /event-types/{name} - * }}} - * @param eventTypeName - Name of the EventType - */ - def getEventType(eventTypeName: String): Future[Either[ClientError, Option[EventType]]] + * Returns the EventType identified by its name. + * {{{ + * curl --request GET /event-types/{name} + * }}} + * @param eventTypeName - Name of the EventType + */ + def getEventType( + eventTypeName: String): Future[Either[ClientError, Option[EventType]]] + /** - * Updates the EventType identified by its name. - * {{{ - * curl --request PUT -d @fileWithEventType /event-types/{name} - * }}} - * @param eventTypeName - Name of the EventType - * @param event - Event to update - */ - def updateEventType(eventTypeName: String, eventType: EventType): Future[Option[ClientError]] + * Updates the EventType identified by its name. + * {{{ + * curl --request PUT -d @fileWithEventType /event-types/{name} + * }}} + * @param eventTypeName - Name of the EventType + * @param event - Event to update + */ + def updateEventType(eventTypeName: String, + eventType: EventType): Future[Option[ClientError]] + /** - * Deletes an EventType identified by its name. - * - * {{{ - * curl --request DELETE /event-types/{name} - * }}} - * - * @param eventTypeName - Name of the EventType - */ + * Deletes an EventType identified by its name. + * + * {{{ + * curl --request DELETE /event-types/{name} + * }}} + * + * @param eventTypeName - Name of the EventType + */ def deleteEventType(eventTypeName: String): Future[Option[ClientError]] /** - * Publishes multiple Events for the given EventType. - * {{{ - * curl --request POST -d @fileWithEvent /event-types/{name}/events - * }}} - * @param eventTypeName - Name of the EventType - * @param event - Event to publish - */ - def publishEvents[T <: Event](eventTypeName: String, events: Seq[T], ser: Serializer[Seq[T]]): Future[Option[ClientError]] - /** - * Publishes multiple Events for the given EventType. - * {{{ - * curl --request POST -d @fileWithEvent /event-types/{name}/events - * }}} - * @param eventTypeName - Name of the EventType - * @param event - Event to publish - */ - def publishEvents[T <: Event](eventTypeName: String, events: Seq[T]): Future[Option[ClientError]] + * Publishes multiple Events for the given EventType. + * {{{ + * curl --request POST -d @fileWithEvent /event-types/{name}/events + * }}} + * @param eventTypeName - Name of the EventType + * @param event - Event to publish + */ + def publishEvents[T <: Event]( + eventTypeName: String, + events: Seq[T], + ser: Serializer[Seq[T]]): Future[Option[ClientError]] /** - * List the partitions tola the given EventType. - * {{{ - * curl --request GET /event-types/{name}/partitions - * }}} - * @param eventTypeName - Name of the EventType - */ - def getPartitions(eventTypeName: String): Future[Either[ClientError, Option[Seq[Partition]]]] + * Publishes multiple Events for the given EventType. + * {{{ + * curl --request POST -d @fileWithEvent /event-types/{name}/events + * }}} + * @param eventTypeName - Name of the EventType + * @param event - Event to publish + */ + def publishEvents[T <: Event](eventTypeName: String, + events: Seq[T]): Future[Option[ClientError]] /** - * Returns all of the enrichment strategies supported by this installation of Nakadi. - * {{{ - * curl --request GET /registry/enrichment-strategies - * }}} - */ - - def getEnrichmentStrategies(): Future[Either[ClientError, Option[Seq[EventEnrichmentStrategy.Value]]]] + * List the partitions tola the given EventType. + * {{{ + * curl --request GET /event-types/{name}/partitions + * }}} + * @param eventTypeName - Name of the EventType + */ + def getPartitions(eventTypeName: String) + : Future[Either[ClientError, Option[Seq[Partition]]]] /** - * Returns all of the partitioning strategies supported by this installation of Nakadi. - * {{{ - * curl --request GET /registry/partitioning-strategies - * }}} - */ - def getPartitioningStrategies(): Future[Either[ClientError, Option[Seq[PartitionStrategy.Value]]]] + * Returns all of the enrichment strategies supported by this installation of Nakadi. + * {{{ + * curl --request GET /registry/enrichment-strategies + * }}} + */ + def getEnrichmentStrategies() + : Future[Either[ClientError, Option[Seq[EventEnrichmentStrategy.Value]]]] /** - * Shuts down the communication system of the client - */ + * Returns all of the partitioning strategies supported by this installation of Nakadi. + * {{{ + * curl --request GET /registry/partitioning-strategies + * }}} + */ + def getPartitioningStrategies() + : Future[Either[ClientError, Option[Seq[PartitionStrategy.Value]]]] + /** + * Shuts down the communication system of the client + */ def stop(): Option[ClientError] /** - * Registers the subscription of a listener to start streaming events from a partition in non-blocking fashion. - * - * @param eventTypeName - Name of the EventType to listen for. - * @param parameters - Parameters for the streaming of events. - * @param listener - Listener to pass the event to when it is received. - * @param des - Json Marshaller(implicit) to deserialize the event to Json. - */ - def subscribe[T <: Event](eventTypeName: String, parameters: StreamParameters, listener: Listener[T])(implicit des: Deserializer[EventStreamBatch[T]]): Option[ClientError] + * Registers the subscription of a listener to start streaming events from a partition in non-blocking fashion. + * + * @param eventTypeName - Name of the EventType to listen for. + * @param parameters - Parameters for the streaming of events. + * @param listener - Listener to pass the event to when it is received. + * @param des - Json Marshaller(implicit) to deserialize the event to Json. + */ + def subscribe[T <: Event](eventTypeName: String, + parameters: StreamParameters, + listener: Listener[T])( + implicit des: Deserializer[EventStreamBatch[T]]): Option[ClientError] + /** - * Registers the subscription of a listener to start streaming events from a partition in non-blocking fashion. - * - * @param eventTypeName - Name of the EventType to listen for. - * @param parameters - Parameters for the streaming of events. - * @param listener - Listener to pass the event to when it is received. - * @param typeRef - TypeReference/Helper for using with the Jackson-Objectmapper to deserializing the event to json. - */ - def subscribe[T <: Event](eventTypeName: String, parameters: StreamParameters, listener: Listener[T], typeRef: TypeReference[EventStreamBatch[T]]): Option[ClientError] + * Registers the subscription of a listener to start streaming events from a partition in non-blocking fashion. + * + * @param eventTypeName - Name of the EventType to listen for. + * @param parameters - Parameters for the streaming of events. + * @param listener - Listener to pass the event to when it is received. + * @param typeRef - TypeReference/Helper for using with the Jackson-Objectmapper to deserializing the event to json. + */ + def subscribe[T <: Event]( + eventTypeName: String, + parameters: StreamParameters, + listener: Listener[T], + typeRef: TypeReference[EventStreamBatch[T]]): Option[ClientError] + /** - * Removes the subscription of a listener, to stop streaming events from a partition. - * - * @param eventTypeName - Name of the EventType. - * @param partition The partition assigned to this listener. - * @param listener - Listener to unsubscribe from the streaming events. - */ - def unsubscribe[T <: Event](eventTypeName: String, partition: Option[String], listener: Listener[T]): Future[Option[ClientError]] + * Removes the subscription of a listener, to stop streaming events from a partition. + * + * @param eventTypeName - Name of the EventType. + * @param partition The partition assigned to this listener. + * @param listener - Listener to unsubscribe from the streaming events. + */ + def unsubscribe[T <: Event]( + eventTypeName: String, + partition: Option[String], + listener: Listener[T]): Future[Option[ClientError]] } - - - diff --git a/api/src/main/scala/org/zalando/nakadi/client/scala/Listener.scala b/api/src/main/scala/org/zalando/nakadi/client/scala/Listener.scala index 637b934..e520537 100644 --- a/api/src/main/scala/org/zalando/nakadi/client/scala/Listener.scala +++ b/api/src/main/scala/org/zalando/nakadi/client/scala/Listener.scala @@ -3,12 +3,9 @@ package org.zalando.nakadi.client.scala import org.zalando.nakadi.client.scala.model.Cursor import org.zalando.nakadi.client.scala.model.Event - - - trait Listener[T] { def id: String def onReceive(endpoint: String, cursor: Cursor, events: Seq[T]): Unit - def onSubscribed(endpoint: String,cursor: Option[Cursor]):Unit + def onSubscribed(endpoint: String, cursor: Option[Cursor]): Unit def onError(endpoint: String, error: Option[ClientError]): Unit -} \ No newline at end of file +} diff --git a/api/src/main/scala/org/zalando/nakadi/client/scala/StreamParameters.scala b/api/src/main/scala/org/zalando/nakadi/client/scala/StreamParameters.scala index 4a5e7b4..27a4fa3 100644 --- a/api/src/main/scala/org/zalando/nakadi/client/scala/StreamParameters.scala +++ b/api/src/main/scala/org/zalando/nakadi/client/scala/StreamParameters.scala @@ -2,11 +2,10 @@ package org.zalando.nakadi.client.scala import org.zalando.nakadi.client.scala.model.Cursor -case class StreamParameters( - cursor: Option[Cursor]=None, - batchLimit: Option[Integer] = None, - streamLimit: Option[Integer] = None, - batchFlushTimeout: Option[Integer] = None, - streamTimeout: Option[Integer] = None, - streamKeepAliveLimit: Option[Integer] = None, - flowId: Option[String] = None) \ No newline at end of file +case class StreamParameters(cursor: Option[Cursor] = None, + batchLimit: Option[Integer] = None, + streamLimit: Option[Integer] = None, + batchFlushTimeout: Option[Integer] = None, + streamTimeout: Option[Integer] = None, + streamKeepAliveLimit: Option[Integer] = None, + flowId: Option[String] = None) diff --git a/api/src/main/scala/org/zalando/nakadi/client/scala/model/Model.scala b/api/src/main/scala/org/zalando/nakadi/client/scala/model/Model.scala index 9befe22..eb99fe0 100644 --- a/api/src/main/scala/org/zalando/nakadi/client/scala/model/Model.scala +++ b/api/src/main/scala/org/zalando/nakadi/client/scala/model/Model.scala @@ -8,171 +8,168 @@ import com.fasterxml.jackson.core.`type`.TypeReference // Updated untill commit 57cace5 /** - * The Event definition will be externalized in future versions of this document. - * A basic payload of an Event. The actual schema is dependent on the information configured for the - * EventType, as is its enforcement (see POST /event-types). Setting of metadata properties are - * dependent on the configured enrichment as well. - * For explanation on default configurations of validation and enrichment, see documentation of - * `EventType.type`. - * For concrete examples of what will be enforced by Nakadi see the objects BusinessEvent and - * DataChangeEvent below. - * @param eventType - * @param additionalProperties Default value is true - * @param title - */ -trait Event { + * The Event definition will be externalized in future versions of this document. + * A basic payload of an Event. The actual schema is dependent on the information configured for the + * EventType, as is its enforcement (see POST /event-types). Setting of metadata properties are + * dependent on the configured enrichment as well. + * For explanation on default configurations of validation and enrichment, see documentation of + * `EventType.type`. + * For concrete examples of what will be enforced by Nakadi see the objects BusinessEvent and + * DataChangeEvent below. + * @param eventType + * @param additionalProperties Default value is true + * @param title + */ +trait Event {} -} /** - * @param partition Id of the partition pointed to by this cursor. - * @param offset Offset of the event being pointed to. - */ -case class Cursor( - partition: String, - offset: String) + * @param partition Id of the partition pointed to by this cursor. + * @param offset Offset of the event being pointed to. + */ +case class Cursor(partition: String, offset: String) /** - * - * Metadata for this Event. Contains commons fields for both Business and DataChange Events. Most are enriched by Nakadi upon reception, but they in general MIGHT be set by the client. - * @param eid Identifier of this Event. Clients are allowed to generate this and this SHOULD be guaranteed to be unique from the perspective of the producer. Consumers MIGHT use this value to assert uniqueness of reception of the Event. - * @param eventType The EventType of this Event. This is enriched by Nakadi on reception of the Event based on the endpoint where the Producer sent the Event to. If provided MUST match the endpoint. Failure to do so will cause rejection of the Event. - * @param occurredAt Timestamp of creation of the Event generated by the producer. - * @param receivedAt Timestamp of the reception of the Event by Nakadi. This is enriched upon reception of the Event. If set by the producer Event will be rejected. - * @param parentEids Event identifier of the Event that caused the generation of this Event. Set by the producer. - * @param flowId The flow-id of the producer of this Event. As this is usually a HTTP header, this is enriched from the header into the metadata by Nakadi to avoid clients having to explicitly copy this. - * @param metadata This Metadata contains common fields unrelated to Nakadi logic. Should be mainly enriched by the Consumer. - * - */ + * + * Metadata for this Event. Contains commons fields for both Business and DataChange Events. Most are enriched by Nakadi upon reception, but they in general MIGHT be set by the client. + * @param eid Identifier of this Event. Clients are allowed to generate this and this SHOULD be guaranteed to be unique from the perspective of the producer. Consumers MIGHT use this value to assert uniqueness of reception of the Event. + * @param eventType The EventType of this Event. This is enriched by Nakadi on reception of the Event based on the endpoint where the Producer sent the Event to. If provided MUST match the endpoint. Failure to do so will cause rejection of the Event. + * @param occurredAt Timestamp of creation of the Event generated by the producer. + * @param receivedAt Timestamp of the reception of the Event by Nakadi. This is enriched upon reception of the Event. If set by the producer Event will be rejected. + * @param parentEids Event identifier of the Event that caused the generation of this Event. Set by the producer. + * @param flowId The flow-id of the producer of this Event. As this is usually a HTTP header, this is enriched from the header into the metadata by Nakadi to avoid clients having to explicitly copy this. + * @param metadata This Metadata contains common fields unrelated to Nakadi logic. Should be mainly enriched by the Consumer. + * + */ case class EventMetadata( - eid: String, - @JsonProperty("event_type") eventTypeName: Option[String], - occurredAt: String, - receivedAt: Option[String], - parentEids: Seq[String], - flowId: Option[String], - partition: Option[String]) + eid: String, + @JsonProperty("event_type") eventTypeName: Option[String], + occurredAt: String, + receivedAt: Option[String], + parentEids: Seq[String], + flowId: Option[String], + partition: Option[String]) /** - * A Business Event. Usually represents a status transition in a Business process. - * - */ + * A Business Event. Usually represents a status transition in a Business process. + * + */ trait BusinessEvent extends Event { def metadata(): Option[EventMetadata] } /** - * Indicators of a `DataChangeEvent`'s referred data type and the type of operations done on them. - * @param dataType The datatype of the `DataChangeEvent`. - * @param dataOperation The type of operation executed on the entity. * C: Creation * U: Update * D: Deletion * S: Snapshot - */ + * Indicators of a `DataChangeEvent`'s referred data type and the type of operations done on them. + * @param dataType The datatype of the `DataChangeEvent`. + * @param dataOperation The type of operation executed on the entity. * C: Creation * U: Update * D: Deletion * S: Snapshot + */ trait DataChangeEventQualifier { def dataType(): String def dataOperation(): DataOperation.Value } /** - * A Data change Event. Represents a change on a resource. - * - * @param data The payload of the type - * @param eventQualifier Indicators of a `DataChangeEvent`'s referred data type and the type of operations done on them. - * @param metadata Metadata for this Event. Contains commons fields for both Business and DataChange Events. Most are enriched by Nakadi upon reception, but they in general MIGHT be set by the client - */ -case class DataChangeEvent[T]( - data: T, - dataType: String, - @JsonProperty("data_op")@JsonScalaEnumeration(classOf[DataOperationType]) dataOperation: DataOperation.Value, - metadata: Option[EventMetadata]) extends DataChangeEventQualifier with Event + * A Data change Event. Represents a change on a resource. + * + * @param data The payload of the type + * @param eventQualifier Indicators of a `DataChangeEvent`'s referred data type and the type of operations done on them. + * @param metadata Metadata for this Event. Contains commons fields for both Business and DataChange Events. Most are enriched by Nakadi upon reception, but they in general MIGHT be set by the client + */ +case class DataChangeEvent[T](data: T, + dataType: String, + @JsonProperty("data_op") @JsonScalaEnumeration( + classOf[DataOperationType]) dataOperation: DataOperation.Value, + metadata: Option[EventMetadata]) + extends DataChangeEventQualifier + with Event /** - * @ param problemType An absolute URI that identifies the problem type. When dereferenced, it SHOULD provide human-readable API documentation for the problem type (e.g., using HTML). This Problem object is the same as provided by https://github.com/zalando/problem - * @ param title A short, summary of the problem type. Written in English and readable for engineers (usually not suited for non technical stakeholders and not localized) - * @ param status The HTTP status code generated by the origin server for this occurrence of the problem. - * @ param detail A human readable explanation specific to this occurrence of the problem. - * @ param instance An absolute URI that identifies the specific occurrence of the problem. It may or may not yield further information if dereferenced. - */ -case class Problem( - @JsonProperty("type") problemType: String, - title: String, - status: Integer, - detail: Option[String], - instance: Option[String]) + * @ param problemType An absolute URI that identifies the problem type. When dereferenced, it SHOULD provide human-readable API documentation for the problem type (e.g., using HTML). This Problem object is the same as provided by https://github.com/zalando/problem + * @ param title A short, summary of the problem type. Written in English and readable for engineers (usually not suited for non technical stakeholders and not localized) + * @ param status The HTTP status code generated by the origin server for this occurrence of the problem. + * @ param detail A human readable explanation specific to this occurrence of the problem. + * @ param instance An absolute URI that identifies the specific occurrence of the problem. It may or may not yield further information if dereferenced. + */ +case class Problem(@JsonProperty("type") problemType: String, + title: String, + status: Integer, + detail: Option[String], + instance: Option[String]) -case class Metrics(version:String,gauges: Map[String, Any]) +case class Metrics(version: String, gauges: Map[String, Any]) /** - * Partition information. Can be helpful when trying to start a stream using an unmanaged API. This information is not related to the state of the consumer clients. - * @param partition The partition's id. - * @param oldestAvailableOffset An offset of the oldest available Event in that partition. This value will be changing upon removal of Events from the partition by the background archiving/cleanup mechanism. - * @param newestAvailableOffset An offset of the newest available Event in that partition. This value will be changing upon reception of new events for this partition by Nakadi. This value can be used to construct a cursor when opening streams (see `GET /event-type/{name}/events` for details). Might assume the special name BEGIN, meaning a pointer to the offset of the oldest available event in the partition. - */ -case class Partition( - partition: String, - oldestAvailableOffset: String, - newestAvailableOffset: String) + * Partition information. Can be helpful when trying to start a stream using an unmanaged API. This information is not related to the state of the consumer clients. + * @param partition The partition's id. + * @param oldestAvailableOffset An offset of the oldest available Event in that partition. This value will be changing upon removal of Events from the partition by the background archiving/cleanup mechanism. + * @param newestAvailableOffset An offset of the newest available Event in that partition. This value will be changing upon reception of new events for this partition by Nakadi. This value can be used to construct a cursor when opening streams (see `GET /event-type/{name}/events` for details). Might assume the special name BEGIN, meaning a pointer to the offset of the oldest available event in the partition. + */ +case class Partition(partition: String, + oldestAvailableOffset: String, + newestAvailableOffset: String) /** - * One chunk of events in a stream. A batch consists of an array of `Event`s plus a `Cursor` pointing to the offset of the last Event in the stream. The size of the array of Event is limited by the parameters used to initialize a Stream. If acting as a keep alive message (see `GET /event-type/{name}/events`) the events array will be omitted. Sequential batches might repeat the cursor if no new events arrive. - * @param cursor The cursor point to an event in the stream. - * @param events The Event definition will be externalized in future versions of this document. A basic payload of an Event. The actual schema is dependent on the information configured for the EventType, as is its enforcement (see POST /event-types). Setting of metadata properties are dependent on the configured enrichment as well. For explanation on default configurations of validation and enrichment, see documentation of `EventType.type`. For concrete examples of what will be enforced by Nakadi see the objects - * sEvent and DataChangeEvent below. - */ -case class EventStreamBatch[T <: Event]( - cursor: Cursor, - events: Option[Seq[T]]) + * One chunk of events in a stream. A batch consists of an array of `Event`s plus a `Cursor` pointing to the offset of the last Event in the stream. The size of the array of Event is limited by the parameters used to initialize a Stream. If acting as a keep alive message (see `GET /event-type/{name}/events`) the events array will be omitted. Sequential batches might repeat the cursor if no new events arrive. + * @param cursor The cursor point to an event in the stream. + * @param events The Event definition will be externalized in future versions of this document. A basic payload of an Event. The actual schema is dependent on the information configured for the EventType, as is its enforcement (see POST /event-types). Setting of metadata properties are dependent on the configured enrichment as well. For explanation on default configurations of validation and enrichment, see documentation of `EventType.type`. For concrete examples of what will be enforced by Nakadi see the objects + * sEvent and DataChangeEvent below. + */ +case class EventStreamBatch[T <: Event](cursor: Cursor, events: Option[Seq[T]]) /** - * An event type defines the schema and its runtime properties. - * @param name Name of this EventType. Encodes the owner/responsible for this EventType. - * @param owningApplication Indicator of the Application owning this `EventType`. - * @param category Defines the category of this EventType. - * @param enrichmentStrategies Determines the enrichment to be performed on an Event upon reception. - * @param partitionStrategy Determines how the assignment of the event to a Partition should be handled. - * @param schema The schema for this EventType. - * @param dataKeyFields Indicators of the path of the properties that constitute the primary key (identifier) of the data within this Event. - * @param partitionKeyFields Indicator of the field used for guaranteeing the ordering of Events of this type (used by the PartitionResolutionStrategy). - * @param statistics Statistics of this EventType used for optimization purposes. - * - */ + * An event type defines the schema and its runtime properties. + * @param name Name of this EventType. Encodes the owner/responsible for this EventType. + * @param owningApplication Indicator of the Application owning this `EventType`. + * @param category Defines the category of this EventType. + * @param enrichmentStrategies Determines the enrichment to be performed on an Event upon reception. + * @param partitionStrategy Determines how the assignment of the event to a Partition should be handled. + * @param schema The schema for this EventType. + * @param dataKeyFields Indicators of the path of the properties that constitute the primary key (identifier) of the data within this Event. + * @param partitionKeyFields Indicator of the field used for guaranteeing the ordering of Events of this type (used by the PartitionResolutionStrategy). + * @param statistics Statistics of this EventType used for optimization purposes. + * + */ case class EventType( - name: String, - owningApplication: String, - @JsonScalaEnumeration(classOf[EventTypeCategoryType]) category: EventTypeCategory.Value, - @JsonScalaEnumeration(classOf[EventEnrichmentStrategyType]) enrichmentStrategies: Seq[EventEnrichmentStrategy.Value], - @JsonScalaEnumeration(classOf[PartitionStrategyType]) partitionStrategy: Option[PartitionStrategy.Value], - schema: EventTypeSchema, - dataKeyFields: Seq[String], - partitionKeyFields: Seq[String], - @JsonProperty("default_statistics") statistics: Option[EventTypeStatistics]) + name: String, + owningApplication: String, + @JsonScalaEnumeration(classOf[EventTypeCategoryType]) category: EventTypeCategory.Value, + @JsonScalaEnumeration(classOf[EventEnrichmentStrategyType]) enrichmentStrategies: Seq[ + EventEnrichmentStrategy.Value], + @JsonScalaEnumeration(classOf[PartitionStrategyType]) partitionStrategy: Option[ + PartitionStrategy.Value], + schema: EventTypeSchema, + dataKeyFields: Seq[String], + partitionKeyFields: Seq[String], + @JsonProperty("default_statistics") statistics: Option[ + EventTypeStatistics]) /** - * The schema for an EventType, expected to be a json schema in yaml - * format (other formats might be added in the future). - * @param schemaType The type of schema definition (avro, json-schema, etc). - * @param schema The schema as string in the syntax defined in the field type. - * Failure to respect the syntax will fail any operation on an EventType. - */ -case class EventTypeSchema( - @JsonProperty("type")@JsonScalaEnumeration(classOf[SchemaTypeType]) schemaType: SchemaType.Value, - schema: String) + * The schema for an EventType, expected to be a json schema in yaml + * format (other formats might be added in the future). + * @param schemaType The type of schema definition (avro, json-schema, etc). + * @param schema The schema as string in the syntax defined in the field type. + * Failure to respect the syntax will fail any operation on an EventType. + */ +case class EventTypeSchema(@JsonProperty("type") @JsonScalaEnumeration( + classOf[SchemaTypeType]) schemaType: SchemaType.Value, + schema: String) /** - * Operational statistics for an EventType. This data is generated by Nakadi - * based on the runtime and might be used to guide changes in internal parameters. - * @param messagesPerMinute - Write rate for events of this EventType. This rate encompasses all producers of this EventType for a Nakadi cluster. Measured in kb/minutes. - * @param messageSize - Average message size for each Event of this EventType. Includes in the count the whole serialized form of the event, including metadata. Measured in bytes. - * @param readParallelism - Amount of parallel readers (consumers) to this EventType. - * @param writeParallelism - Amount of parallel writers (producers) to this EventType. - * - */ -case class EventTypeStatistics( - messagesPerMinute: Integer, - messageSize: Integer, - readParallelism: Integer, - writeParallelism: Integer) + * Operational statistics for an EventType. This data is generated by Nakadi + * based on the runtime and might be used to guide changes in internal parameters. + * @param messagesPerMinute - Write rate for events of this EventType. This rate encompasses all producers of this EventType for a Nakadi cluster. Measured in kb/minutes. + * @param messageSize - Average message size for each Event of this EventType. Includes in the count the whole serialized form of the event, including metadata. Measured in bytes. + * @param readParallelism - Amount of parallel readers (consumers) to this EventType. + * @param writeParallelism - Amount of parallel writers (producers) to this EventType. + * + */ +case class EventTypeStatistics(messagesPerMinute: Integer, + messageSize: Integer, + readParallelism: Integer, + writeParallelism: Integer) /** - * Defines a rule for the resolution of incoming Events into partitions. Rules might require additional parameters; see the `doc` field of the existing rules for details. See `GET /registry/partition-strategies` for a list of available rules. - */ + * Defines a rule for the resolution of incoming Events into partitions. Rules might require additional parameters; see the `doc` field of the existing rules for details. See `GET /registry/partition-strategies` for a list of available rules. + */ case object PartitionStrategy extends Enumeration { type PartitionStrategy = Value val HASH = Value("hash") @@ -182,41 +179,42 @@ case object PartitionStrategy extends Enumeration { class PartitionStrategyType extends TypeReference[PartitionStrategy.type] /** - * Defines a rule for transformation of an incoming Event. No existing fields might be modified. In practice this is used to set automatically values in the Event upon reception (i.e. set a reception timestamp on the Event). Rules might require additional parameters; see the `doc` field of the existing rules for details. See GET /registry/enrichment-strategies for a list of available rules. - */ + * Defines a rule for transformation of an incoming Event. No existing fields might be modified. In practice this is used to set automatically values in the Event upon reception (i.e. set a reception timestamp on the Event). Rules might require additional parameters; see the `doc` field of the existing rules for details. See GET /registry/enrichment-strategies for a list of available rules. + */ case object EventEnrichmentStrategy extends Enumeration { type EventEnrichmentStrategy = Value val METADATA = Value("metadata_enrichment") } -class EventEnrichmentStrategyType extends TypeReference[EventEnrichmentStrategy.type] +class EventEnrichmentStrategyType + extends TypeReference[EventEnrichmentStrategy.type] /** - * A status corresponding to one individual Event's publishing attempt. - * @param eid Eid of the corresponding item. Will be absent if missing on the incoming Event. - * @param publishingStatus Indicator of the submission of the Event within a Batch. - SUBMITTED indicates successful submission, including commit on he underlying broker. - FAILED indicates the message submission was not possible and can be resubmitted if so desired. - ABORTED indicates that the submission of this item was not attempted any further due to a failure on another item in the batch. - * @param step Indicator of the step in the publishing process this Event reached. In Items that FAILED means the step of the failure. - NONE indicates that nothing was yet attempted for the publishing of this Event. Should be present only in the case of aborting the publishing during the validation of another (previous) Event. - VALIDATING, ENRICHING, PARTITIONING and PUBLISHING indicate all the corresponding steps of the publishing process. - * @param detail Human readable information about the failure on this item. Items that are not SUBMITTED should have a description. - * - */ + * A status corresponding to one individual Event's publishing attempt. + * @param eid Eid of the corresponding item. Will be absent if missing on the incoming Event. + * @param publishingStatus Indicator of the submission of the Event within a Batch. - SUBMITTED indicates successful submission, including commit on he underlying broker. - FAILED indicates the message submission was not possible and can be resubmitted if so desired. - ABORTED indicates that the submission of this item was not attempted any further due to a failure on another item in the batch. + * @param step Indicator of the step in the publishing process this Event reached. In Items that FAILED means the step of the failure. - NONE indicates that nothing was yet attempted for the publishing of this Event. Should be present only in the case of aborting the publishing during the validation of another (previous) Event. - VALIDATING, ENRICHING, PARTITIONING and PUBLISHING indicate all the corresponding steps of the publishing process. + * @param detail Human readable information about the failure on this item. Items that are not SUBMITTED should have a description. + * + */ case class BatchItemResponse( - eid: Option[String], - @JsonScalaEnumeration(classOf[BatchItemPublishingStatusType]) publishingStatus: BatchItemPublishingStatus.Value, - @JsonScalaEnumeration(classOf[BatchItemStepType]) step: Option[BatchItemStep.Value], - detail: Option[String]) + eid: Option[String], + @JsonScalaEnumeration(classOf[BatchItemPublishingStatusType]) publishingStatus: BatchItemPublishingStatus.Value, + @JsonScalaEnumeration(classOf[BatchItemStepType]) step: Option[ + BatchItemStep.Value], + detail: Option[String]) ///////////////////////////////// // ENUMS //////////////////////// ///////////////////////////////// /** - * Identifier for the type of operation to executed on the entity.
          - * C: Creation
          - * U: Update
          - * D: Deletion
          - * S: Snapshot

          - * Values = CREATE("C"), UPDATE("U"), DELETE("D"), SNAPSHOT("S") - */ - + * Identifier for the type of operation to executed on the entity.
          + * C: Creation
          + * U: Update
          + * D: Deletion
          + * S: Snapshot

          + * Values = CREATE("C"), UPDATE("U"), DELETE("D"), SNAPSHOT("S") + */ case object DataOperation extends Enumeration { type DataOperation = Value val CREATE = Value("C") @@ -228,9 +226,9 @@ case object DataOperation extends Enumeration { class DataOperationType extends TypeReference[DataOperation.type] /** - * Defines the category of an EventType.
          - * Values = UNDEFINED("undefined") DATA("data") BUSINESS("business") - */ + * Defines the category of an EventType.
          + * Values = UNDEFINED("undefined") DATA("data") BUSINESS("business") + */ case object EventTypeCategory extends Enumeration { type EventTypeCategory = Value val UNDEFINED = Value("undefined") @@ -239,14 +237,15 @@ case object EventTypeCategory extends Enumeration { } class EventTypeCategoryType extends TypeReference[EventTypeCategory.type] + /** - * Indicator of the submission of the Event within a Batch.
          - * - SUBMITTED indicates successful submission, including commit on he underlying broker.
          - * - FAILED indicates the message submission was not possible and can be resubmitted if so desired.
          - * - ABORTED indicates that the submission of this item was not attempted any further due to a failure - * on another item in the batch.

          - * Values = SUBMITTED("SUBMITTED") FAILED("FAILED") ABORTED("ABORTED") - */ + * Indicator of the submission of the Event within a Batch.
          + * - SUBMITTED indicates successful submission, including commit on he underlying broker.
          + * - FAILED indicates the message submission was not possible and can be resubmitted if so desired.
          + * - ABORTED indicates that the submission of this item was not attempted any further due to a failure + * on another item in the batch.

          + * Values = SUBMITTED("SUBMITTED") FAILED("FAILED") ABORTED("ABORTED") + */ case object BatchItemPublishingStatus extends Enumeration { type BatchItemPublishingStatus = Value val SUBMITTED = Value("submitted") @@ -254,17 +253,18 @@ case object BatchItemPublishingStatus extends Enumeration { val ABORTED = Value("aborted") } -class BatchItemPublishingStatusType extends TypeReference[BatchItemPublishingStatus.type] +class BatchItemPublishingStatusType + extends TypeReference[BatchItemPublishingStatus.type] /** - * Indicator of the step in the pulbishing process this Event reached. - * In Items that FAILED means the step of the failure. - * - NONE indicates that nothing was yet attempted for the publishing of this Event. Should be present - * only in the case of aborting the publishing during the validation of another (previous) Event.
          - * - VALIDATING, ENRICHING, PARTITIONING and PUBLISHING indicate all the corresponding steps of the - * publishing process.

          - * Values = NONE("none"), VALIDATING("validating"), ENRICHING("enriching"), PARTITIONING("partitioning"), PUBLISHING("publishing") - */ + * Indicator of the step in the pulbishing process this Event reached. + * In Items that FAILED means the step of the failure. + * - NONE indicates that nothing was yet attempted for the publishing of this Event. Should be present + * only in the case of aborting the publishing during the validation of another (previous) Event.
          + * - VALIDATING, ENRICHING, PARTITIONING and PUBLISHING indicate all the corresponding steps of the + * publishing process.

          + * Values = NONE("none"), VALIDATING("validating"), ENRICHING("enriching"), PARTITIONING("partitioning"), PUBLISHING("publishing") + */ case object BatchItemStep extends Enumeration { type BatchItemStep = Value val NONE = Value("none") diff --git a/client/src/main/scala/org/zalando/nakadi/client/actor/ConsumingActor.scala b/client/src/main/scala/org/zalando/nakadi/client/actor/ConsumingActor.scala index cedab7d..91ceecb 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/actor/ConsumingActor.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/actor/ConsumingActor.scala @@ -22,36 +22,40 @@ import akka.util.ByteString import org.zalando.nakadi.client.utils.ModelConverter import org.zalando.nakadi.client.scala.EventHandler import org.zalando.nakadi.client.scala.ErrorResult -import org.zalando.nakadi.client.java.model.{ Event => JEvent } +import org.zalando.nakadi.client.java.model.{Event => JEvent} import SupervisingActor._ import akka.actor.Terminated /** - * This actor serves as Sink for the pipeline.
          - * 1. It receives the message and the cursor from the payload. - * 2. It tries to deserialize the message to EventStreamBatch, containing a cursor and a sequence of Events. - * 3. Passes the deserialized sequence of events to the listener. - * 4. Sends the received cursor from the Publisher, to be passed to the pipeline. - * - */ - -class ConsumingActor(subscription: SubscriptionKey, - handler: EventHandler) - extends Actor with ActorLogging with ActorSubscriber { + * This actor serves as Sink for the pipeline.
          + * 1. It receives the message and the cursor from the payload. + * 2. It tries to deserialize the message to EventStreamBatch, containing a cursor and a sequence of Events. + * 3. Passes the deserialized sequence of events to the listener. + * 4. Sends the received cursor from the Publisher, to be passed to the pipeline. + * + */ +class ConsumingActor(subscription: SubscriptionKey, handler: EventHandler) + extends Actor + with ActorLogging + with ActorSubscriber { import ModelConverter._ var lastCursor: Option[Cursor] = null - override protected def requestStrategy: RequestStrategy = new RequestStrategy { - override def requestDemand(remainingRequested: Int): Int = { - Math.max(remainingRequested, 10) + override protected def requestStrategy: RequestStrategy = + new RequestStrategy { + override def requestDemand(remainingRequested: Int): Int = { + Math.max(remainingRequested, 10) + } } - } override def receive: Receive = { case OnNext(msg: ByteString) => val message = msg.utf8String - log.info("Event - prevCursor [{}] - [{}] - msg [{}]", lastCursor, subscription, message) + log.info("Event - prevCursor [{}] - [{}] - msg [{}]", + lastCursor, + subscription, + message) handler.handleOnReceive(subscription.toString(), message) match { case Right(cursor) => lastCursor = Some(cursor) @@ -59,13 +63,23 @@ class ConsumingActor(subscription: SubscriptionKey, case Left(error) => log.error(error.error.getMessage) } case OnError(err: Throwable) => - log.error("onError - cursor [{}] - [{}] - error [{}]", lastCursor, subscription, err.getMessage) + log.error("onError - cursor [{}] - [{}] - error [{}]", + lastCursor, + subscription, + err.getMessage) context.stop(self) case OnComplete => - log.info("onComplete - connection closed by server - cursor [{}] - [{}]", lastCursor, subscription) - context.parent ! UnsubscribeMsg(subscription.eventTypeName,subscription.partition,handler.id()) + log.info("onComplete - connection closed by server - cursor [{}] - [{}]", + lastCursor, + subscription) + context.parent ! UnsubscribeMsg(subscription.eventTypeName, + subscription.partition, + handler.id()) case Terminated => - log.info("Received Terminated msg - subscription [{}] with listener-id [{}] ", subscription, handler.id()) + log.info( + "Received Terminated msg - subscription [{}] with listener-id [{}] ", + subscription, + handler.id()) context.stop(self) case a => log.error("Could not handle message: [{}]", a) @@ -73,6 +87,3 @@ class ConsumingActor(subscription: SubscriptionKey, } } - - - diff --git a/client/src/main/scala/org/zalando/nakadi/client/actor/SubscriptionHolder.scala b/client/src/main/scala/org/zalando/nakadi/client/actor/SubscriptionHolder.scala index f4bd2ff..1c8b155 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/actor/SubscriptionHolder.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/actor/SubscriptionHolder.scala @@ -8,8 +8,8 @@ import com.google.common.base.Preconditions import org.slf4j.Logger /** - * Tracks subscriptions of SubscriptionEntries and Cursors. - */ + * Tracks subscriptions of SubscriptionEntries and Cursors. + */ trait SubscriptionHolder { import SupervisingActor._ def addCursor(key: SubscriptionKey, cursor: Option[Cursor]): Unit @@ -55,19 +55,24 @@ class SubscriptionHolderImpl extends SubscriptionHolder { } } - private def removeCursor(key: SubscriptionKey): Unit = cursors.get(key) match { - case None => logger.warn(s"Cursor subscription for $key not found!") - case Some(cursor) => - cursors = cursors - (key) - logger.info(s"Removed cursor [$cursor] with subscription [$key]!") - } + private def removeCursor(key: SubscriptionKey): Unit = + cursors.get(key) match { + case None => logger.warn(s"Cursor subscription for $key not found!") + case Some(cursor) => + cursors = cursors - (key) + logger.info(s"Removed cursor [$cursor] with subscription [$key]!") + } - private def removeSubscriptionKey(actor: ActorRef): Unit = actors.get(actor.path.toString()) match { - case None => logger.warn(s"SubscriptionKey for actor [${actor.path.toString()}] not found!") - case Some(key) => - actors = actors - actor.path.toString() - logger.info(s"Removed subscriptionKey [$key] for actor [${actor.path.toString()}]!") - } + private def removeSubscriptionKey(actor: ActorRef): Unit = + actors.get(actor.path.toString()) match { + case None => + logger.warn( + s"SubscriptionKey for actor [${actor.path.toString()}] not found!") + case Some(key) => + actors = actors - actor.path.toString() + logger.info( + s"Removed subscriptionKey [$key] for actor [${actor.path.toString()}]!") + } def entry(key: SubscriptionKey): Option[SubscriptionEntry] = { subscriptions.get(key) @@ -85,7 +90,9 @@ class SubscriptionHolderImpl extends SubscriptionHolder { } def cursorByActor(actor: ActorRef): Option[Cursor] = { - actors.get(actor.path.toString()).flatMap(x => cursors.get(x).flatMap(a => a)) + actors + .get(actor.path.toString()) + .flatMap(x => cursors.get(x).flatMap(a => a)) } -} \ No newline at end of file +} diff --git a/client/src/main/scala/org/zalando/nakadi/client/actor/SupervisingActor.scala b/client/src/main/scala/org/zalando/nakadi/client/actor/SupervisingActor.scala index 057f30e..5c5843a 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/actor/SupervisingActor.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/actor/SupervisingActor.scala @@ -25,32 +25,41 @@ import akka.stream.actor.ActorSubscriber import akka.util.ByteString object SupervisingActor { - case class SubscribeMsg(eventTypeName: String, endpoint: String, cursor: Option[Cursor], handler: EventHandler) { + case class SubscribeMsg(eventTypeName: String, + endpoint: String, + cursor: Option[Cursor], + handler: EventHandler) { override def toString(): String = s"SubscriptionKey(eventTypeName: $eventTypeName - endpoint: $endpoint - cursor: $cursor - listener: ${handler.id})" } - case class UnsubscribeMsg(eventTypeName: String, partition: Option[String], eventHandlerId: String) + case class UnsubscribeMsg(eventTypeName: String, + partition: Option[String], + eventHandlerId: String) case class OffsetMsg(cursor: Cursor, subKey: SubscriptionKey) case class SubscriptionKey(eventTypeName: String, partition: Option[String]) { override def toString(): String = partition match { - case Some(p) => s"SubscriptionKey(eventTypeName:$eventTypeName - Partition:$p)"; - case None => s"SubscriptionKey(eventTypeName:$eventTypeName)"; + case Some(p) => + s"SubscriptionKey(eventTypeName:$eventTypeName - Partition:$p)"; + case None => s"SubscriptionKey(eventTypeName:$eventTypeName)"; } } case class SubscriptionEntry(subuscription: SubscribeMsg, actor: ActorRef) } -class SupervisingActor(val connection: Connection, val subscriptionHandler: SubscriptionHandler) extends Actor with ActorLogging { +class SupervisingActor(val connection: Connection, + val subscriptionHandler: SubscriptionHandler) + extends Actor + with ActorLogging { import SupervisingActor._ val subscriptions: SubscriptionHolder = new SubscriptionHolderImpl() var subscriptionCounter = 0; override val supervisorStrategy: SupervisorStrategy = { def defaultDecider: Decider = { case _: ActorInitializationException ⇒ Stop - case _: ActorKilledException ⇒ Stop - case _: IllegalStateException ⇒ Stop - case _: Exception ⇒ Stop - case _: Throwable ⇒ Stop + case _: ActorKilledException ⇒ Stop + case _: IllegalStateException ⇒ Stop + case _: Exception ⇒ Stop + case _: Throwable ⇒ Stop } OneForOneStrategy()(defaultDecider) } @@ -63,29 +72,44 @@ class SupervisingActor(val connection: Connection, val subscriptionHandler: Subs val before = subscriptions.size subscribe(subscrition) val after = subscriptions.size - log.info(s"SubscribeMsg - nr of subscriptions before [$before] - after [$after]") + log.info( + s"SubscribeMsg - nr of subscriptions before [$before] - after [$after]") case unsubscription: UnsubscribeMsg => val before = subscriptions.size unsubscribe(unsubscription) val after = subscriptions.size - log.info(s"UnsubscribeMsg - nr of subscriptions before [$before] - after [$after]") + log.info( + s"UnsubscribeMsg - nr of subscriptions before [$before] - after [$after]") case Terminated(terminatedActor) => log.info(s"Actor [{}] terminated", terminatedActor.path.name) subscriptions.entryByActor(terminatedActor) match { - case Some(SubscriptionEntry(SubscribeMsg(eventTypeName, endpoint, Some(Cursor(partition, offset)), handler), actor: ActorRef)) => + case Some( + SubscriptionEntry(SubscribeMsg(eventTypeName, + endpoint, + Some(Cursor(partition, offset)), + handler), + actor: ActorRef)) => val cursor = subscriptions.cursorByActor(terminatedActor) - val unsubscription = UnsubscribeMsg(eventTypeName, Option(partition), handler.id()) + val unsubscription = + UnsubscribeMsg(eventTypeName, Option(partition), handler.id()) unsubscribe(unsubscription) - val newSubscription = SubscribeMsg(eventTypeName, endpoint, cursor, handler) + val newSubscription = + SubscribeMsg(eventTypeName, endpoint, cursor, handler) subscribe(newSubscription) - case Some(SubscriptionEntry(SubscribeMsg(eventTypeName, endpoint, None, handler), actor: ActorRef)) => + case Some( + SubscriptionEntry( + SubscribeMsg(eventTypeName, endpoint, None, handler), + actor: ActorRef)) => val cursor = subscriptions.cursorByActor(terminatedActor) - val unsubscription = UnsubscribeMsg(eventTypeName, None, handler.id()) + val unsubscription = + UnsubscribeMsg(eventTypeName, None, handler.id()) unsubscribe(unsubscription) - val newSubscription = SubscribeMsg(eventTypeName, endpoint, cursor, handler) + val newSubscription = + SubscribeMsg(eventTypeName, endpoint, cursor, handler) subscribe(newSubscription) case None => - log.warning("Did not find any SubscriptionKey for [{}]", terminatedActor.path.name) + log.warning("Did not find any SubscriptionKey for [{}]", + terminatedActor.path.name) case e => log.error("Received unexpected message! [{}]", e) } @@ -93,36 +117,53 @@ class SupervisingActor(val connection: Connection, val subscriptionHandler: Subs def subscribe(subscribe: SubscribeMsg) = { subscriptionCounter += 1 - val SubscribeMsg(eventTypeName, endpoint, optCursor, eventHandler) = subscribe - log.info("Subscription nr [{}] - cursor [{}] - eventType [{}] - listener [{}]", subscriptionCounter, optCursor, eventTypeName, eventHandler.id()) + val SubscribeMsg(eventTypeName, endpoint, optCursor, eventHandler) = + subscribe + log.info( + "Subscription nr [{}] - cursor [{}] - eventType [{}] - listener [{}]", + subscriptionCounter, + optCursor, + eventTypeName, + eventHandler.id()) val subscriptionKey: SubscriptionKey = optCursor match { - case Some(Cursor(partition, _)) => SubscriptionKey(eventTypeName, Some(partition)) - case None => SubscriptionKey(eventTypeName, None) + case Some(Cursor(partition, _)) => + SubscriptionKey(eventTypeName, Some(partition)) + case None => SubscriptionKey(eventTypeName, None) } //Create the Consumer - val consumingActor = context.actorOf(Props(classOf[ConsumingActor], subscriptionKey, eventHandler), "ConsumingActor-" + subscriptionCounter) + val consumingActor = context.actorOf( + Props(classOf[ConsumingActor], subscriptionKey, eventHandler), + "ConsumingActor-" + subscriptionCounter) context.watch(consumingActor) - val subEntry: SubscriptionEntry = SubscriptionEntry(subscribe, consumingActor) + val subEntry: SubscriptionEntry = + SubscriptionEntry(subscribe, consumingActor) // Notify listener it is subscribed eventHandler.handleOnSubscribed(endpoint, optCursor) //Create the pipeline - subscriptionHandler.createPipeline(optCursor, consumingActor, endpoint, eventHandler) + subscriptionHandler + .createPipeline(optCursor, consumingActor, endpoint, eventHandler) subscriptions.add(subscriptionKey, consumingActor, subEntry) subscriptions.addCursor(subscriptionKey, optCursor) } def unsubscribe(unsubscription: UnsubscribeMsg): Unit = { - val UnsubscribeMsg(eventTypeName, partition, eventHandlerId) = unsubscription + val UnsubscribeMsg(eventTypeName, partition, eventHandlerId) = + unsubscription val key: SubscriptionKey = SubscriptionKey(eventTypeName, partition) subscriptions.entry(key) match { - case Some(SubscriptionEntry(SubscribeMsg(eventTypeName, endpoint, cursor, handler), actor: ActorRef)) => - log.info("Unsubscribing Listener : [{}] from actor: [{}]", handler.id(), actor.path.name) + case Some( + SubscriptionEntry( + SubscribeMsg(eventTypeName, endpoint, cursor, handler), + actor: ActorRef)) => + log.info("Unsubscribing Listener : [{}] from actor: [{}]", + handler.id(), + actor.path.name) subscriptions.remove(key) actor ! PoisonPill case None => @@ -130,4 +171,4 @@ class SupervisingActor(val connection: Connection, val subscriptionHandler: Subs } } -} \ No newline at end of file +} diff --git a/client/src/main/scala/org/zalando/nakadi/client/handler/SubscriptionHandler.scala b/client/src/main/scala/org/zalando/nakadi/client/handler/SubscriptionHandler.scala index 8d1dcfc..de27110 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/handler/SubscriptionHandler.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/handler/SubscriptionHandler.scala @@ -33,32 +33,52 @@ import akka.stream.scaladsl.Source import akka.util.ByteString trait SubscriptionHandler { + /** - * Handles the subscription for an eventHandler. - */ - def subscribe(eventTypeName: String, endpoint: String, cursor: Option[Cursor], eventHandler: EventHandler): Option[ClientError] - def unsubscribe(eventTypeName: String, partition: Option[String], listenerId: String): Option[ClientError] - def createPipeline(cursor: Option[Cursor], consumingActor: ActorRef, url: String, eventHandler: EventHandler) + * Handles the subscription for an eventHandler. + */ + def subscribe(eventTypeName: String, + endpoint: String, + cursor: Option[Cursor], + eventHandler: EventHandler): Option[ClientError] + def unsubscribe(eventTypeName: String, + partition: Option[String], + listenerId: String): Option[ClientError] + def createPipeline(cursor: Option[Cursor], + consumingActor: ActorRef, + url: String, + eventHandler: EventHandler) } -class SubscriptionHandlerImpl(val connection: Connection) extends SubscriptionHandler { +class SubscriptionHandlerImpl(val connection: Connection) + extends SubscriptionHandler { import HttpFactory._ import SupervisingActor._ //Local variables private implicit val materializer = connection.materializer(decider()) - private val supervisingActor = connection.actorSystem.actorOf(Props(classOf[SupervisingActor], connection, this), "SupervisingActor" + System.currentTimeMillis()) + private val supervisingActor = connection.actorSystem.actorOf( + Props(classOf[SupervisingActor], connection, this), + "SupervisingActor" + System.currentTimeMillis()) private val RECEIVE_BUFFER_SIZE = 40960 private val EVENT_DELIMITER = "\n" val logger = LoggerFactory.getLogger(this.getClass) - def subscribe(eventTypeName: String, endpoint: String, cursor: Option[Cursor], eventHandler: EventHandler) = { - supervisingActor ! SubscribeMsg(eventTypeName, endpoint, cursor, eventHandler) + def subscribe(eventTypeName: String, + endpoint: String, + cursor: Option[Cursor], + eventHandler: EventHandler) = { + supervisingActor ! SubscribeMsg(eventTypeName, + endpoint, + cursor, + eventHandler) None } - def unsubscribe(eventTypeName: String, partition: Option[String], listenerId: String) = { + def unsubscribe(eventTypeName: String, + partition: Option[String], + listenerId: String) = { supervisingActor ! UnsubscribeMsg(eventTypeName, partition, listenerId) None } @@ -67,12 +87,16 @@ class SubscriptionHandlerImpl(val connection: Connection) extends SubscriptionHa case _ => Supervision.Stop } - def createPipeline(cursor: Option[Cursor], consumingActor: ActorRef, url: String, eventHandler: EventHandler) = { + def createPipeline(cursor: Option[Cursor], + consumingActor: ActorRef, + url: String, + eventHandler: EventHandler) = { val subscriber = ActorSubscriber[ByteString](consumingActor) //Setup a flow for the request - val requestFlow = Flow[Option[Cursor]].via(requestCreator(url)) + val requestFlow = Flow[Option[Cursor]] + .via(requestCreator(url)) .via(connection.requestFlow()) .buffer(RECEIVE_BUFFER_SIZE, OverflowStrategy.backpressure) .via(requestRenderer(eventHandler)) @@ -80,7 +104,8 @@ class SubscriptionHandlerImpl(val connection: Connection) extends SubscriptionHa //create the pipeline val result = Source(List(cursor)) - .via(requestFlow).withAttributes(ActorAttributes.supervisionStrategy(decider())) + .via(requestFlow) + .withAttributes(ActorAttributes.supervisionStrategy(decider())) .runForeach(_.runWith(Sink.fromSubscriber(subscriber))) result.onComplete { @@ -89,7 +114,10 @@ class SubscriptionHandlerImpl(val connection: Connection) extends SubscriptionHa val msg = "An exception occurred: " + e.getMessage eventHandler.handleOnError(url, Some(msg), e) logger.error(msg + e) - connection.actorSystem().scheduler.scheduleOnce(5.seconds)(stopActor(consumingActor)) //TODO: Make it configurable + connection + .actorSystem() + .scheduler + .scheduleOnce(5.seconds)(stopActor(consumingActor)) //TODO: Make it configurable } } @@ -98,22 +126,30 @@ class SubscriptionHandlerImpl(val connection: Connection) extends SubscriptionHa actor ! PoisonPill } - private def requestCreator(url: String): Flow[Option[Cursor], HttpRequest, NotUsed] = - Flow[Option[Cursor]].map(withHttpRequest(url, _, None, connection.tokenProvider)) + private def requestCreator( + url: String): Flow[Option[Cursor], HttpRequest, NotUsed] = + Flow[Option[Cursor]] + .map(withHttpRequest(url, _, None, connection.tokenProvider)) - private def requestRenderer(handler:EventHandler): Flow[HttpResponse, Source[ByteString, Any], NotUsed] = + private def requestRenderer(handler: EventHandler) + : Flow[HttpResponse, Source[ByteString, Any], NotUsed] = Flow[HttpResponse].filter { x => if (x.status.isSuccess()) { - logger.info("Connection established with success!") + logger.info("Connection established with success!") x.status.isSuccess() } else { x.entity.toStrict(10.second).map { body => - logger.error(s"http-status: ${ x.status.intValue().toString()}, reason[${ x.status.reason()}], body:[${body.data.decodeString("UTF-8")}]") + logger.error( + s"http-status: ${x.status.intValue().toString()}, reason[${x.status + .reason()}], body:[${body.data.decodeString("UTF-8")}]") } false } }.map(_.entity.withSizeLimit(Long.MaxValue).dataBytes.via(delimiterFlow)) - private def delimiterFlow = Flow[ByteString] - .via(Framing.delimiter(ByteString(EVENT_DELIMITER), maximumFrameLength = RECEIVE_BUFFER_SIZE, allowTruncation = true)) -} \ No newline at end of file + private def delimiterFlow = + Flow[ByteString].via( + Framing.delimiter(ByteString(EVENT_DELIMITER), + maximumFrameLength = RECEIVE_BUFFER_SIZE, + allowTruncation = true)) +} diff --git a/client/src/main/scala/org/zalando/nakadi/client/java/JavaClientHandler.scala b/client/src/main/scala/org/zalando/nakadi/client/java/JavaClientHandler.scala index 3e54a2b..a1d334e 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/java/JavaClientHandler.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/java/JavaClientHandler.scala @@ -12,21 +12,20 @@ import scala.util.Try import org.slf4j.LoggerFactory import org.zalando.nakadi.client.Deserializer import org.zalando.nakadi.client.Serializer -import org.zalando.nakadi.client.java.model.{ Event => JEvent } -import org.zalando.nakadi.client.scala.model.{ Cursor => ScalaCursor } -import org.zalando.nakadi.client.scala.{ ClientImpl => SClientImpl } -import org.zalando.nakadi.client.java.model.{ EventStreamBatch => JEventStreamBatch } -import org.zalando.nakadi.client.java.{ StreamParameters => JStreamParameters } -import org.zalando.nakadi.client.java.{ Listener => JListener } +import org.zalando.nakadi.client.java.model.{Event => JEvent} +import org.zalando.nakadi.client.scala.model.{Cursor => ScalaCursor} +import org.zalando.nakadi.client.scala.{ClientImpl => SClientImpl} +import org.zalando.nakadi.client.java.model.{EventStreamBatch => JEventStreamBatch} +import org.zalando.nakadi.client.java.{StreamParameters => JStreamParameters} +import org.zalando.nakadi.client.java.{Listener => JListener} import org.zalando.nakadi.client.scala.Connection import org.zalando.nakadi.client.scala.EmptyScalaEvent import org.zalando.nakadi.client.scala.EventHandler import org.zalando.nakadi.client.scala.HttpFactory -import org.zalando.nakadi.client.scala.{ StreamParameters => ScalaStreamParameters } +import org.zalando.nakadi.client.scala.{StreamParameters => ScalaStreamParameters} import org.zalando.nakadi.client.utils.FutureConversions import org.zalando.nakadi.client.utils.ModelConverter - import akka.http.scaladsl.model.HttpHeader import akka.http.scaladsl.model.HttpMethods import akka.http.scaladsl.model.HttpResponse @@ -41,84 +40,134 @@ import scala.concurrent.Await import scala.concurrent.duration.Duration /** - * Handler for mapping(Java<->Scala) and handling http calls and listener subscriptions for the Java API. - */ + * Handler for mapping(Java<->Scala) and handling http calls and listener subscriptions for the Java API. + */ trait JavaClientHandler { - def deserialize[T](response: HttpResponse, des: Deserializer[T]): Future[Optional[T]] - def get[T](endpoint: String, des: Deserializer[T]): java.util.concurrent.Future[Optional[T]] - def get[T](endpoint: String, headers: Seq[HttpHeader], des: Deserializer[T]): java.util.concurrent.Future[Optional[T]] - def post[T](endpoint: String, model: T)(implicit serializer: Serializer[T]): java.util.concurrent.Future[Void] - def subscribe[T <: JEvent](eventTypeName: String, endpoint: String, parameters: JStreamParameters, listener: JListener[T])(implicit des: Deserializer[JEventStreamBatch[T]]): Optional[ClientError] - def unsubscribe[T <: JEvent](eventTypeName: String, partition: Optional[String], listener: JListener[T]) + def deserialize[T](response: HttpResponse, + des: Deserializer[T]): Future[Optional[T]] + def get[T](endpoint: String, + des: Deserializer[T]): java.util.concurrent.Future[Optional[T]] + def get[T](endpoint: String, + headers: Seq[HttpHeader], + des: Deserializer[T]): java.util.concurrent.Future[Optional[T]] + def post[T](endpoint: String, model: T)( + implicit serializer: Serializer[T]): java.util.concurrent.Future[Void] + def subscribe[T <: JEvent](eventTypeName: String, + endpoint: String, + parameters: JStreamParameters, + listener: JListener[T])( + implicit des: Deserializer[JEventStreamBatch[T]]): Optional[ClientError] + def unsubscribe[T <: JEvent](eventTypeName: String, + partition: Optional[String], + listener: JListener[T]) def stop(): Unit } -class JavaClientHandlerImpl(val connection: Connection, subscriber: SubscriptionHandler) extends JavaClientHandler { +class JavaClientHandlerImpl(val connection: Connection, + subscriber: SubscriptionHandler) + extends JavaClientHandler { val logger = LoggerFactory.getLogger(this.getClass) import HttpFactory._ import GeneralConversions._ private implicit val mat = connection.materializer() - def deserialize[T](response: HttpResponse, des: Deserializer[T]): Future[Optional[T]] = response match { - case HttpResponse(status, headers, entity, protocol) if (status.isSuccess()) => - Try(Unmarshal(entity).to[String].map(body => des.from(body))) match { - case Success(result) => result.map(Optional.of(_)) - case Failure(error) => throw new RuntimeException(error.getMessage) - } - case HttpResponse(StatusCodes.NotFound, headers, entity, protocol) => - Future.successful(Optional.empty()) - case HttpResponse(status, headers, entity, protocol) if (status.isFailure()) => - throw new RuntimeException(status.reason()) - } + def deserialize[T](response: HttpResponse, + des: Deserializer[T]): Future[Optional[T]] = + response match { + case HttpResponse(status, headers, entity, protocol) + if (status.isSuccess()) => + Try(Unmarshal(entity).to[String].map(body => des.from(body))) match { + case Success(result) => result.map(Optional.of(_)) + case Failure(error) => throw new RuntimeException(error.getMessage) + } + case HttpResponse(StatusCodes.NotFound, headers, entity, protocol) => + Future.successful(Optional.empty()) + case HttpResponse(status, headers, entity, protocol) + if (status.isFailure()) => + throw new RuntimeException(status.reason()) + } - def get[T](endpoint: String, des: Deserializer[T]): java.util.concurrent.Future[Optional[T]] = { - FutureConversions.fromFuture2Future(connection.get(endpoint).flatMap(deserialize(_, des))) + def get[T]( + endpoint: String, + des: Deserializer[T]): java.util.concurrent.Future[Optional[T]] = { + FutureConversions.fromFuture2Future( + connection.get(endpoint).flatMap(deserialize(_, des))) } - def get[T](endpoint: String, headers: Seq[HttpHeader], des: Deserializer[T]): java.util.concurrent.Future[Optional[T]] = { - FutureConversions.fromFuture2Future(connection.executeCall(withHttpRequest(endpoint, HttpMethods.GET, headers, connection.tokenProvider, None)).flatMap(deserialize(_, des))) + def get[T]( + endpoint: String, + headers: Seq[HttpHeader], + des: Deserializer[T]): java.util.concurrent.Future[Optional[T]] = { + FutureConversions.fromFuture2Future( + connection + .executeCall( + withHttpRequest(endpoint, + HttpMethods.GET, + headers, + connection.tokenProvider, + None)) + .flatMap(deserialize(_, des))) } - def post[T](endpoint: String, model: T)(implicit serializer: Serializer[T]): java.util.concurrent.Future[Void] = { + def post[T](endpoint: String, model: T)(implicit serializer: Serializer[T]) + : java.util.concurrent.Future[Void] = { val entity = serializer.to(model) logger.info("Posting to endpoint {}", endpoint) logger.debug("Data to post {}", entity) - val result = connection.executeCall( - withHttpRequestAndPayload(endpoint, serialize(model), HttpMethods.POST, connection.tokenProvider)) + val result = connection + .executeCall( + withHttpRequestAndPayload(endpoint, + serialize(model), + HttpMethods.POST, + connection.tokenProvider)) .flatMap(response(_)) FutureConversions.fromOption2Void(result) } - private def serialize[T](model: T)(implicit serializer: Serializer[T]): String = + private def serialize[T](model: T)( + implicit serializer: Serializer[T]): String = Try(serializer.to(model)) match { case Success(result) => result - case Failure(error) => throw new RuntimeException("Failed to serialize: " + error.getMessage) + case Failure(error) => + throw new RuntimeException("Failed to serialize: " + error.getMessage) } - private def response[T](response: HttpResponse): Future[Option[String]] = response match { - case HttpResponse(status, headers, entity, protocol) if (status.isSuccess()) => - Try(Unmarshal(entity).to[String]) match { - case Success(result) => result.map(Option(_)) - case Failure(error) => throw new RuntimeException(error.getMessage) - } - - case HttpResponse(status, headers, entity, protocol) if (status.isFailure()) => - Unmarshal(entity).to[String].map { x => - val msg = "http-stats(%s) - %s - problem: %s ".format(status.intValue(), x, status.defaultMessage()) - logger.warn(msg) - throw new RuntimeException(msg) - } - } + private def response[T](response: HttpResponse): Future[Option[String]] = + response match { + case HttpResponse(status, headers, entity, protocol) + if (status.isSuccess()) => + Try(Unmarshal(entity).to[String]) match { + case Success(result) => result.map(Option(_)) + case Failure(error) => throw new RuntimeException(error.getMessage) + } + + case HttpResponse(status, headers, entity, protocol) + if (status.isFailure()) => + Unmarshal(entity).to[String].map { x => + val msg = "http-stats(%s) - %s - problem: %s " + .format(status.intValue(), x, status.defaultMessage()) + logger.warn(msg) + throw new RuntimeException(msg) + } + } - def subscribe[T <: JEvent](eventTypeName: String, endpoint: String, parameters: JStreamParameters, listener: JListener[T])(implicit des: Deserializer[JEventStreamBatch[T]]) = { + def subscribe[T <: JEvent](eventTypeName: String, + endpoint: String, + parameters: JStreamParameters, + listener: JListener[T])( + implicit des: Deserializer[JEventStreamBatch[T]]) = { import ModelConverter._ - val params: Option[ScalaStreamParameters] = toScalaStreamParameters(parameters) + val params: Option[ScalaStreamParameters] = toScalaStreamParameters( + parameters) val eventHandler: EventHandler = new JavaEventHandlerImpl(des, listener) val finalUrl = withUrl(endpoint, params) - val res = subscriber.subscribe(eventTypeName, finalUrl, getCursor(params), eventHandler) + val res = subscriber + .subscribe(eventTypeName, finalUrl, getCursor(params), eventHandler) toJavaClientError(res) } - def unsubscribe[T <: JEvent](eventTypeName: String, partition: Optional[String], listener: JListener[T]) = { + def unsubscribe[T <: JEvent](eventTypeName: String, + partition: Optional[String], + listener: JListener[T]) = { subscriber.unsubscribe(eventTypeName, toOption(partition), listener.getId) } @@ -127,10 +176,19 @@ class JavaClientHandlerImpl(val connection: Connection, subscriber: Subscription Await.ready(connection.actorSystem().terminate(), Duration.Inf) } - private def getCursor(params: Option[ScalaStreamParameters]): Option[ScalaCursor] = params match { - case Some(ScalaStreamParameters(cursor, batchLimit, streamLimit, batchFlushTimeout, streamTimeout, streamKeepAliveLimit, flowId)) => cursor - case None => None - } + private def getCursor( + params: Option[ScalaStreamParameters]): Option[ScalaCursor] = + params match { + case Some( + ScalaStreamParameters(cursor, + batchLimit, + streamLimit, + batchFlushTimeout, + streamTimeout, + streamKeepAliveLimit, + flowId)) => + cursor + case None => None + } } - diff --git a/client/src/main/scala/org/zalando/nakadi/client/java/model/JavaJacksonJsonMarshaller.scala b/client/src/main/scala/org/zalando/nakadi/client/java/model/JavaJacksonJsonMarshaller.scala index 7e15fae..d39faea 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/java/model/JavaJacksonJsonMarshaller.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/java/model/JavaJacksonJsonMarshaller.scala @@ -22,45 +22,64 @@ import com.fasterxml.jackson.databind.annotation.JsonSerialize.Inclusion object JavaJacksonJsonMarshaller { val logger = LoggerFactory.getLogger(this.getClass) // All TypeReferences - def problemTR: TypeReference[Problem] = new TypeReference[Problem] {} - def metricsTR: TypeReference[Metrics] = new TypeReference[Metrics] {} - def partitionTR: TypeReference[Partition] = new TypeReference[Partition] {} - def cursorTR: TypeReference[Cursor] = new TypeReference[Cursor] {} - def eventTypeSchemaTR: TypeReference[EventTypeSchema] = new TypeReference[EventTypeSchema] {} - def partitionResolutionStrategyTR: TypeReference[PartitionStrategy] = new TypeReference[PartitionStrategy] {} - def eventEnrichmentStrategyTR: TypeReference[EventEnrichmentStrategy] = new TypeReference[EventEnrichmentStrategy] {} - def dataChangeEventQualifierTR: TypeReference[DataChangeEventQualifier] = new TypeReference[DataChangeEventQualifier] {} - def eventTypeStatisticsTR: TypeReference[EventTypeStatistics] = new TypeReference[EventTypeStatistics] {} - def eventTypeTR: TypeReference[EventType] = new TypeReference[EventType] {} - def eventTR: TypeReference[Event] = new TypeReference[Event] {} - def eventStreamBatchTR: TypeReference[EventStreamBatch[_]] = new TypeReference[EventStreamBatch[_]] {} + def problemTR: TypeReference[Problem] = new TypeReference[Problem] {} + def metricsTR: TypeReference[Metrics] = new TypeReference[Metrics] {} + def partitionTR: TypeReference[Partition] = new TypeReference[Partition] {} + def cursorTR: TypeReference[Cursor] = new TypeReference[Cursor] {} + def eventTypeSchemaTR: TypeReference[EventTypeSchema] = + new TypeReference[EventTypeSchema] {} + def partitionResolutionStrategyTR: TypeReference[PartitionStrategy] = + new TypeReference[PartitionStrategy] {} + def eventEnrichmentStrategyTR: TypeReference[EventEnrichmentStrategy] = + new TypeReference[EventEnrichmentStrategy] {} + def dataChangeEventQualifierTR: TypeReference[DataChangeEventQualifier] = + new TypeReference[DataChangeEventQualifier] {} + def eventTypeStatisticsTR: TypeReference[EventTypeStatistics] = + new TypeReference[EventTypeStatistics] {} + def eventTypeTR: TypeReference[EventType] = new TypeReference[EventType] {} + def eventTR: TypeReference[Event] = new TypeReference[Event] {} + def eventStreamBatchTR: TypeReference[EventStreamBatch[_]] = + new TypeReference[EventStreamBatch[_]] {} - def eventMetadataTR: TypeReference[EventMetadata] = new TypeReference[EventMetadata] {} - def businessEventTR: TypeReference[BusinessEvent] = new TypeReference[BusinessEvent] {} - def batchItemResponseTR: TypeReference[BatchItemResponse] = new TypeReference[BatchItemResponse] {} - def dataChangeEventTR: TypeReference[DataChangeEvent[Any]] = new TypeReference[DataChangeEvent[Any]] {} + def eventMetadataTR: TypeReference[EventMetadata] = + new TypeReference[EventMetadata] {} + def businessEventTR: TypeReference[BusinessEvent] = + new TypeReference[BusinessEvent] {} + def batchItemResponseTR: TypeReference[BatchItemResponse] = + new TypeReference[BatchItemResponse] {} + def dataChangeEventTR: TypeReference[DataChangeEvent[Any]] = + new TypeReference[DataChangeEvent[Any]] {} //Lists - def listOfPartitionStrategyTR: TypeReference[java.util.List[PartitionStrategy]] = new TypeReference[java.util.List[PartitionStrategy]] {} - def listOfEventEnrichmentStrategyTR: TypeReference[java.util.List[EventEnrichmentStrategy]] = new TypeReference[java.util.List[EventEnrichmentStrategy]] {} - def listOfEventTypeTR: TypeReference[java.util.List[EventType]] = new TypeReference[java.util.List[EventType]] {} - def listOfPartitionTR: TypeReference[java.util.List[Partition]] = new TypeReference[java.util.List[Partition]] {} + def listOfPartitionStrategyTR: TypeReference[ + java.util.List[PartitionStrategy]] = + new TypeReference[java.util.List[PartitionStrategy]] {} + def listOfEventEnrichmentStrategyTR: TypeReference[ + java.util.List[EventEnrichmentStrategy]] = + new TypeReference[java.util.List[EventEnrichmentStrategy]] {} + def listOfEventTypeTR: TypeReference[java.util.List[EventType]] = + new TypeReference[java.util.List[EventType]] {} + def listOfPartitionTR: TypeReference[java.util.List[Partition]] = + new TypeReference[java.util.List[Partition]] {} - def optionalDeserializer[T]( expectedType: TypeReference[T]): Deserializer[Option[T]] = new Deserializer[Option[T]] { - def from(from: String): Option[T] = { - defaultObjectMapper.readValue[Option[T]](from, expectedType) + def optionalDeserializer[T]( + expectedType: TypeReference[T]): Deserializer[Option[T]] = + new Deserializer[Option[T]] { + def from(from: String): Option[T] = { + defaultObjectMapper.readValue[Option[T]](from, expectedType) + } } - } - def serializer[T]: Serializer[T] = new Serializer[T] { + def serializer[T]: Serializer[T] = new Serializer[T] { def to(from: T): String = defaultObjectMapper.writeValueAsString(from) } - def deserializer[T]( expectedType: TypeReference[T]): Deserializer[T] = new Deserializer[T] { - def from(from: String): T = { - defaultObjectMapper.readValue[T](from, expectedType) + def deserializer[T](expectedType: TypeReference[T]): Deserializer[T] = + new Deserializer[T] { + def from(from: String): T = { + defaultObjectMapper.readValue[T](from, expectedType) + } } - } lazy val defaultObjectMapper: ObjectMapper = new ObjectMapper() // .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) @@ -69,11 +88,13 @@ object JavaJacksonJsonMarshaller { .setSerializationInclusion(JsonInclude.Include.NON_EMPTY) .addHandler(new DeserializationProblemHandler() { override def handleUnknownProperty(ctxt: DeserializationContext, - jp: JsonParser, deserializer: JsonDeserializer[_], + jp: JsonParser, + deserializer: JsonDeserializer[_], beanOrClass: AnyRef, propertyName: String): Boolean = { - logger.warn(s"unknown property occurred in JSON representation: [beanOrClass=$beanOrClass, property=$propertyName]") + logger.warn( + s"unknown property occurred in JSON representation: [beanOrClass=$beanOrClass, property=$propertyName]") true } }) -} \ No newline at end of file +} diff --git a/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala index e2455c0..e94844f 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala @@ -28,7 +28,10 @@ import akka.http.scaladsl.model.HttpResponse import akka.http.scaladsl.model.StatusCodes import akka.http.scaladsl.unmarshalling.Unmarshal -class ClientImpl(connection: Connection, subscriber: SubscriptionHandler, charSet: String = "UTF-8") extends Client { +class ClientImpl(connection: Connection, + subscriber: SubscriptionHandler, + charSet: String = "UTF-8") + extends Client { import Uri._ import JacksonJsonMarshaller._ import HttpFactory._ @@ -37,59 +40,106 @@ class ClientImpl(connection: Connection, subscriber: SubscriptionHandler, charSe private val logger = LoggerFactory.getLogger(this.getClass) def getMetrics(): Future[Either[ClientError, Option[Metrics]]] = { - logFutureEither(connection.get(URI_METRICS).flatMap(mapToEither(_)(deserializer(metricsTR)))) + logFutureEither( + connection + .get(URI_METRICS) + .flatMap(mapToEither(_)(deserializer(metricsTR)))) } def getEventTypes(): Future[Either[ClientError, Option[Seq[EventType]]]] = { - logFutureEither(connection.get(URI_EVENT_TYPES).flatMap(mapToEither(_)(deserializer(listOfEventTypeTR)))) + logFutureEither( + connection + .get(URI_EVENT_TYPES) + .flatMap(mapToEither(_)(deserializer(listOfEventTypeTR)))) } def createEventType(eventType: EventType): Future[Option[ClientError]] = { - logFutureOption(connection.post(URI_EVENT_TYPES, eventType).flatMap(mapToOption(_))) + logFutureOption( + connection.post(URI_EVENT_TYPES, eventType).flatMap(mapToOption(_))) } - def getEventType(name: String): Future[Either[ClientError, Option[EventType]]] = { - logFutureEither(connection.get(URI_EVENT_TYPE_BY_NAME.format(name)).flatMap(in => mapToEither(in)(deserializer(eventTypeTR)))) + def getEventType( + name: String): Future[Either[ClientError, Option[EventType]]] = { + logFutureEither( + connection + .get(URI_EVENT_TYPE_BY_NAME.format(name)) + .flatMap(in => mapToEither(in)(deserializer(eventTypeTR)))) } - def updateEventType(name: String, eventType: EventType): Future[Option[ClientError]] = { + def updateEventType(name: String, + eventType: EventType): Future[Option[ClientError]] = { val result = connection.put(URI_EVENT_TYPE_BY_NAME.format(name), eventType) logFutureOption(result.flatMap(in => mapToOption(in))) } def deleteEventType(name: String): Future[Option[ClientError]] = { - logFutureOption(connection.delete(URI_EVENT_TYPE_BY_NAME.format(name)).flatMap(in => mapToOption(in))) + logFutureOption( + connection + .delete(URI_EVENT_TYPE_BY_NAME.format(name)) + .flatMap(in => mapToOption(in))) } - def publishEvents[T <: Event](eventTypeName: String, events: Seq[T], ser: Serializer[Seq[T]]): Future[Option[ClientError]] = { - logFutureOption(connection.post(URI_EVENTS_OF_EVENT_TYPE.format(eventTypeName), events).flatMap(in => mapToOption(in))) + def publishEvents[T <: Event]( + eventTypeName: String, + events: Seq[T], + ser: Serializer[Seq[T]]): Future[Option[ClientError]] = { + logFutureOption( + connection + .post(URI_EVENTS_OF_EVENT_TYPE.format(eventTypeName), events) + .flatMap(in => mapToOption(in))) } - def publishEvents[T <: Event](eventTypeName: String, events: Seq[T]): Future[Option[ClientError]] = { - logFutureOption(connection.post(URI_EVENTS_OF_EVENT_TYPE.format(eventTypeName), events).flatMap(in => mapToOption(in))) + def publishEvents[T <: Event]( + eventTypeName: String, + events: Seq[T]): Future[Option[ClientError]] = { + logFutureOption( + connection + .post(URI_EVENTS_OF_EVENT_TYPE.format(eventTypeName), events) + .flatMap(in => mapToOption(in))) } - def getPartitions(name: String): Future[Either[ClientError, Option[Seq[Partition]]]] = { - logFutureEither(connection.get(URI_PARTITIONS_BY_EVENT_TYPE.format(name)).flatMap(in => mapToEither(in)(deserializer(listOfPartitionTR)))) + def getPartitions( + name: String): Future[Either[ClientError, Option[Seq[Partition]]]] = { + logFutureEither( + connection + .get(URI_PARTITIONS_BY_EVENT_TYPE.format(name)) + .flatMap(in => mapToEither(in)(deserializer(listOfPartitionTR)))) } - def getEnrichmentStrategies(): Future[Either[ClientError, Option[Seq[EventEnrichmentStrategy.Value]]]] = { - logFutureEither(connection.get(URI_ENRICHMENT_STRATEGIES).flatMap(mapToEither(_)(deserializer(listOfEventEnrichmentStrategyTR)))) + def getEnrichmentStrategies(): Future[ + Either[ClientError, Option[Seq[EventEnrichmentStrategy.Value]]]] = { + logFutureEither( + connection + .get(URI_ENRICHMENT_STRATEGIES) + .flatMap( + mapToEither(_)(deserializer(listOfEventEnrichmentStrategyTR)))) } - def getPartitioningStrategies(): Future[Either[ClientError, Option[Seq[PartitionStrategy.Value]]]] = - logFutureEither(connection.get(URI_PARTITIONING_STRATEGIES).flatMap(mapToEither(_)(deserializer(listOfPartitionStrategyTR)))) + def getPartitioningStrategies() + : Future[Either[ClientError, Option[Seq[PartitionStrategy.Value]]]] = + logFutureEither( + connection + .get(URI_PARTITIONING_STRATEGIES) + .flatMap(mapToEither(_)(deserializer(listOfPartitionStrategyTR)))) def stop(): Option[ClientError] = { materializer.shutdown() - val result = Await.ready(connection.actorSystem().terminate(), Duration.Inf) + val result = + Await.ready(connection.actorSystem().terminate(), Duration.Inf) None } - def subscribe[T <: Event](eventTypeName: String, parameters: StreamParameters, listener: Listener[T], typeRef: TypeReference[EventStreamBatch[T]]): Option[ClientError] = { + def subscribe[T <: Event]( + eventTypeName: String, + parameters: StreamParameters, + listener: Listener[T], + typeRef: TypeReference[EventStreamBatch[T]]): Option[ClientError] = { subscribe(eventTypeName, parameters, listener)(deserializer(typeRef)) } - def subscribe[T <: Event](eventTypeName: String, params: StreamParameters, listener: Listener[T])(implicit des: Deserializer[EventStreamBatch[T]]): Option[ClientError] = + def subscribe[T <: Event](eventTypeName: String, + params: StreamParameters, + listener: Listener[T])( + implicit des: Deserializer[EventStreamBatch[T]]): Option[ClientError] = (eventTypeName, params, listener) match { case (_, _, listener) if listener == null => @@ -100,15 +150,26 @@ class ClientImpl(connection: Connection, subscriber: SubscriptionHandler, charSe logger.info("eventType is null") Some(ClientError("Eventype may not be empty(null)!", None)) - case (eventType, StreamParameters(cursor, _, _, _, _, _, _), listener) if Option(eventType).isDefined => + case (eventType, StreamParameters(cursor, _, _, _, _, _, _), listener) + if Option(eventType).isDefined => val url = URI_EVENTS_OF_EVENT_TYPE.format(eventType) - logger.debug("Subscribing listener {} - cursor {} - parameters {} - eventType {} - url {}", listener.id, cursor, params, eventType, url) + logger.debug( + "Subscribing listener {} - cursor {} - parameters {} - eventType {} - url {}", + listener.id, + cursor, + params, + eventType, + url) val finalUrl = withUrl(url, Some(params)) - val eventHandler: EventHandler = new ScalaEventHandlerImpl(des, listener) + val eventHandler: EventHandler = + new ScalaEventHandlerImpl(des, listener) subscriber.subscribe(eventTypeName, finalUrl, cursor, eventHandler) } - def unsubscribe[T <: Event](eventTypeName: String, partition: Option[String], listener: Listener[T]): Future[Option[ClientError]] = { + def unsubscribe[T <: Event]( + eventTypeName: String, + partition: Option[String], + listener: Listener[T]): Future[Option[ClientError]] = { subscriber.unsubscribe(eventTypeName, partition, listener.id) Future.successful(None) } @@ -117,7 +178,8 @@ class ClientImpl(connection: Connection, subscriber: SubscriptionHandler, charSe //# HELPER METHODS # //#################### - private def logFutureEither[A, T](future: Future[Either[ClientError, T]]): Future[Either[ClientError, T]] = { + private def logFutureEither[A, T](future: Future[Either[ClientError, T]]) + : Future[Either[ClientError, T]] = { future recover { case e: Throwable => val msg = s"A unexpected error occured: ${e.getMessage}" @@ -125,7 +187,8 @@ class ClientImpl(connection: Connection, subscriber: SubscriptionHandler, charSe Left(ClientError(msg, None)) } } - private def logFutureOption(future: Future[Option[ClientError]]): Future[Option[ClientError]] = { + private def logFutureOption( + future: Future[Option[ClientError]]): Future[Option[ClientError]] = { future recover { case e: Throwable => val msg = s"A unexpected error occured: ${e.getMessage}" @@ -133,57 +196,74 @@ class ClientImpl(connection: Connection, subscriber: SubscriptionHandler, charSe } } - private def mapToEither[T](response: HttpResponse)(implicit deserializer: Deserializer[T]): Future[Either[ClientError, Option[T]]] = { + private def mapToEither[T](response: HttpResponse)( + implicit deserializer: Deserializer[T]) + : Future[Either[ClientError, Option[T]]] = { logger.debug("received [response={}]", response) response match { - case HttpResponse(status, headers, entity, protocol) if (status.isSuccess()) => + case HttpResponse(status, headers, entity, protocol) + if (status.isSuccess()) => try { - Unmarshal(entity).to[String].map { - body => - logger.debug(s"Payload: $body") - Right(Some(deserializer.from(body))) + Unmarshal(entity).to[String].map { body => + logger.debug(s"Payload: $body") + Right(Some(deserializer.from(body))) } } catch { case e: Throwable => - val msg = "Failed to deserialise the content with error: %s".format(e.getMessage) + val msg = + "Failed to deserialise the content with error: %s".format( + e.getMessage) logger.error(msg) Future.successful(Left(ClientError(msg, Some(status.intValue())))) } case HttpResponse(StatusCodes.NotFound, headers, entity, protocol) => - logger.info(s"Received: httpStatus - Not found ${StatusCodes.NotFound}") + logger.info( + s"Received: httpStatus - Not found ${StatusCodes.NotFound}") Future.successful(Right(None)) - case HttpResponse(status, headers, entity, protocol) if (status.isRedirection()) => - val msg = "Not implemented: http-status (" + status.intValue() + "}) and reason:" + status.reason() + case HttpResponse(status, headers, entity, protocol) + if (status.isRedirection()) => + val msg = "Not implemented: http-status (" + status + .intValue() + "}) and reason:" + status.reason() logger.info(msg) Future.successful(Left(ClientError(msg, Some(status.intValue())))) case HttpResponse(status, headers, entity, protocol) => Unmarshal(entity).to[String].map { body => - val msg = "Service return http-status: %s (%s) Message: %s".format(status.intValue(), status.reason(), body) + val msg = "Service return http-status: %s (%s) Message: %s" + .format(status.intValue(), status.reason(), body) logger.warn(msg) Left(ClientError(msg, Some(status.intValue()))) } } } - private[client] def mapToOption[T](response: HttpResponse): Future[Option[ClientError]] = { + private[client] def mapToOption[T]( + response: HttpResponse): Future[Option[ClientError]] = { response.status match { case status if (status.isSuccess()) => logger.info(s"Success. http-status: ${status.intValue()}") response.entity.toStrict(10.second).map { body => - logger.debug("Success - http-status: %s, body:[%s]".format(status.intValue().toString(), body.data.decodeString(charSet))) + logger.debug( + "Success - http-status: %s, body:[%s]".format( + status.intValue().toString(), + body.data.decodeString(charSet))) } Future.successful(None) case status if (status.isRedirection()) => - val msg = s"Redirection - http-status: ${status.intValue()}, reason[${status.reason()}]" + val msg = + s"Redirection - http-status: ${status.intValue()}, reason[${status.reason()}]" logger.info(msg) response.entity.toStrict(10.second).map { body => - logger.debug(s"Redirection - http-status: ${status.intValue().toString()}, reason[${status.reason()}], body:[${body.data.decodeString(charSet)}]") + logger.debug( + s"Redirection - http-status: ${status.intValue().toString()}, reason[${status + .reason()}], body:[${body.data.decodeString(charSet)}]") } Future.successful(Option(ClientError(msg, Some(status.intValue())))) case status if (status.isFailure()) => logger.warn(s"Failure. http-status: ${status.intValue()}") response.entity.toStrict(10.second).map { body => - val msg = s"Failure - http-status: ${status.intValue()}, reason[${status.reason()}], body:[${body.data.decodeString(charSet)}]" + val msg = + s"Failure - http-status: ${status.intValue()}, reason[${status + .reason()}], body:[${body.data.decodeString(charSet)}]" logger.warn(msg) Option(ClientError(msg, Some(status.intValue()))) } @@ -191,4 +271,3 @@ class ClientImpl(connection: Connection, subscriber: SubscriptionHandler, charSe } } - diff --git a/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala index a3dc888..f848ad8 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala @@ -11,7 +11,7 @@ import scala.util.Try import org.slf4j.LoggerFactory import org.zalando.nakadi.client.java.JavaClientHandler import org.zalando.nakadi.client.java.JavaClientHandlerImpl -import org.zalando.nakadi.client.java.model.{ Event => JEvent } +import org.zalando.nakadi.client.java.model.{Event => JEvent} import org.zalando.nakadi.client.scala.model.Event import org.zalando.nakadi.client.Serializer @@ -31,7 +31,7 @@ import akka.stream.scaladsl.Source import javax.net.ssl.SSLContext import javax.net.ssl.TrustManager import javax.net.ssl.X509TrustManager -import akka.http.scaladsl.model.{ HttpHeader, HttpMethod, HttpMethods, HttpResponse, MediaRange } +import akka.http.scaladsl.model.{HttpHeader, HttpMethod, HttpMethods, HttpResponse, MediaRange} import org.zalando.nakadi.client.handler.SubscriptionHandlerImpl import akka.stream.Supervision @@ -45,112 +45,165 @@ trait Connection { def executeCall(request: HttpRequest): Future[HttpResponse] def get(endpoint: String): Future[HttpResponse] def delete(endpoint: String): Future[HttpResponse] - def put[T](endpoint: String, model: T)(implicit serializer: Serializer[T]): Future[HttpResponse] - def post[T](endpoint: String, model: T)(implicit serializer: Serializer[T]): Future[HttpResponse] - def requestFlow(): Flow[HttpRequest, HttpResponse, Future[Http.OutgoingConnection]] + def put[T](endpoint: String, model: T)( + implicit serializer: Serializer[T]): Future[HttpResponse] + def post[T](endpoint: String, model: T)( + implicit serializer: Serializer[T]): Future[HttpResponse] + def requestFlow() + : Flow[HttpRequest, HttpResponse, Future[Http.OutgoingConnection]] def materializer(decider: Supervision.Decider): ActorMaterializer def materializer(): ActorMaterializer def actorSystem(): ActorSystem } /** - * Companion object with factory methods. - */ + * Companion object with factory methods. + */ object Connection { val RECEIVE_BUFFER_SIZE = 10240 val EVENT_DELIMITER = "\n" + /** - * Creates a new SSL context for usage with connections based on the HTTPS protocol. - */ - def newSslContext(secured: Boolean, verified: Boolean): Option[HttpsConnectionContext] = (secured, verified) match { - case (true, true) => Some(new HttpsConnectionContext(SSLContext.getDefault)) - case (true, false) => - val permissiveTrustManager: TrustManager = new X509TrustManager() { - override def checkClientTrusted(x$1: Array[java.security.cert.X509Certificate], x$2: String): Unit = {} - override def checkServerTrusted(x$1: Array[java.security.cert.X509Certificate], x$2: String): Unit = {} - override def getAcceptedIssuers(): Array[X509Certificate] = Array.empty - } - val sslContext = SSLContext.getInstance("TLS") - sslContext.init(Array.empty, Array(permissiveTrustManager), new SecureRandom()) - Some(new HttpsConnectionContext(sslContext)) - case _ => None - } + * Creates a new SSL context for usage with connections based on the HTTPS protocol. + */ + def newSslContext(secured: Boolean, + verified: Boolean): Option[HttpsConnectionContext] = + (secured, verified) match { + case (true, true) => + Some(new HttpsConnectionContext(SSLContext.getDefault)) + case (true, false) => + val permissiveTrustManager: TrustManager = new X509TrustManager() { + override def checkClientTrusted( + x$1: Array[java.security.cert.X509Certificate], + x$2: String): Unit = {} + override def checkServerTrusted( + x$1: Array[java.security.cert.X509Certificate], + x$2: String): Unit = {} + override def getAcceptedIssuers(): Array[X509Certificate] = + Array.empty + } + val sslContext = SSLContext.getInstance("TLS") + sslContext + .init(Array.empty, Array(permissiveTrustManager), new SecureRandom()) + Some(new HttpsConnectionContext(sslContext)) + case _ => None + } - def newClient(host: String, port: Int, tokenProvider: Option[() => String], securedConnection: Boolean, verifySSlCertificate: Boolean): Client = { - val connection = new ConnectionImpl(host, port, tokenProvider, securedConnection, verifySSlCertificate) + def newClient(host: String, + port: Int, + tokenProvider: Option[() => String], + securedConnection: Boolean, + verifySSlCertificate: Boolean): Client = { + val connection = new ConnectionImpl(host, + port, + tokenProvider, + securedConnection, + verifySSlCertificate) new ClientImpl(connection, new SubscriptionHandlerImpl(connection)) } - def newClientHandler4Java(host: String, port: Int, tokenProvider: Option[() => String], securedConnection: Boolean, verifySSlCertificate: Boolean): JavaClientHandler = { - val connection = new ConnectionImpl(host, port, tokenProvider, securedConnection, verifySSlCertificate) - new JavaClientHandlerImpl(connection, new SubscriptionHandlerImpl(connection)) + def newClientHandler4Java( + host: String, + port: Int, + tokenProvider: Option[() => String], + securedConnection: Boolean, + verifySSlCertificate: Boolean): JavaClientHandler = { + val connection = new ConnectionImpl(host, + port, + tokenProvider, + securedConnection, + verifySSlCertificate) + new JavaClientHandlerImpl(connection, + new SubscriptionHandlerImpl(connection)) } } /** - * Class for handling the basic http calls. - */ - -sealed class ConnectionImpl(val host: String, val port: Int, val tokenProvider: Option[() => String], securedConnection: Boolean, verifySSlCertificate: Boolean) extends Connection { + * Class for handling the basic http calls. + */ +sealed class ConnectionImpl(val host: String, + val port: Int, + val tokenProvider: Option[() => String], + securedConnection: Boolean, + verifySSlCertificate: Boolean) + extends Connection { import Connection._ import HttpFactory._ var registrationCounter: AtomicLong = new AtomicLong(0); - type HttpFlow[T] = Flow[(HttpRequest, T), (Try[HttpResponse], T), HostConnectionPool] + type HttpFlow[T] = + Flow[(HttpRequest, T), (Try[HttpResponse], T), HostConnectionPool] type StepResult[T] = (T, Option[String]) implicit val actorSystem = ActorSystem("Nakadi-Client-Connections") - - def materializer(decider: Supervision.Decider): ActorMaterializer = { - ActorMaterializer( ActorMaterializerSettings(actorSystem) - .withSupervisionStrategy(decider)) + + def materializer(decider: Supervision.Decider): ActorMaterializer = { + ActorMaterializer( + ActorMaterializerSettings(actorSystem).withSupervisionStrategy( + decider)) } - def materializer() = ActorMaterializer( - ActorMaterializerSettings(actorSystem)) + def materializer() = + ActorMaterializer(ActorMaterializerSettings(actorSystem)) private implicit val http = Http(actorSystem) private val actors: Map[String, Actor] = Map() val logger = LoggerFactory.getLogger(this.getClass) - def requestFlow(): Flow[HttpRequest, HttpResponse, Future[Http.OutgoingConnection]] = newSslContext(securedConnection, verifySSlCertificate) match { - case Some(result) => http.outgoingConnectionHttps(host, port, result) - case None => - logger.warn("Disabled HTTPS, switching to HTTP only!") - http.outgoingConnection(host, port) - } + def requestFlow() + : Flow[HttpRequest, HttpResponse, Future[Http.OutgoingConnection]] = + newSslContext(securedConnection, verifySSlCertificate) match { + case Some(result) => http.outgoingConnectionHttps(host, port, result) + case None => + logger.warn("Disabled HTTPS, switching to HTTP only!") + http.outgoingConnection(host, port) + } def get(endpoint: String): Future[HttpResponse] = { logger.info("Get - URL {}", endpoint) - executeCall(withHttpRequest(endpoint, HttpMethods.GET, Nil, tokenProvider, None)) + executeCall( + withHttpRequest(endpoint, HttpMethods.GET, Nil, tokenProvider, None)) } def delete(endpoint: String): Future[HttpResponse] = { logger.info("Delete: {}", endpoint) - executeCall(withHttpRequest(endpoint, HttpMethods.DELETE, Nil, tokenProvider, None)) + executeCall( + withHttpRequest(endpoint, + HttpMethods.DELETE, + Nil, + tokenProvider, + None)) } - def put[T](endpoint: String, model: T)(implicit serializer: Serializer[T]): Future[HttpResponse] = { - val entity = serializer.to(model) + def put[T](endpoint: String, model: T)( + implicit serializer: Serializer[T]): Future[HttpResponse] = { + val entity = serializer.to(model) logger.info(s"Put: $endpoint - Data: $entity") - executeCall(withHttpRequest(endpoint, HttpMethods.PUT, Nil, tokenProvider, None)) + executeCall( + withHttpRequest(endpoint, HttpMethods.PUT, Nil, tokenProvider, None)) } - def post[T](endpoint: String, model: T)(implicit serializer: Serializer[T]): Future[HttpResponse] = { + def post[T](endpoint: String, model: T)( + implicit serializer: Serializer[T]): Future[HttpResponse] = { val entity = serializer.to(model) - logger.info(s"Put: $endpoint - Data: $entity") - executeCall(withHttpRequestAndPayload(endpoint, entity, HttpMethods.POST, tokenProvider)) + logger.info(s"Put: $endpoint - Data: $entity") + executeCall( + withHttpRequestAndPayload(endpoint, + entity, + HttpMethods.POST, + tokenProvider)) } def executeCall(request: HttpRequest): Future[HttpResponse] = { - val response: Future[HttpResponse] = - Source.single(request) - .via(requestFlow). - runWith(Sink.head)(materializer()) + val response: Future[HttpResponse] = Source + .single(request) + .via(requestFlow) + .runWith(Sink.head)(materializer()) logError(response) response } private def logError(future: Future[Any]) { future recover { - case e: Throwable => logger.error("Failed to call endpoint with: ", e.getMessage) + case e: Throwable => + logger.error("Failed to call endpoint with: ", e.getMessage) } } diff --git a/client/src/main/scala/org/zalando/nakadi/client/scala/HttpFactory.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/HttpFactory.scala index b784a05..e7710e2 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/scala/HttpFactory.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/scala/HttpFactory.scala @@ -1,79 +1,116 @@ package org.zalando.nakadi.client.scala import scala.collection.immutable.Seq -import akka.http.scaladsl.model.{ ContentType, HttpHeader, HttpMethod, HttpRequest, MediaRange } +import akka.http.scaladsl.model.{ContentType, HttpHeader, HttpMethod, HttpRequest, MediaRange} import akka.http.scaladsl.model.MediaTypes.`application/json` import akka.http.scaladsl.model.Uri.apply import akka.http.scaladsl.model.headers -import akka.http.scaladsl.model.headers.{ OAuth2BearerToken, RawHeader } +import akka.http.scaladsl.model.headers.{OAuth2BearerToken, RawHeader} import akka.http.scaladsl.model.HttpMethods -import akka.http.scaladsl.model.headers.{ Accept, RawHeader } +import akka.http.scaladsl.model.headers.{Accept, RawHeader} import akka.http.scaladsl.model.MediaTypes.`application/json` import org.zalando.nakadi.client.scala.model.Cursor import org.slf4j.LoggerFactory // object HttpFactory { + /** - * Alias for a function that returns a string. - */ + * Alias for a function that returns a string. + */ type TokenProvider = () => String def withUrl(url: String, params: Option[StreamParameters]) = { val paramsList = withQueryParams(params) - val urlParams = if (!paramsList.isEmpty) paramsList.mkString("?", "&", "") else "" + val urlParams = + if (!paramsList.isEmpty) paramsList.mkString("?", "&", "") else "" url + urlParams } - def withHttpRequest(url: String, httpMethod: HttpMethod, additionalHeaders: Seq[HttpHeader], tokenProvider: Option[TokenProvider], params: Option[StreamParameters]): HttpRequest = { + def withHttpRequest(url: String, + httpMethod: HttpMethod, + additionalHeaders: Seq[HttpHeader], + tokenProvider: Option[TokenProvider], + params: Option[StreamParameters]): HttpRequest = { val allHeaders: Seq[HttpHeader] = tokenProvider match { - case None => additionalHeaders - case Some(token) => additionalHeaders :+ headers.Authorization(OAuth2BearerToken(token())) + case None => additionalHeaders + case Some(token) => + additionalHeaders :+ headers.Authorization(OAuth2BearerToken(token())) } val paramsList = withQueryParams(params) HttpRequest(uri = url, method = httpMethod).withHeaders(allHeaders) } - def withHttpRequestAndPayload(url: String, entity: String, httpMethod: HttpMethod, tokenProvider: Option[TokenProvider]): HttpRequest = { + def withHttpRequestAndPayload( + url: String, + entity: String, + httpMethod: HttpMethod, + tokenProvider: Option[TokenProvider]): HttpRequest = { val request = HttpRequest(uri = url, method = httpMethod) tokenProvider match { - case None => request.withHeaders(headers.Accept(MediaRange(`application/json`))) - .withEntity(ContentType(`application/json`), entity) - case Some(token) => request.withHeaders(headers.Authorization(OAuth2BearerToken(token())), - headers.Accept(MediaRange(`application/json`))) - .withEntity(ContentType(`application/json`), entity) + case None => + request + .withHeaders(headers.Accept(MediaRange(`application/json`))) + .withEntity(ContentType(`application/json`), entity) + case Some(token) => + request + .withHeaders(headers.Authorization(OAuth2BearerToken(token())), + headers.Accept(MediaRange(`application/json`))) + .withEntity(ContentType(`application/json`), entity) } } - def withHttpRequest(url: String, cursor: Option[Cursor], flowId: Option[String], tokenProvider: Option[TokenProvider]): HttpRequest = { - val customHeaders = withDefaultHeaders(cursor, flowId) :+ RawHeader("Accept", "application/x-json-stream") + def withHttpRequest(url: String, + cursor: Option[Cursor], + flowId: Option[String], + tokenProvider: Option[TokenProvider]): HttpRequest = { + val customHeaders = withDefaultHeaders(cursor, flowId) :+ RawHeader( + "Accept", + "application/x-json-stream") val allHeaders = tokenProvider match { - case None => customHeaders - case Some(token) => customHeaders :+ headers.Authorization(OAuth2BearerToken(token())) + case None => customHeaders + case Some(token) => + customHeaders :+ headers.Authorization(OAuth2BearerToken(token())) } HttpRequest(uri = url, method = HttpMethods.GET).withHeaders(allHeaders) } private def withQueryParams(params: Option[StreamParameters]): Seq[String] = { params match { - case Some(StreamParameters(_, batchLimit, streamLimit, batchFlushTimeout, streamTimeout, streamKeepAliveLimit, _)) => - val parameters = List(("batch_limit", batchLimit), ("stream_limit", streamLimit), // - ("batch_flush_timeout", batchFlushTimeout), ("stream_timeout", streamTimeout), // - ("stream_keep_alive_limit", streamKeepAliveLimit)) - for { (key, optional) <- parameters; value <- optional } yield (key + "=" + value) + case Some( + StreamParameters(_, + batchLimit, + streamLimit, + batchFlushTimeout, + streamTimeout, + streamKeepAliveLimit, + _)) => + val parameters = List( + ("batch_limit", batchLimit), + ("stream_limit", streamLimit), // + ("batch_flush_timeout", batchFlushTimeout), + ("stream_timeout", streamTimeout), // + ("stream_keep_alive_limit", streamKeepAliveLimit)) + for { (key, optional) <- parameters; value <- optional } yield + (key + "=" + value) case None => Nil } } - private def withDefaultHeaders(cursor: Option[Cursor], flowId: Option[String]): Seq[HttpHeader] = { + private def withDefaultHeaders(cursor: Option[Cursor], + flowId: Option[String]): Seq[HttpHeader] = { val nakadiCursor = cursor match { - case Some(value) if Option(value.partition).isDefined && Option(value.offset).isDefined => - ("X-Nakadi-Cursors", Some("[{\"partition\":\"" + value.partition + "\", \"offset\": \"" + value.offset + "\"}]")) + case Some(value) + if Option(value.partition).isDefined && Option(value.offset).isDefined => + ("X-Nakadi-Cursors", + Some( + "[{\"partition\":\"" + value.partition + "\", \"offset\": \"" + value.offset + "\"}]")) case None => ("X-Nakadi-Cursors", None) } val parameters = List(nakadiCursor, ("X-Flow-Id", flowId)) - for { (key, optional) <- parameters; value <- optional } yield RawHeader(key, value) + for { (key, optional) <- parameters; value <- optional } yield + RawHeader(key, value) } } diff --git a/client/src/main/scala/org/zalando/nakadi/client/scala/MessageHandler.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/MessageHandler.scala index 8abe3c8..b5b3e9a 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/scala/MessageHandler.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/scala/MessageHandler.scala @@ -4,16 +4,16 @@ import scala.util.Failure import scala.util.Success import scala.util.Try import org.zalando.nakadi.client.Deserializer -import org.zalando.nakadi.client.java.model.{ Cursor => JCursor } -import org.zalando.nakadi.client.java.model.{ Event => JEvent } -import org.zalando.nakadi.client.java.model.{ EventStreamBatch => JEventStreamBatch } -import org.zalando.nakadi.client.java.{ Listener => JListener } -import org.zalando.nakadi.client.java.{ ClientError => JClientError } +import org.zalando.nakadi.client.java.model.{Cursor => JCursor} +import org.zalando.nakadi.client.java.model.{Event => JEvent} +import org.zalando.nakadi.client.java.model.{EventStreamBatch => JEventStreamBatch} +import org.zalando.nakadi.client.java.{Listener => JListener} +import org.zalando.nakadi.client.java.{ClientError => JClientError} import org.zalando.nakadi.client.scala.model.Cursor import org.zalando.nakadi.client.scala.model.Event import org.zalando.nakadi.client.scala.model.EventStreamBatch import org.zalando.nakadi.client.utils.ModelConverter._ -import java.util.{ List => JList } +import java.util.{List => JList} import java.util.Optional import org.slf4j.LoggerFactory import jdk.nashorn.internal.runtime.regexp.joni.constants.Arguments @@ -22,16 +22,21 @@ import com.google.common.base.Preconditions._ case class ErrorResult(error: Throwable) /** - * EventHandlers should handle the logic of deserializing the messages and acting op on this attempt. - */ + * EventHandlers should handle the logic of deserializing the messages and acting op on this attempt. + */ trait EventHandler { def id(): String - def handleOnReceive(eventTypeName: String, msg: String): Either[ErrorResult, Cursor] + def handleOnReceive(eventTypeName: String, + msg: String): Either[ErrorResult, Cursor] def handleOnSubscribed(endpoint: String, cursor: Option[Cursor]): Unit - def handleOnError(eventTypeName: String, msg: Option[String], exception: Throwable) + def handleOnError(eventTypeName: String, + msg: Option[String], + exception: Throwable) } -class ScalaEventHandlerImpl[S <: Event](des: Deserializer[EventStreamBatch[S]], listener: Listener[S]) extends EventHandler { +class ScalaEventHandlerImpl[S <: Event](des: Deserializer[EventStreamBatch[S]], + listener: Listener[S]) + extends EventHandler { private val log = LoggerFactory.getLogger(this.getClass) private def createException(msg: String) = new IllegalStateException(msg) checkArgument(listener != null, "Listener must not be null", null) @@ -39,15 +44,17 @@ class ScalaEventHandlerImpl[S <: Event](des: Deserializer[EventStreamBatch[S]], def id: String = listener.id - def handleOnReceive(eventTypeName: String, msg: String): Either[ErrorResult, Cursor] = { + def handleOnReceive(eventTypeName: String, + msg: String): Either[ErrorResult, Cursor] = { Try(des.from(msg)) match { - case Success(EventStreamBatch(cursor, None)) => Right(cursor) + case Success(EventStreamBatch(cursor, None)) => Right(cursor) case Success(EventStreamBatch(cursor, Some(Nil))) => Right(cursor) case Success(EventStreamBatch(cursor, Some(events))) => listener.onReceive(eventTypeName, cursor, events) Right(cursor) case Failure(err) => - val errorMsg = s"Could not deserialize[Scala] url [$eventTypeName] listener [${listener.id}] msg [$msg] error[${err.getMessage}]" + val errorMsg = + s"Could not deserialize[Scala] url [$eventTypeName] listener [${listener.id}] msg [$msg] error[${err.getMessage}]" log.error(errorMsg) listener.onError(errorMsg, None) Left(ErrorResult(createException(errorMsg))) @@ -56,15 +63,21 @@ class ScalaEventHandlerImpl[S <: Event](des: Deserializer[EventStreamBatch[S]], } - def handleOnError(eventTypeName: String, msg: Option[String], exception: Throwable) = { + def handleOnError(eventTypeName: String, + msg: Option[String], + exception: Throwable) = { val errorMsg = if (msg.isDefined) msg.get else exception.getMessage val clientError = Some(ClientError(errorMsg, exception = Some(exception))) listener.onError(errorMsg, clientError) } - def handleOnSubscribed(endpoint: String, cursor: Option[Cursor]): Unit = listener.onSubscribed(endpoint, cursor) + def handleOnSubscribed(endpoint: String, cursor: Option[Cursor]): Unit = + listener.onSubscribed(endpoint, cursor) } -class JavaEventHandlerImpl[J <: JEvent](des: Deserializer[JEventStreamBatch[J]], listener: JListener[J]) extends EventHandler { +class JavaEventHandlerImpl[J <: JEvent]( + des: Deserializer[JEventStreamBatch[J]], + listener: JListener[J]) + extends EventHandler { private val log = LoggerFactory.getLogger(this.getClass) private def createException(msg: String) = new IllegalStateException(msg) checkArgument(listener != null, "Listener must not be null", null) @@ -72,9 +85,12 @@ class JavaEventHandlerImpl[J <: JEvent](des: Deserializer[JEventStreamBatch[J]], def id: String = listener.getId - def handleOnReceive(eventTypeName: String, msg: String): Either[ErrorResult, Cursor] = { + def handleOnReceive(eventTypeName: String, + msg: String): Either[ErrorResult, Cursor] = { Try(des.from(msg)) match { - case Success(eventBatch) if (eventBatch.getEvents != null && !eventBatch.getEvents.isEmpty()) => + case Success(eventBatch) + if (eventBatch.getEvents != null && !eventBatch.getEvents + .isEmpty()) => val Some(sCursor: Cursor) = toScalaCursor(eventBatch.getCursor) val jcursor: JCursor = eventBatch.getCursor listener.onReceive(eventTypeName, jcursor, eventBatch.getEvents) @@ -84,14 +100,17 @@ class JavaEventHandlerImpl[J <: JEvent](des: Deserializer[JEventStreamBatch[J]], Right(sCursor) case Failure(err) => Left(ErrorResult(err)) - val errorMsg = s"Could not deserialize[Java] url [$eventTypeName] listener [${listener.getId}] msg [$msg] error[${err.getMessage}]" + val errorMsg = + s"Could not deserialize[Java] url [$eventTypeName] listener [${listener.getId}] msg [$msg] error[${err.getMessage}]" log.error(errorMsg) listener.onError(errorMsg, Optional.empty()) Left(ErrorResult(createException(errorMsg))) } } - def handleOnError(eventTypeName: String, msg: Option[String], exception: Throwable) = { + def handleOnError(eventTypeName: String, + msg: Option[String], + exception: Throwable) = { val errorMsg = if (msg.isDefined) msg.get else exception.getMessage val clientError = Some(ClientError(errorMsg, exception = Some(exception))) listener.onError(errorMsg, toJavaClientError(clientError)) @@ -102,4 +121,3 @@ class JavaEventHandlerImpl[J <: JEvent](des: Deserializer[JEventStreamBatch[J]], } } - diff --git a/client/src/main/scala/org/zalando/nakadi/client/scala/model/ScalaJacksonJsonMarshaller.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/model/ScalaJacksonJsonMarshaller.scala index 188e793..34c691a 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/scala/model/ScalaJacksonJsonMarshaller.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/scala/model/ScalaJacksonJsonMarshaller.scala @@ -25,53 +25,77 @@ object JacksonJsonMarshaller { val logger = LoggerFactory.getLogger(this.getClass) // All TypeReferences - implicit def problemTR: TypeReference[Problem] = new TypeReference[Problem] {} - implicit val metricsTR: TypeReference[Metrics] = new TypeReference[Metrics] {} - implicit def partitionTR: TypeReference[Partition] = new TypeReference[Partition] {} + implicit def problemTR: TypeReference[Problem] = + new TypeReference[Problem] {} + implicit val metricsTR: TypeReference[Metrics] = + new TypeReference[Metrics] {} + implicit def partitionTR: TypeReference[Partition] = + new TypeReference[Partition] {} implicit def cursorTR: TypeReference[Cursor] = new TypeReference[Cursor] {} - implicit def eventTypeSchemaTR: TypeReference[EventTypeSchema] = new TypeReference[EventTypeSchema] {} - implicit def partitionResolutionStrategyTR: TypeReference[PartitionStrategy.Value] = new TypeReference[PartitionStrategy.Value] {} - implicit def eventEnrichmentStrategyTR: TypeReference[EventEnrichmentStrategy.Value] = new TypeReference[EventEnrichmentStrategy.Value] {} - implicit def dataChangeEventQualifierTR: TypeReference[DataChangeEventQualifier] = new TypeReference[DataChangeEventQualifier] {} - implicit def eventTypeStatisticsTR: TypeReference[EventTypeStatistics] = new TypeReference[EventTypeStatistics] {} - implicit def eventTypeTR: TypeReference[EventType] = new TypeReference[EventType] {} + implicit def eventTypeSchemaTR: TypeReference[EventTypeSchema] = + new TypeReference[EventTypeSchema] {} + implicit def partitionResolutionStrategyTR: TypeReference[ + PartitionStrategy.Value] = new TypeReference[PartitionStrategy.Value] {} + implicit def eventEnrichmentStrategyTR: TypeReference[ + EventEnrichmentStrategy.Value] = + new TypeReference[EventEnrichmentStrategy.Value] {} + implicit def dataChangeEventQualifierTR: TypeReference[ + DataChangeEventQualifier] = + new TypeReference[DataChangeEventQualifier] {} + implicit def eventTypeStatisticsTR: TypeReference[EventTypeStatistics] = + new TypeReference[EventTypeStatistics] {} + implicit def eventTypeTR: TypeReference[EventType] = + new TypeReference[EventType] {} implicit def eventTR: TypeReference[Event] = new TypeReference[Event] {} - implicit def eventStreamBatchTR: TypeReference[EventStreamBatch[_]] = new TypeReference[EventStreamBatch[_]] {} + implicit def eventStreamBatchTR: TypeReference[EventStreamBatch[_]] = + new TypeReference[EventStreamBatch[_]] {} - implicit def eventMetadataTR: TypeReference[EventMetadata] = new TypeReference[EventMetadata] {} - implicit def businessEventTR: TypeReference[BusinessEvent] = new TypeReference[BusinessEvent] {} - implicit def batchItemResponseTR: TypeReference[BatchItemResponse] = new TypeReference[BatchItemResponse] {} - implicit def dataChangeEventTR: TypeReference[DataChangeEvent[Any]] = new TypeReference[DataChangeEvent[Any]] {} + implicit def eventMetadataTR: TypeReference[EventMetadata] = + new TypeReference[EventMetadata] {} + implicit def businessEventTR: TypeReference[BusinessEvent] = + new TypeReference[BusinessEvent] {} + implicit def batchItemResponseTR: TypeReference[BatchItemResponse] = + new TypeReference[BatchItemResponse] {} + implicit def dataChangeEventTR: TypeReference[DataChangeEvent[Any]] = + new TypeReference[DataChangeEvent[Any]] {} //Lists - implicit def listOfPartitionStrategyTR: TypeReference[Seq[PartitionStrategy.Value]] = new TypeReference[Seq[PartitionStrategy.Value]] {} - implicit def listOfEventEnrichmentStrategyTR: TypeReference[Seq[EventEnrichmentStrategy.Value]] = new TypeReference[Seq[EventEnrichmentStrategy.Value]] {} - implicit def listOfEventTypeTR: TypeReference[Seq[EventType]] = new TypeReference[Seq[EventType]] {} - implicit def listOfPartitionTR: TypeReference[Seq[Partition]] = new TypeReference[Seq[Partition]] {} + implicit def listOfPartitionStrategyTR: TypeReference[ + Seq[PartitionStrategy.Value]] = + new TypeReference[Seq[PartitionStrategy.Value]] {} + implicit def listOfEventEnrichmentStrategyTR: TypeReference[ + Seq[EventEnrichmentStrategy.Value]] = + new TypeReference[Seq[EventEnrichmentStrategy.Value]] {} + implicit def listOfEventTypeTR: TypeReference[Seq[EventType]] = + new TypeReference[Seq[EventType]] {} + implicit def listOfPartitionTR: TypeReference[Seq[Partition]] = + new TypeReference[Seq[Partition]] {} - implicit def optionalDeserializer[T](implicit expectedType: TypeReference[T]): Deserializer[Option[T]] = new Deserializer[Option[T]] { - def from(from: String): Option[T] = { + implicit def optionalDeserializer[T]( + implicit expectedType: TypeReference[T]): Deserializer[Option[T]] = + new Deserializer[Option[T]] { + def from(from: String): Option[T] = { - defaultObjectMapper.readValue[Option[T]](from, expectedType) + defaultObjectMapper.readValue[Option[T]](from, expectedType) + } } - } implicit def serializer[T]: Serializer[T] = new Serializer[T] { def to(from: T): String = defaultObjectMapper.writeValueAsString(from) } - implicit def deserializer[T](implicit expectedType: TypeReference[T]): Deserializer[T] = new Deserializer[T] { - def from(from: String): T = defaultObjectMapper.readValue[T](from, expectedType) - } + implicit def deserializer[T]( + implicit expectedType: TypeReference[T]): Deserializer[T] = + new Deserializer[T] { + def from(from: String): T = + defaultObjectMapper.readValue[T](from, expectedType) + } - - lazy val defaultObjectMapper: ObjectMapper = new ObjectMapper(){ - private val module = new OptionModule - with MapModule - with SeqModule + lazy val defaultObjectMapper: ObjectMapper = new ObjectMapper() { + private val module = new OptionModule with MapModule with SeqModule with IteratorModule - this.registerModule(module) + this.registerModule(module) } // .registerModule(new DefaultScalaModule) @@ -83,13 +107,14 @@ object JacksonJsonMarshaller { .setSerializationInclusion(JsonInclude.Include.NON_EMPTY) .addHandler(new DeserializationProblemHandler() { override def handleUnknownProperty(ctxt: DeserializationContext, - jp: JsonParser, deserializer: JsonDeserializer[_], + jp: JsonParser, + deserializer: JsonDeserializer[_], beanOrClass: AnyRef, propertyName: String): Boolean = { - logger.warn(s"unknown property occurred in JSON representation: [beanOrClass=$beanOrClass, property=$propertyName]") + logger.warn( + s"unknown property occurred in JSON representation: [beanOrClass=$beanOrClass, property=$propertyName]") true } }) } - diff --git a/client/src/main/scala/org/zalando/nakadi/client/utils/ClientBuilder.scala b/client/src/main/scala/org/zalando/nakadi/client/utils/ClientBuilder.scala index f44fc96..bd71178 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/utils/ClientBuilder.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/utils/ClientBuilder.scala @@ -1,8 +1,8 @@ package org.zalando.nakadi.client.utils import org.zalando.nakadi.client.scala.Client -import org.zalando.nakadi.client.java.{ Client => JClient } -import org.zalando.nakadi.client.java.{ ClientImpl => JClientImpl } +import org.zalando.nakadi.client.java.{Client => JClient} +import org.zalando.nakadi.client.java.{ClientImpl => JClientImpl} import org.zalando.nakadi.client.scala.ClientImpl import org.zalando.nakadi.client.scala.Connection import java.util.function.Supplier @@ -10,12 +10,16 @@ import com.google.common.base.Preconditions._ object ClientBuilder { - def apply( - host: String = null, - port: Int = DEFAULT_PORT, - tokenProvider: Option[() => String] = None, - securedConnection: Boolean = true, - verifySSlCertificate: Boolean = true) = new ClientBuilder(host, port, tokenProvider, securedConnection, verifySSlCertificate) + def apply(host: String = null, + port: Int = DEFAULT_PORT, + tokenProvider: Option[() => String] = None, + securedConnection: Boolean = true, + verifySSlCertificate: Boolean = true) = + new ClientBuilder(host, + port, + tokenProvider, + securedConnection, + verifySSlCertificate) private val DEFAULT_PORT = 443 } @@ -26,54 +30,65 @@ class ClientBuilder private (host: String = null, // securedConnection: Boolean = true, // verifySSlCertificate: Boolean = true) { def this() = this(null, ClientBuilder.DEFAULT_PORT, None, true, true) - def withHost(host: String): ClientBuilder = new ClientBuilder( - checkNotNull(host), - port, - tokenProvider, - securedConnection, - verifySSlCertificate) + def withHost(host: String): ClientBuilder = + new ClientBuilder(checkNotNull(host), + port, + tokenProvider, + securedConnection, + verifySSlCertificate) - def withPort(port: Int): ClientBuilder = new ClientBuilder( - host, - port, - tokenProvider, - securedConnection, - verifySSlCertificate) + def withPort(port: Int): ClientBuilder = + new ClientBuilder(host, + port, + tokenProvider, + securedConnection, + verifySSlCertificate) - def withTokenProvider(tokenProvider: Option[() => String]): ClientBuilder = new ClientBuilder( - host, - port, - tokenProvider, - securedConnection, - verifySSlCertificate) + def withTokenProvider(tokenProvider: Option[() => String]): ClientBuilder = + new ClientBuilder(host, + port, + tokenProvider, + securedConnection, + verifySSlCertificate) - def withTokenProvider4Java(tokenProvider: Supplier[String]): ClientBuilder = withTokenProvider { - if (tokenProvider == null) { - None - } else { - Option(() => tokenProvider.get()) + def withTokenProvider4Java(tokenProvider: Supplier[String]): ClientBuilder = + withTokenProvider { + if (tokenProvider == null) { + None + } else { + Option(() => tokenProvider.get()) + } } - } - def withSecuredConnection(securedConnection: Boolean = true): ClientBuilder = new ClientBuilder( - host, - port, - tokenProvider, - checkNotNull(securedConnection), - verifySSlCertificate) + def withSecuredConnection(securedConnection: Boolean = true): ClientBuilder = + new ClientBuilder(host, + port, + tokenProvider, + checkNotNull(securedConnection), + verifySSlCertificate) - def withVerifiedSslCertificate(verifySSlCertificate: Boolean = true): ClientBuilder = new ClientBuilder( - host, - port, - tokenProvider, - securedConnection, - checkNotNull(verifySSlCertificate)) + def withVerifiedSslCertificate( + verifySSlCertificate: Boolean = true): ClientBuilder = + new ClientBuilder(host, + port, + tokenProvider, + securedConnection, + checkNotNull(verifySSlCertificate)) - def build(): Client = Connection.newClient(host, port, tokenProvider, securedConnection, verifySSlCertificate) + def build(): Client = + Connection.newClient(host, + port, + tokenProvider, + securedConnection, + verifySSlCertificate) def buildJavaClient(): JClient = { - val connection = Connection.newClientHandler4Java(host, port, tokenProvider, securedConnection, verifySSlCertificate) + val connection = Connection.newClientHandler4Java(host, + port, + tokenProvider, + securedConnection, + verifySSlCertificate) new JClientImpl(connection) } -} \ No newline at end of file +} diff --git a/client/src/main/scala/org/zalando/nakadi/client/utils/FutureConversions.scala b/client/src/main/scala/org/zalando/nakadi/client/utils/FutureConversions.scala index 06a7986..1a62f0e 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/utils/FutureConversions.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/utils/FutureConversions.scala @@ -11,47 +11,55 @@ object FutureConversions { // private def extractEither[T](either: Either[String, T]): T = either match { case Left(error) => throw new RuntimeException(error) - case Right(t) => t + case Right(t) => t } - def fromOption2Optional[T](in: scala.concurrent.Future[Option[T]]): java.util.concurrent.Future[Optional[T]] = { + def fromOption2Optional[T](in: scala.concurrent.Future[Option[T]]) + : java.util.concurrent.Future[Optional[T]] = { new MFuture[Option[T], Optional[T]](in, a => fromOptional2Optional(a)) } - def fromOption2Void[T](in: scala.concurrent.Future[Option[T]]): java.util.concurrent.Future[Void] = { + def fromOption2Void[T](in: scala.concurrent.Future[Option[T]]) + : java.util.concurrent.Future[Void] = { new MFuture[Option[T], Void](in, a => null) } - def fromFuture2Future[T](in: scala.concurrent.Future[T]): java.util.concurrent.Future[T] = { + def fromFuture2Future[T]( + in: scala.concurrent.Future[T]): java.util.concurrent.Future[T] = { new MFuture[T, T](in, a => a) } - def fromFuture2FutureVoid[T](in: scala.concurrent.Future[T]): java.util.concurrent.Future[Void] = { - new MFuture[T, Void](in, a => null) + def fromFuture2FutureVoid[T]( + in: scala.concurrent.Future[T]): java.util.concurrent.Future[Void] = { + new MFuture[T, Void](in, a => null) } + private def fromSequenceToList[T](in: Seq[T]): Optional[java.util.List[T]] = + in match { + case Nil => Optional.empty() + case seq => Optional.of(new java.util.ArrayList[T](seq)) - private def fromSequenceToList[T](in: Seq[T]): Optional[java.util.List[T]] = in match { - case Nil => Optional.empty() - case seq => Optional.of(new java.util.ArrayList[T](seq)) - - } + } private def fromOptional2Optional[R](in: Option[R]): Optional[R] = in match { case Some(value) => Optional.of(value) - case None => Optional.empty() + case None => Optional.empty() } - private def convert[T](x: scala.concurrent.Future[Either[String, T]]): java.util.concurrent.Future[T] = + private def convert[T](x: scala.concurrent.Future[Either[String, T]]) + : java.util.concurrent.Future[T] = new MFuture[Either[String, T], T](x, a => extractEither(a)) } -private class MFuture[A, B](f: scala.concurrent.Future[A], converter: A => B) extends java.util.concurrent.Future[B] { +private class MFuture[A, B](f: scala.concurrent.Future[A], converter: A => B) + extends java.util.concurrent.Future[B] { override def isCancelled: Boolean = throw new UnsupportedOperationException override def get(): B = converter.apply(Await.result(f, Duration.Inf)) - override def get(timeout: Long, unit: TimeUnit): B = converter.apply(Await.result(f, Duration.create(timeout, unit))) + override def get(timeout: Long, unit: TimeUnit): B = + converter.apply(Await.result(f, Duration.create(timeout, unit))) - override def cancel(mayInterruptIfRunning: Boolean): Boolean = throw new UnsupportedOperationException + override def cancel(mayInterruptIfRunning: Boolean): Boolean = + throw new UnsupportedOperationException override def isDone: Boolean = f.isCompleted } diff --git a/client/src/main/scala/org/zalando/nakadi/client/utils/GeneralConversions.scala b/client/src/main/scala/org/zalando/nakadi/client/utils/GeneralConversions.scala index e86b2f8..7b1aa7d 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/utils/GeneralConversions.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/utils/GeneralConversions.scala @@ -7,18 +7,20 @@ import scala.language.implicitConversions object GeneralConversions { def toOptional[T](in: Option[T]): Optional[T] = in match { - case None => Optional.empty() + case None => Optional.empty() case Some(value) => Optional.of(value) } - def toOption[T](in: Optional[T]): Option[T] = if (in.isPresent()) { - Option(in.get) - } else { - None - } + def toOption[T](in: Optional[T]): Option[T] = + if (in.isPresent()) { + Option(in.get) + } else { + None + } - def toOptionOfSeq[T](in: Option[Seq[T]]): Optional[java.util.List[T]] = in match { - case None => Optional.empty() - case Some(seq: Seq[T]) => Optional.of(seqAsJavaList(seq)) - } -} \ No newline at end of file + def toOptionOfSeq[T](in: Option[Seq[T]]): Optional[java.util.List[T]] = + in match { + case None => Optional.empty() + case Some(seq: Seq[T]) => Optional.of(seqAsJavaList(seq)) + } +} diff --git a/client/src/main/scala/org/zalando/nakadi/client/utils/ModelConverter.scala b/client/src/main/scala/org/zalando/nakadi/client/utils/ModelConverter.scala index deead65..c8fa09c 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/utils/ModelConverter.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/utils/ModelConverter.scala @@ -5,11 +5,11 @@ import org.zalando.nakadi.client.scala.model.Event import org.zalando.nakadi.client.scala.ClientError import org.zalando.nakadi.client.scala.Listener import org.zalando.nakadi.client.scala.StreamParameters -import org.zalando.nakadi.client.java.{ ClientError => JClientError } -import org.zalando.nakadi.client.java.{ StreamParameters => JStreamParameters } -import org.zalando.nakadi.client.java.model.{ Event => JEvent } -import org.zalando.nakadi.client.java.model.{ EventStreamBatch => JEventStreamBatch } -import org.zalando.nakadi.client.java.model.{ Cursor => JCursor } +import org.zalando.nakadi.client.java.{ClientError => JClientError} +import org.zalando.nakadi.client.java.{StreamParameters => JStreamParameters} +import org.zalando.nakadi.client.java.model.{Event => JEvent} +import org.zalando.nakadi.client.java.model.{EventStreamBatch => JEventStreamBatch} +import org.zalando.nakadi.client.java.model.{Cursor => JCursor} import java.util.Optional import scala.collection.JavaConversions._ import org.slf4j.LoggerFactory @@ -18,33 +18,39 @@ import org.zalando.nakadi.client.scala.model.EventStreamBatch object ModelConverter { import GeneralConversions._ - def toScalaCursor(in: Optional[JCursor]): Option[Cursor] = if (in.isPresent()) { - toScalaCursor(in.get) - } else { - None - } + def toScalaCursor(in: Optional[JCursor]): Option[Cursor] = + if (in.isPresent()) { + toScalaCursor(in.get) + } else { + None + } def toScalaCursor(in: JCursor): Option[Cursor] = Option(in) match { - case None => None + case None => None case Some(c) => Some(Cursor(c.getPartition, c.getOffset)) } - def toScalaStreamParameters(in: Optional[JStreamParameters]): Option[StreamParameters] = if (in.isPresent()) { - toScalaStreamParameters(in.get) - } else { - None - } + def toScalaStreamParameters( + in: Optional[JStreamParameters]): Option[StreamParameters] = + if (in.isPresent()) { + toScalaStreamParameters(in.get) + } else { + None + } - def toScalaStreamParameters(in: JStreamParameters): Option[StreamParameters] = Option(in) match { + def toScalaStreamParameters( + in: JStreamParameters): Option[StreamParameters] = Option(in) match { case None => None - case Some(c) => Some(StreamParameters( - cursor = toScalaCursor(c.getCursor), - batchLimit = toOption(c.getBatchLimit), - streamLimit = toOption(c.getStreamLimit), - batchFlushTimeout = toOption(c.getBatchFlushTimeout), - streamTimeout = toOption(c.getStreamTimeout), - streamKeepAliveLimit = toOption(c.getStreamKeepAliveLimit), - flowId = toOption(c.getFlowId))) + case Some(c) => + Some( + StreamParameters( + cursor = toScalaCursor(c.getCursor), + batchLimit = toOption(c.getBatchLimit), + streamLimit = toOption(c.getStreamLimit), + batchFlushTimeout = toOption(c.getBatchFlushTimeout), + streamTimeout = toOption(c.getStreamTimeout), + streamKeepAliveLimit = toOption(c.getStreamKeepAliveLimit), + flowId = toOption(c.getFlowId))) } def toJavaCursor(in: Cursor): JCursor = in match { @@ -53,26 +59,31 @@ object ModelConverter { case null => null } def toJavaCursor(in: Option[Cursor]): Optional[JCursor] = in match { - case Some(Cursor(partition, offset)) => Optional.of(new JCursor(partition, offset)) - case None => Optional.empty() + case Some(Cursor(partition, offset)) => + Optional.of(new JCursor(partition, offset)) + case None => Optional.empty() } - + def toScalaCursor[T <: JEvent](in: JEventStreamBatch[T]): Option[Cursor] = + Option(in) match { + case None => None + case Some(in) => toScalaCursor(in.getCursor) + } - def toScalaCursor[T <: JEvent](in: JEventStreamBatch[T]): Option[Cursor] = Option(in) match { - case None => None - case Some(in) => toScalaCursor(in.getCursor) - } - - def toJavaEvents[T <: JEvent](in: JEventStreamBatch[T]): Option[java.util.List[T]] = Option(in) match { - case None => None + def toJavaEvents[T <: JEvent]( + in: JEventStreamBatch[T]): Option[java.util.List[T]] = Option(in) match { + case None => None case Some(in) => Option(in.getEvents) } - def toJavaClientError(error: Option[ClientError]): Optional[JClientError] = error match { - case Some(ClientError(msg, httpStatusCodeOpt, exceptionOpt)) => Optional.of(new JClientError(msg, toOptional(httpStatusCodeOpt), toOptional(exceptionOpt))) - case None => Optional.empty() - } + def toJavaClientError(error: Option[ClientError]): Optional[JClientError] = + error match { + case Some(ClientError(msg, httpStatusCodeOpt, exceptionOpt)) => + Optional.of( + new JClientError(msg, + toOptional(httpStatusCodeOpt), + toOptional(exceptionOpt))) + case None => Optional.empty() + } - -} \ No newline at end of file +} diff --git a/client/src/main/scala/org/zalando/nakadi/client/utils/Uri.scala b/client/src/main/scala/org/zalando/nakadi/client/utils/Uri.scala index ec7942a..59f2813 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/utils/Uri.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/utils/Uri.scala @@ -1,23 +1,25 @@ package org.zalando.nakadi.client.utils object Uri { - + final def URI_METRICS = "/metrics" - + /*Events*/ final def URI_EVENT_TYPES = "/event-types" final def URI_EVENT_TYPE_BY_NAME = "/event-types/%s" - final def getEventTypeByName(eventTypeName:String) = s"/event-types/$eventTypeName" + final def getEventTypeByName(eventTypeName: String) = + s"/event-types/$eventTypeName" final def URI_EVENTS_OF_EVENT_TYPE() = "/event-types/%s/events" - final def getEventStreamingUri(eventTypeName:String) = s"/event-types/$eventTypeName/events" - + final def getEventStreamingUri(eventTypeName: String) = + s"/event-types/$eventTypeName/events" /*Partitions*/ final def URI_PARTITIONS_BY_EVENT_TYPE = "/event-types/%s/partitions" - final def getPartitions(eventTypeName:String) = s"/event-types/$eventTypeName/partitions" + final def getPartitions(eventTypeName: String) = + s"/event-types/$eventTypeName/partitions" /*Strategies*/ final def URI_VALIDATION_STRATEGIES = "/registry/validation-strategies" final def URI_ENRICHMENT_STRATEGIES = "/registry/enrichment-strategies" final def URI_PARTITIONING_STRATEGIES = "/registry/partition-strategies" -} \ No newline at end of file +} diff --git a/project/plugins.sbt b/project/plugins.sbt index 0b238f9..c6169f8 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -13,3 +13,5 @@ addSbtPlugin("net.virtual-void" % "sbt-dependency-graph" % "0.8.2") addSbtPlugin("org.scoverage" % "sbt-scoverage" % "1.3.5") addSbtPlugin("de.johoop" % "findbugs4sbt" % "1.4.0") + +addSbtPlugin("com.geirsson" % "sbt-scalafmt" % "0.2.8") From 105dfa3750ba0eeb73fdea4dd78a6cc65ae779bf Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Thu, 30 Jun 2016 09:40:11 +0200 Subject: [PATCH 114/183] techjira:LAAS-60 Fixed errors in enum. Fixes #77 --- .../java/enumerator/BatchItemPublishingStatus.java | 10 +++++----- .../nakadi/client/java/enumerator/BatchItemStep.java | 12 ++++++------ .../nakadi/client/java/enumerator/DataOperation.java | 12 ++++++------ .../java/enumerator/EventEnrichmentStrategy.java | 2 +- .../client/java/enumerator/EventTypeCategory.java | 2 +- .../nakadi/client/java/enumerator/SchemaType.java | 2 +- 6 files changed, 20 insertions(+), 20 deletions(-) diff --git a/api/src/main/java/org/zalando/nakadi/client/java/enumerator/BatchItemPublishingStatus.java b/api/src/main/java/org/zalando/nakadi/client/java/enumerator/BatchItemPublishingStatus.java index 2aabe68..8103c44 100644 --- a/api/src/main/java/org/zalando/nakadi/client/java/enumerator/BatchItemPublishingStatus.java +++ b/api/src/main/java/org/zalando/nakadi/client/java/enumerator/BatchItemPublishingStatus.java @@ -1,7 +1,7 @@ package org.zalando.nakadi.client.java.enumerator; -import scala.Option; +import java.util.Optional; import com.fasterxml.jackson.annotation.JsonValue; @@ -27,11 +27,11 @@ public String getStatus() { return status; }; - public static Option withName(String code) { + public static Optional withName(String name) { for (BatchItemPublishingStatus e : BatchItemPublishingStatus.values()) { - if (e.status.equals(code)) - return Option.apply(e); + if (e != null && e.name().equals(name)) + return Optional.of(e); } - return Option.empty(); + return Optional.empty(); } } diff --git a/api/src/main/java/org/zalando/nakadi/client/java/enumerator/BatchItemStep.java b/api/src/main/java/org/zalando/nakadi/client/java/enumerator/BatchItemStep.java index 34bc794..2e85bab 100644 --- a/api/src/main/java/org/zalando/nakadi/client/java/enumerator/BatchItemStep.java +++ b/api/src/main/java/org/zalando/nakadi/client/java/enumerator/BatchItemStep.java @@ -1,9 +1,9 @@ package org.zalando.nakadi.client.java.enumerator; -import com.fasterxml.jackson.annotation.JsonValue; +import java.util.Optional; -import scala.Option; +import com.fasterxml.jackson.annotation.JsonValue; /** @@ -31,12 +31,12 @@ public String getStep() { return step; } - public static Option withName(String code) { + public static Optional withName(String code) { for (BatchItemStep e : BatchItemStep.values()) { - if (e.step.equals(code)) - return Option.apply(e); + if (e != null && e.name().equals(code)) + return Optional.of(e); } - return Option.empty(); + return Optional.empty(); } } diff --git a/api/src/main/java/org/zalando/nakadi/client/java/enumerator/DataOperation.java b/api/src/main/java/org/zalando/nakadi/client/java/enumerator/DataOperation.java index 68444db..6c2f0a2 100644 --- a/api/src/main/java/org/zalando/nakadi/client/java/enumerator/DataOperation.java +++ b/api/src/main/java/org/zalando/nakadi/client/java/enumerator/DataOperation.java @@ -1,9 +1,9 @@ package org.zalando.nakadi.client.java.enumerator; -import com.fasterxml.jackson.annotation.JsonValue; +import java.util.Optional; -import scala.Option; +import com.fasterxml.jackson.annotation.JsonValue; /** @@ -26,11 +26,11 @@ public String getOperation() { return operation; } - public static Option withName(String code){ + public static Optional withName(String code){ for(DataOperation e : DataOperation.values()){ - if (e.operation.equals(code)) - return Option.apply(e); + if (e != null && e.name().equals(code)) + return Optional.of(e); } - return Option.empty(); + return Optional.empty(); } } diff --git a/api/src/main/java/org/zalando/nakadi/client/java/enumerator/EventEnrichmentStrategy.java b/api/src/main/java/org/zalando/nakadi/client/java/enumerator/EventEnrichmentStrategy.java index dd0d374..d6503d1 100644 --- a/api/src/main/java/org/zalando/nakadi/client/java/enumerator/EventEnrichmentStrategy.java +++ b/api/src/main/java/org/zalando/nakadi/client/java/enumerator/EventEnrichmentStrategy.java @@ -26,7 +26,7 @@ public String getMetadata() { public static Optional withName(String code) { for (EventEnrichmentStrategy e : EventEnrichmentStrategy.values()) { - if (e != null && e.metadata.equals(code)) + if (e != null && e.name().equals(code)) return Optional.of(e); } return Optional.empty(); diff --git a/api/src/main/java/org/zalando/nakadi/client/java/enumerator/EventTypeCategory.java b/api/src/main/java/org/zalando/nakadi/client/java/enumerator/EventTypeCategory.java index 87b3cad..6ec115c 100644 --- a/api/src/main/java/org/zalando/nakadi/client/java/enumerator/EventTypeCategory.java +++ b/api/src/main/java/org/zalando/nakadi/client/java/enumerator/EventTypeCategory.java @@ -25,7 +25,7 @@ public String getCategory() { public static Optional withName(String code) { for (EventTypeCategory e : EventTypeCategory.values()) { - if (e != null && e.category.equals(code)) + if (e != null && e.name().equals(code)) return Optional.of(e); } return Optional.empty(); diff --git a/api/src/main/java/org/zalando/nakadi/client/java/enumerator/SchemaType.java b/api/src/main/java/org/zalando/nakadi/client/java/enumerator/SchemaType.java index 9153571..a951f69 100644 --- a/api/src/main/java/org/zalando/nakadi/client/java/enumerator/SchemaType.java +++ b/api/src/main/java/org/zalando/nakadi/client/java/enumerator/SchemaType.java @@ -19,7 +19,7 @@ public String getSchema() { public static Optional withName(String code) { for (SchemaType e : SchemaType.values()) { - if (e != null && e.schema.equals(code)) + if (e != null && e.name().equals(code)) return Optional.of(e); } return Optional.empty(); From b766637f82b3b39b9086a6db793899a31e6ee8f9 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Thu, 30 Jun 2016 16:32:43 +0200 Subject: [PATCH 115/183] techjira:LAAS-60 Json object of DataChangeEvent does not match the API. Fixes #84. --- .../client/java/model/BatchItemResponse.java | 5 ++ .../nakadi/client/java/model/Cursor.java | 5 ++ .../client/java/model/DataChangeEvent.java | 2 +- .../client/java/model/EventMetadata.java | 69 +++++++++++++++++++ 4 files changed, 80 insertions(+), 1 deletion(-) diff --git a/api/src/main/java/org/zalando/nakadi/client/java/model/BatchItemResponse.java b/api/src/main/java/org/zalando/nakadi/client/java/model/BatchItemResponse.java index a95220d..25562e9 100644 --- a/api/src/main/java/org/zalando/nakadi/client/java/model/BatchItemResponse.java +++ b/api/src/main/java/org/zalando/nakadi/client/java/model/BatchItemResponse.java @@ -72,4 +72,9 @@ public String getDetail() { return detail; } + @Override + public String toString() { + return "BatchItemResponse [eid=" + eid + ", publishingStatus=" + publishingStatus + ", step=" + step + ", detail=" + detail + "]"; + } + } diff --git a/api/src/main/java/org/zalando/nakadi/client/java/model/Cursor.java b/api/src/main/java/org/zalando/nakadi/client/java/model/Cursor.java index 597fb04..7b8792d 100644 --- a/api/src/main/java/org/zalando/nakadi/client/java/model/Cursor.java +++ b/api/src/main/java/org/zalando/nakadi/client/java/model/Cursor.java @@ -64,6 +64,11 @@ public boolean equals(Object obj) { return true; } + @Override + public String toString() { + return "Cursor [partition=" + partition + ", offset=" + offset + "]"; + } + } diff --git a/api/src/main/java/org/zalando/nakadi/client/java/model/DataChangeEvent.java b/api/src/main/java/org/zalando/nakadi/client/java/model/DataChangeEvent.java index 7841b01..0d1ed78 100644 --- a/api/src/main/java/org/zalando/nakadi/client/java/model/DataChangeEvent.java +++ b/api/src/main/java/org/zalando/nakadi/client/java/model/DataChangeEvent.java @@ -32,7 +32,7 @@ public T getData() { public String getDataType() { return dataType; } - + @JsonProperty("data_op") @Override public DataOperation getDataOperation() { return dataOperation; diff --git a/api/src/main/java/org/zalando/nakadi/client/java/model/EventMetadata.java b/api/src/main/java/org/zalando/nakadi/client/java/model/EventMetadata.java index 932d2f7..19909c1 100644 --- a/api/src/main/java/org/zalando/nakadi/client/java/model/EventMetadata.java +++ b/api/src/main/java/org/zalando/nakadi/client/java/model/EventMetadata.java @@ -101,4 +101,73 @@ public String getPartition() { return partition; } + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((eid == null) ? 0 : eid.hashCode()); + result = prime * result + ((eventTypeName == null) ? 0 : eventTypeName.hashCode()); + result = prime * result + ((flowId == null) ? 0 : flowId.hashCode()); + result = prime * result + ((occurredAt == null) ? 0 : occurredAt.hashCode()); + result = prime * result + ((parentEids == null) ? 0 : parentEids.hashCode()); + result = prime * result + ((partition == null) ? 0 : partition.hashCode()); + result = prime * result + ((receivedAt == null) ? 0 : receivedAt.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + EventMetadata other = (EventMetadata) obj; + if (eid == null) { + if (other.eid != null) + return false; + } else if (!eid.equals(other.eid)) + return false; + if (eventTypeName == null) { + if (other.eventTypeName != null) + return false; + } else if (!eventTypeName.equals(other.eventTypeName)) + return false; + if (flowId == null) { + if (other.flowId != null) + return false; + } else if (!flowId.equals(other.flowId)) + return false; + if (occurredAt == null) { + if (other.occurredAt != null) + return false; + } else if (!occurredAt.equals(other.occurredAt)) + return false; + if (parentEids == null) { + if (other.parentEids != null) + return false; + } else if (!parentEids.equals(other.parentEids)) + return false; + if (partition == null) { + if (other.partition != null) + return false; + } else if (!partition.equals(other.partition)) + return false; + if (receivedAt == null) { + if (other.receivedAt != null) + return false; + } else if (!receivedAt.equals(other.receivedAt)) + return false; + return true; + } + + @Override + public String toString() { + return "EventMetadata [eid=" + eid + ", eventTypeName=" + eventTypeName + ", occurredAt=" + occurredAt + ", receivedAt=" + receivedAt + ", parentEids=" + parentEids + ", flowId=" + flowId + + ", partition=" + partition + "]"; + } + + + } From a28bafa411ec229543cdc2d0fc22db70018b2214 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Thu, 30 Jun 2016 16:44:14 +0200 Subject: [PATCH 116/183] techjira:LAAS-60 Development release 2.0.0-pre-alpha.17 --- project/Build.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/Build.scala b/project/Build.scala index c75ad33..0672065 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -57,7 +57,7 @@ lazy val client = withDefaults( project.settings( name := projectName, organization := "org.zalando.nakadi.client", - version := "2.0.0-pre-alpha.16", + version := "2.0.0-pre-alpha.17", crossPaths := false, scalaVersion := "2.11.8", publishTo := whereToPublishTo(isSnapshot.value), From ee731ff3f23168a015c27570e629e15b455414c9 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Fri, 1 Jul 2016 15:02:20 +0200 Subject: [PATCH 117/183] techjira:LAAS-60: Error in DatachangeEvent model. Fixes #88. --- .../client/java/model/DataChangeEvent.java | 51 ++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/api/src/main/java/org/zalando/nakadi/client/java/model/DataChangeEvent.java b/api/src/main/java/org/zalando/nakadi/client/java/model/DataChangeEvent.java index 0d1ed78..f2d8d6a 100644 --- a/api/src/main/java/org/zalando/nakadi/client/java/model/DataChangeEvent.java +++ b/api/src/main/java/org/zalando/nakadi/client/java/model/DataChangeEvent.java @@ -13,7 +13,7 @@ public class DataChangeEvent implements DataChangeEventQualifier, Event { @JsonCreator public DataChangeEvent( - @JsonProperty T data, + @JsonProperty("data") T data, @JsonProperty("data_type") String dataType, @JsonProperty("data_op") DataOperation dataOperation, @JsonProperty("metadata") EventMetadata metadata @@ -41,5 +41,54 @@ public DataOperation getDataOperation() { public EventMetadata getMetadata() { return metadata; } + + + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((data == null) ? 0 : data.hashCode()); + result = prime * result + ((dataOperation == null) ? 0 : dataOperation.hashCode()); + result = prime * result + ((dataType == null) ? 0 : dataType.hashCode()); + result = prime * result + ((metadata == null) ? 0 : metadata.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + DataChangeEvent other = (DataChangeEvent) obj; + if (data == null) { + if (other.data != null) + return false; + } else if (!data.equals(other.data)) + return false; + if (dataOperation != other.dataOperation) + return false; + if (dataType == null) { + if (other.dataType != null) + return false; + } else if (!dataType.equals(other.dataType)) + return false; + if (metadata == null) { + if (other.metadata != null) + return false; + } else if (!metadata.equals(other.metadata)) + return false; + return true; + } + + @Override + public String toString() { + return "DataChangeEvent [data=" + data + ", dataType=" + dataType + ", dataOperation=" + dataOperation + ", metadata=" + metadata + "]"; + } + + } From bd684f99d8601cb12605c75bad8fe11492cc3b66 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Fri, 1 Jul 2016 15:04:32 +0200 Subject: [PATCH 118/183] techjira:LAAS-60 Added extra tests for the Java models. #54 --- .../client/java/model/BatchItemResponse.java | 39 +++ .../client/java/model/EventStreamBatch.java | 38 +++ .../client/java/model/EventTypeSchema.java | 28 ++ .../nakadi/client/java/model/Metrics.java | 31 ++ .../nakadi/client/java/model/Problem.java | 51 ++++ .../nakadi/client/scala/model/Model.scala | 44 +-- .../java/enumerator/EnumeratorTest.java | 62 ++++ .../model/JavaJacksonJsonMarshaller.scala | 19 +- .../nakadi/client/scala/ClientImpl.scala | 6 +- .../model/ScalaJacksonJsonMarshaller.scala | 2 +- .../nakadi/client/java/BusinessEventImpl.java | 30 ++ .../java/JavaJacksonJsonMarshallerTest.java | 273 ++++++++++++++++++ .../nakadi/client/java/ModelFactory.java | 124 ++++++++ .../nakadi/client/java/SimpleEvent.java | 54 ++++ .../nakadi/client/scala/EnumModule.scala | 65 ++--- .../event/generator/SimpleEventListener.java | 42 +++ .../scala/ClientCreationExample.scala | 11 +- .../examples/scala/EventCreationExample.scala | 21 +- .../examples/scala/EventListenerExample.scala | 47 +-- .../integration/java/GetEnventTypes.scala | 8 +- .../nakadi/client/scala/ClientFactory.scala | 10 +- .../nakadi/client/scala/TestFactory.scala | 41 +-- .../scala/test/factory/ClientActions.scala | 2 +- .../scala/test/factory/EventGenerator.scala | 90 +++--- .../test/factory/EventIntegrationHelper.scala | 67 +++-- .../test/factory/events/MySimpleEvent.scala | 19 +- .../client/java/ClientIntegrationTest.java | 34 ++- .../client/scala/ClientIntegrationTest.scala | 12 + 28 files changed, 1057 insertions(+), 213 deletions(-) create mode 100644 api/src/test/java/org/zalando/nakadi/client/java/enumerator/EnumeratorTest.java create mode 100644 client/src/test/java/org/zalando/nakadi/client/java/BusinessEventImpl.java create mode 100644 client/src/test/java/org/zalando/nakadi/client/java/JavaJacksonJsonMarshallerTest.java create mode 100644 client/src/test/java/org/zalando/nakadi/client/java/ModelFactory.java create mode 100644 client/src/test/java/org/zalando/nakadi/client/java/SimpleEvent.java create mode 100644 it/src/main/java/org/zalando/nakadi/client/java/test/event/generator/SimpleEventListener.java diff --git a/api/src/main/java/org/zalando/nakadi/client/java/model/BatchItemResponse.java b/api/src/main/java/org/zalando/nakadi/client/java/model/BatchItemResponse.java index 25562e9..b30bbd6 100644 --- a/api/src/main/java/org/zalando/nakadi/client/java/model/BatchItemResponse.java +++ b/api/src/main/java/org/zalando/nakadi/client/java/model/BatchItemResponse.java @@ -71,6 +71,45 @@ public BatchItemStep getStep() { public String getDetail() { return detail; } + + + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((detail == null) ? 0 : detail.hashCode()); + result = prime * result + ((eid == null) ? 0 : eid.hashCode()); + result = prime * result + ((publishingStatus == null) ? 0 : publishingStatus.hashCode()); + result = prime * result + ((step == null) ? 0 : step.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + BatchItemResponse other = (BatchItemResponse) obj; + if (detail == null) { + if (other.detail != null) + return false; + } else if (!detail.equals(other.detail)) + return false; + if (eid == null) { + if (other.eid != null) + return false; + } else if (!eid.equals(other.eid)) + return false; + if (publishingStatus != other.publishingStatus) + return false; + if (step != other.step) + return false; + return true; + } @Override public String toString() { diff --git a/api/src/main/java/org/zalando/nakadi/client/java/model/EventStreamBatch.java b/api/src/main/java/org/zalando/nakadi/client/java/model/EventStreamBatch.java index e5147c5..62e531b 100644 --- a/api/src/main/java/org/zalando/nakadi/client/java/model/EventStreamBatch.java +++ b/api/src/main/java/org/zalando/nakadi/client/java/model/EventStreamBatch.java @@ -59,4 +59,42 @@ public List getEvents() { return events; } + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((cursor == null) ? 0 : cursor.hashCode()); + result = prime * result + ((events == null) ? 0 : events.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + EventStreamBatch other = (EventStreamBatch) obj; + if (cursor == null) { + if (other.cursor != null) + return false; + } else if (!cursor.equals(other.cursor)) + return false; + if (events == null) { + if (other.events != null) + return false; + } else if (!events.equals(other.events)) + return false; + return true; + } + + @Override + public String toString() { + return "EventStreamBatch [cursor=" + cursor + ", events=" + events + "]"; + } + + + } diff --git a/api/src/main/java/org/zalando/nakadi/client/java/model/EventTypeSchema.java b/api/src/main/java/org/zalando/nakadi/client/java/model/EventTypeSchema.java index 0e9e905..97046c2 100644 --- a/api/src/main/java/org/zalando/nakadi/client/java/model/EventTypeSchema.java +++ b/api/src/main/java/org/zalando/nakadi/client/java/model/EventTypeSchema.java @@ -34,6 +34,34 @@ public String getSchema() { return schema; } + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((schema == null) ? 0 : schema.hashCode()); + result = prime * result + ((type == null) ? 0 : type.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + EventTypeSchema other = (EventTypeSchema) obj; + if (schema == null) { + if (other.schema != null) + return false; + } else if (!schema.equals(other.schema)) + return false; + if (type != other.type) + return false; + return true; + } + @Override public String toString() { return "EventTypeSchema [type=" + type + ", schema=" + schema + "]"; diff --git a/api/src/main/java/org/zalando/nakadi/client/java/model/Metrics.java b/api/src/main/java/org/zalando/nakadi/client/java/model/Metrics.java index d942220..b1234de 100644 --- a/api/src/main/java/org/zalando/nakadi/client/java/model/Metrics.java +++ b/api/src/main/java/org/zalando/nakadi/client/java/model/Metrics.java @@ -23,6 +23,37 @@ public Map getGauges() { return gauges; } + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((gauges == null) ? 0 : gauges.hashCode()); + result = prime * result + ((version == null) ? 0 : version.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Metrics other = (Metrics) obj; + if (gauges == null) { + if (other.gauges != null) + return false; + } else if (!gauges.equals(other.gauges)) + return false; + if (version == null) { + if (other.version != null) + return false; + } else if (!version.equals(other.version)) + return false; + return true; + } + @Override public String toString() { return "Metrics [version=" + version + ", gauges=" + gauges + "]"; diff --git a/api/src/main/java/org/zalando/nakadi/client/java/model/Problem.java b/api/src/main/java/org/zalando/nakadi/client/java/model/Problem.java index 49da60d..acc29fd 100644 --- a/api/src/main/java/org/zalando/nakadi/client/java/model/Problem.java +++ b/api/src/main/java/org/zalando/nakadi/client/java/model/Problem.java @@ -55,6 +55,57 @@ public String getDetail() { public String getInstance() { return instance; } + + + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((detail == null) ? 0 : detail.hashCode()); + result = prime * result + ((instance == null) ? 0 : instance.hashCode()); + result = prime * result + ((status == null) ? 0 : status.hashCode()); + result = prime * result + ((title == null) ? 0 : title.hashCode()); + result = prime * result + ((type == null) ? 0 : type.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Problem other = (Problem) obj; + if (detail == null) { + if (other.detail != null) + return false; + } else if (!detail.equals(other.detail)) + return false; + if (instance == null) { + if (other.instance != null) + return false; + } else if (!instance.equals(other.instance)) + return false; + if (status == null) { + if (other.status != null) + return false; + } else if (!status.equals(other.status)) + return false; + if (title == null) { + if (other.title != null) + return false; + } else if (!title.equals(other.title)) + return false; + if (type == null) { + if (other.type != null) + return false; + } else if (!type.equals(other.type)) + return false; + return true; + } @Override public String toString() { diff --git a/api/src/main/scala/org/zalando/nakadi/client/scala/model/Model.scala b/api/src/main/scala/org/zalando/nakadi/client/scala/model/Model.scala index eb99fe0..5c66ce2 100644 --- a/api/src/main/scala/org/zalando/nakadi/client/scala/model/Model.scala +++ b/api/src/main/scala/org/zalando/nakadi/client/scala/model/Model.scala @@ -167,28 +167,7 @@ case class EventTypeStatistics(messagesPerMinute: Integer, readParallelism: Integer, writeParallelism: Integer) -/** - * Defines a rule for the resolution of incoming Events into partitions. Rules might require additional parameters; see the `doc` field of the existing rules for details. See `GET /registry/partition-strategies` for a list of available rules. - */ -case object PartitionStrategy extends Enumeration { - type PartitionStrategy = Value - val HASH = Value("hash") - val USER_DEFINED = Value("user_defined") - val RANDOM = Value("random") -} -class PartitionStrategyType extends TypeReference[PartitionStrategy.type] - -/** - * Defines a rule for transformation of an incoming Event. No existing fields might be modified. In practice this is used to set automatically values in the Event upon reception (i.e. set a reception timestamp on the Event). Rules might require additional parameters; see the `doc` field of the existing rules for details. See GET /registry/enrichment-strategies for a list of available rules. - */ -case object EventEnrichmentStrategy extends Enumeration { - type EventEnrichmentStrategy = Value - val METADATA = Value("metadata_enrichment") -} -class EventEnrichmentStrategyType - extends TypeReference[EventEnrichmentStrategy.type] - -/** + /** * A status corresponding to one individual Event's publishing attempt. * @param eid Eid of the corresponding item. Will be absent if missing on the incoming Event. * @param publishingStatus Indicator of the submission of the Event within a Batch. - SUBMITTED indicates successful submission, including commit on he underlying broker. - FAILED indicates the message submission was not possible and can be resubmitted if so desired. - ABORTED indicates that the submission of this item was not attempted any further due to a failure on another item in the batch. @@ -207,6 +186,27 @@ case class BatchItemResponse( // ENUMS //////////////////////// ///////////////////////////////// + +/** + * Defines a rule for the resolution of incoming Events into partitions. Rules might require additional parameters; see the `doc` field of the existing rules for details. See `GET /registry/partition-strategies` for a list of available rules. + */ +case object PartitionStrategy extends Enumeration { + type PartitionStrategy = Value + val HASH = Value("hash") + val USER_DEFINED = Value("user_defined") + val RANDOM = Value("random") +} +class PartitionStrategyType extends TypeReference[PartitionStrategy.type] + +/** + * Defines a rule for transformation of an incoming Event. No existing fields might be modified. In practice this is used to set automatically values in the Event upon reception (i.e. set a reception timestamp on the Event). Rules might require additional parameters; see the `doc` field of the existing rules for details. See GET /registry/enrichment-strategies for a list of available rules. + */ +case object EventEnrichmentStrategy extends Enumeration { + type EventEnrichmentStrategy = Value + val METADATA = Value("metadata_enrichment") +} +class EventEnrichmentStrategyType extends TypeReference[EventEnrichmentStrategy.type] + /** * Identifier for the type of operation to executed on the entity.
          * C: Creation
          diff --git a/api/src/test/java/org/zalando/nakadi/client/java/enumerator/EnumeratorTest.java b/api/src/test/java/org/zalando/nakadi/client/java/enumerator/EnumeratorTest.java new file mode 100644 index 0000000..00d444a --- /dev/null +++ b/api/src/test/java/org/zalando/nakadi/client/java/enumerator/EnumeratorTest.java @@ -0,0 +1,62 @@ +package org.zalando.nakadi.client.java.enumerator; + + +import static org.junit.Assert.assertEquals; + +import java.util.Optional; + +import org.junit.Test; + +@SuppressWarnings("static-access") +public class EnumeratorTest { + + @Test + public void testBatchItemPublishingStatus() { + for (BatchItemPublishingStatus e : BatchItemPublishingStatus.values()) { + assertEquals("BatchItemPublishingStatus ", Optional.of(e), (e.withName(e.name()))); + } + } + + @Test + public void testBatchItemStep() { + for (BatchItemStep e : BatchItemStep.values()) { + assertEquals("BatchItemStep ", Optional.of(e), (e.withName(e.name()))); + } + } + + @Test + public void testDataOperation() { + for (DataOperation e : DataOperation.values()) { + assertEquals("DataOperation ", Optional.of(e), (e.withName(e.name()))); + } + } + + @Test + public void testEventEnrichmentStrategy() { + for (EventEnrichmentStrategy e : EventEnrichmentStrategy.values()) { + assertEquals("EventEnrichmentStrategy ", Optional.of(e), (e.withName(e.name()))); + } + } + + @Test + public void testEventTypeCategory() { + for (EventTypeCategory e : EventTypeCategory.values()) { + assertEquals("EventTypeCategory ", Optional.of(e), (e.withName(e.name()))); + } + } + + @Test + public void testPartitionStrategy() { + for (PartitionStrategy e : PartitionStrategy.values()) { + assertEquals("PartitionStrategy ", Optional.of(e), (e.withName(e.name()))); + } + } + + @Test + public void testSchemaType() { + for (SchemaType e : SchemaType.values()) { + assertEquals("SchemaType ", Optional.of(e), (e.withName(e.name()))); + } + } + +} diff --git a/client/src/main/scala/org/zalando/nakadi/client/java/model/JavaJacksonJsonMarshaller.scala b/client/src/main/scala/org/zalando/nakadi/client/java/model/JavaJacksonJsonMarshaller.scala index d39faea..90553fc 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/java/model/JavaJacksonJsonMarshaller.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/java/model/JavaJacksonJsonMarshaller.scala @@ -18,6 +18,7 @@ import com.fasterxml.jackson.databind.SerializationFeature import com.fasterxml.jackson.databind.deser.DeserializationProblemHandler import com.fasterxml.jackson.module.scala.DefaultScalaModule import com.fasterxml.jackson.databind.annotation.JsonSerialize.Inclusion +import com.fasterxml.jackson.databind.JavaType object JavaJacksonJsonMarshaller { val logger = LoggerFactory.getLogger(this.getClass) @@ -51,19 +52,19 @@ object JavaJacksonJsonMarshaller { new TypeReference[DataChangeEvent[Any]] {} //Lists - def listOfPartitionStrategyTR: TypeReference[ - java.util.List[PartitionStrategy]] = + def listOfPartitionStrategyTR: TypeReference[java.util.List[PartitionStrategy]] = new TypeReference[java.util.List[PartitionStrategy]] {} - def listOfEventEnrichmentStrategyTR: TypeReference[ - java.util.List[EventEnrichmentStrategy]] = + def listOfEventEnrichmentStrategyTR: TypeReference[java.util.List[EventEnrichmentStrategy]] = new TypeReference[java.util.List[EventEnrichmentStrategy]] {} def listOfEventTypeTR: TypeReference[java.util.List[EventType]] = new TypeReference[java.util.List[EventType]] {} def listOfPartitionTR: TypeReference[java.util.List[Partition]] = new TypeReference[java.util.List[Partition]] {} + def listOfDataChangeEventTR: TypeReference[java.util.List[DataChangeEvent[Any]]] = + new TypeReference[java.util.List[DataChangeEvent[Any]]] {} def optionalDeserializer[T]( - expectedType: TypeReference[T]): Deserializer[Option[T]] = + expectedType: TypeReference[T]): Deserializer[Option[T]] = new Deserializer[Option[T]] { def from(from: String): Option[T] = { defaultObjectMapper.readValue[Option[T]](from, expectedType) @@ -80,6 +81,12 @@ object JavaJacksonJsonMarshaller { defaultObjectMapper.readValue[T](from, expectedType) } } + def deserializer[T](expectedType: JavaType): Deserializer[T] = + new Deserializer[T] { + def from(from: String): T = { + defaultObjectMapper.readValue[T](from, expectedType) + } + } lazy val defaultObjectMapper: ObjectMapper = new ObjectMapper() // .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) @@ -93,7 +100,7 @@ object JavaJacksonJsonMarshaller { beanOrClass: AnyRef, propertyName: String): Boolean = { logger.warn( - s"unknown property occurred in JSON representation: [beanOrClass=$beanOrClass, property=$propertyName]") + s"unknown property occurred in JSON representation: [beanOrClass=$beanOrClass, property=$propertyName]") true } }) diff --git a/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala index e94844f..514a1f1 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala @@ -27,6 +27,8 @@ import com.fasterxml.jackson.core.`type`.TypeReference import akka.http.scaladsl.model.HttpResponse import akka.http.scaladsl.model.StatusCodes import akka.http.scaladsl.unmarshalling.Unmarshal +import com.fasterxml.jackson.module.scala.JsonScalaEnumeration +import org.zalando.nakadi.client.scala.model.EventEnrichmentStrategyType class ClientImpl(connection: Connection, subscriber: SubscriptionHandler, @@ -182,7 +184,7 @@ class ClientImpl(connection: Connection, : Future[Either[ClientError, T]] = { future recover { case e: Throwable => - val msg = s"A unexpected error occured: ${e.getMessage}" + val msg = s"An unexpected error occured: ${e.getMessage}" logger.error(msg) Left(ClientError(msg, None)) } @@ -191,7 +193,7 @@ class ClientImpl(connection: Connection, future: Future[Option[ClientError]]): Future[Option[ClientError]] = { future recover { case e: Throwable => - val msg = s"A unexpected error occured: ${e.getMessage}" + val msg = s"An unexpected error occured: ${e.getMessage}" Option(ClientError(msg, None)) } } diff --git a/client/src/main/scala/org/zalando/nakadi/client/scala/model/ScalaJacksonJsonMarshaller.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/model/ScalaJacksonJsonMarshaller.scala index 34c691a..1cc58e3 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/scala/model/ScalaJacksonJsonMarshaller.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/scala/model/ScalaJacksonJsonMarshaller.scala @@ -101,7 +101,7 @@ object JacksonJsonMarshaller { .registerModule(new DefaultScalaModule) .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) .configure(DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT, true) - .enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY) + .configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY,true) .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) .setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE) .setSerializationInclusion(JsonInclude.Include.NON_EMPTY) diff --git a/client/src/test/java/org/zalando/nakadi/client/java/BusinessEventImpl.java b/client/src/test/java/org/zalando/nakadi/client/java/BusinessEventImpl.java new file mode 100644 index 0000000..aa4355a --- /dev/null +++ b/client/src/test/java/org/zalando/nakadi/client/java/BusinessEventImpl.java @@ -0,0 +1,30 @@ +package org.zalando.nakadi.client.java; + +import org.zalando.nakadi.client.java.model.BusinessEvent; +import org.zalando.nakadi.client.java.model.EventMetadata; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +public class BusinessEventImpl implements BusinessEvent { + private EventMetadata metadata; + + @JsonCreator + public BusinessEventImpl( @JsonProperty("metadata")EventMetadata metadata) { + this.metadata = metadata; + } + + + public EventMetadata metadata() { + return metadata; + } + public EventMetadata getMetadata() { + return metadata; + } + + @Override + public String toString() { + return "BusinessEventImpl [eventMetdata=" + metadata + "]"; + } + +} \ No newline at end of file diff --git a/client/src/test/java/org/zalando/nakadi/client/java/JavaJacksonJsonMarshallerTest.java b/client/src/test/java/org/zalando/nakadi/client/java/JavaJacksonJsonMarshallerTest.java new file mode 100644 index 0000000..374c483 --- /dev/null +++ b/client/src/test/java/org/zalando/nakadi/client/java/JavaJacksonJsonMarshallerTest.java @@ -0,0 +1,273 @@ +package org.zalando.nakadi.client.java; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.util.List; +import java.util.function.Predicate; +import java.util.stream.Stream; + +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.zalando.nakadi.client.java.model.BatchItemResponse; +import org.zalando.nakadi.client.java.model.BusinessEvent; +import org.zalando.nakadi.client.java.model.Cursor; +import org.zalando.nakadi.client.java.model.DataChangeEvent; +import org.zalando.nakadi.client.java.model.DataChangeEventQualifier; +import org.zalando.nakadi.client.java.model.Event; +import org.zalando.nakadi.client.java.model.EventMetadata; +import org.zalando.nakadi.client.java.model.EventStreamBatch; +import org.zalando.nakadi.client.java.model.EventTypeSchema; +import org.zalando.nakadi.client.java.model.EventTypeStatistics; +import org.zalando.nakadi.client.java.model.JavaJacksonJsonMarshaller; +import org.zalando.nakadi.client.java.model.Metrics; +import org.zalando.nakadi.client.java.model.Partition; +import org.zalando.nakadi.client.java.model.Problem; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.google.common.collect.Lists; + +public class JavaJacksonJsonMarshallerTest { + Logger log = LoggerFactory.getLogger(this.getClass()); + + private String toJson(T in) throws JsonProcessingException { + log.info("ToJson - in {}", in.toString()); + String result = JavaJacksonJsonMarshaller.serializer().to(in); + log.info("ToJson - out {}", result); + return result; + } + + private T toObject(String json, TypeReference expectedType) throws JsonProcessingException { + log.info("toObject - in {}", json); + T result = JavaJacksonJsonMarshaller.deserializer(expectedType).from(json); + log.info("toObject - out {}", result.toString()); + + return result; + } + + @Test + public void testBatchItemResponse() throws JsonProcessingException { + // Create object + BatchItemResponse in = ModelFactory.newBatchItemResponse(); + String json = toJson(in); + BatchItemResponse out = toObject(json, JavaJacksonJsonMarshaller.batchItemResponseTR()); + + // Check + compareBatchItemResponse(in, out); + assertEquals(json,toJson(out)); + } + + @Test + public void testBusinessEvent() throws JsonProcessingException { + BusinessEvent in = ModelFactory.newBusinessEvent(); + String json = toJson(in); + BusinessEvent out = toObject(json, new TypeReference() { + }); + compareMetadata(in.metadata(), out.metadata()); + assertEquals(json,toJson(out)); + } + + @Test + public void testCursor() throws JsonProcessingException { + Cursor in = ModelFactory.newCursor(); + String json = toJson(in); + Cursor out = toObject(json, JavaJacksonJsonMarshaller.cursorTR()); + compareCursor(in, out); + assertEquals(json,toJson(out)); + } + + @Test + public void testDataChangeEvent() throws JsonProcessingException { + SimpleEvent event = ModelFactory.newSimpleEvent(); + DataChangeEvent in = ModelFactory.newDataChangeEvent(event); + String json = toJson(in); + DataChangeEvent out = toObject(json, new TypeReference>() { + }); + + compareDataChangeEvent(in, out); + compareSimpleEvent(in.getData(), out.getData()); + assertEquals(json,toJson(out)); + + } + + @Test + public void testEventMetadata() throws JsonProcessingException { + EventMetadata in = ModelFactory.newEventMetadata(); + String json = toJson(in); + EventMetadata out = toObject(json, JavaJacksonJsonMarshaller.eventMetadataTR()); + compareMetadata(in, out); + assertEquals(json,toJson(out)); + } + + @Test + public void testEventStreamBatch() throws JsonProcessingException { + Cursor cursor = ModelFactory.newCursor(); + List events = Lists.newArrayList(ModelFactory.newSimpleEvent(), ModelFactory.newSimpleEvent()); + EventStreamBatch in = ModelFactory.newEventStreamBatch(events, cursor); + TypeReference> expectedType = new TypeReference>() { + }; + String json = toJson(in); + EventStreamBatch out = toObject(json, expectedType); + compareEventStreamBatch(in, out); + compareSimpleEvents(in.getEvents(), out.getEvents()); + assertEquals(json,toJson(out)); + } + + @Test + public void testEventTypeSchema() throws JsonProcessingException { + EventTypeSchema in = ModelFactory.newEventTypeSchema(); + String json = toJson(in); + EventTypeSchema out = toObject(json, JavaJacksonJsonMarshaller.eventTypeSchemaTR()); + compareEventTypeSchema(in, out); + assertEquals(json,toJson(out)); + + } + + @Test + public void testEventTypeStatistics() throws JsonProcessingException { + EventTypeStatistics in = ModelFactory.newEventTypeStatistics(); + String json = toJson(in); + EventTypeStatistics out = toObject(json, JavaJacksonJsonMarshaller.eventTypeStatisticsTR()); + compareEventTypeStatistics(in, out); + assertEquals(json,toJson(out)); + } + + @Test + public void testMetrics() throws JsonProcessingException { + Metrics in = ModelFactory.newMetrics(); + String json = toJson(in); + Metrics out = toObject(json, JavaJacksonJsonMarshaller.metricsTR()); + assertEquals(json,toJson(out)); + } + + @Test + public void testPartition() throws JsonProcessingException { + Partition in = ModelFactory.newPartition(); + String json = toJson(in); + Partition out = toObject(json, JavaJacksonJsonMarshaller.partitionTR()); + comparePartition(in,out); + assertEquals(json,toJson(out)); + } + + + + @Test + public void testProblem() throws JsonProcessingException { + Problem in = ModelFactory.newProblem(); + String json = toJson(in); + Problem out = toObject(json, JavaJacksonJsonMarshaller.problemTR()); + compareProblem(in,out); + assertEquals(json,toJson(out)); + + } + + private void compareProblem(Problem left, Problem right) { + assertEquals(left.getDetail(), right.getDetail()); + assertEquals(left.getInstance(), right.getInstance()); + assertEquals(left.getStatus(), right.getStatus()); + assertEquals(left.getTitle(), right.getTitle()); + assertEquals(left.getType(), right.getType()); + assertTrue(left.equals(right)); + + } + + private void comparePartition(Partition left, Partition right) { + assertEquals(left.getNewestAvailableOffset(), right.getNewestAvailableOffset()); + assertEquals(left.getOldestAvailableOffset(), right.getOldestAvailableOffset()); + assertEquals(left.getPartition(), right.getPartition()); + assertTrue(left.equals(right)); + + } + + private void compareEventTypeSchema(EventTypeSchema left, EventTypeSchema right) { + assertEquals(left.getType(), right.getType()); + assertEquals(left.getSchema(), right.getSchema()); + assertTrue(left.equals(right)); + } + + private void compareBatchItemResponse(BatchItemResponse left, BatchItemResponse right) { + assertEquals(left.getDetail(), right.getDetail()); + assertEquals(left.getEid(), right.getEid()); + assertEquals(left.getPublishingStatus(), right.getPublishingStatus()); + assertEquals(left.getStep(), right.getStep()); + assertEquals(left.toString(), right.toString()); + assertTrue(left.equals(right)); + } + + private void compareMetadata(EventMetadata left, EventMetadata right) { + assertEquals(left.getEid(), right.getEid()); + assertEquals(left.getEventTypeName(), right.getEventTypeName()); + assertEquals(left.getFlowId(), right.getFlowId()); + assertEquals(left.getOccurredAt(), right.getOccurredAt()); + assertEquals(left.getParentEids(), right.getParentEids()); + assertEquals(left.getPartition(), right.getPartition()); + assertEquals(left.getReceivedAt(), right.getReceivedAt()); + assertEquals(left.toString(), right.toString()); + assertTrue(left.equals(right)); + assertTrue(left.equals(right)); + } + + private void compareEventTypeStatistics(EventTypeStatistics left, EventTypeStatistics right) { + assertEquals(left.getMessageSize(), right.getMessageSize()); + assertEquals(left.getMessagesPerMinute(), right.getMessagesPerMinute()); + assertEquals(left.getReadParallelism(), right.getReadParallelism()); + assertEquals(left.getWriteParallelism(), right.getWriteParallelism()); + assertTrue(left.equals(right)); + + } + + + + private void compareCursor(Cursor left, Cursor right) { + assertEquals(left.getOffset(), right.getOffset()); + assertEquals(left.getPartition(), right.getPartition()); + assertTrue(left.equals(right)); + } + + private void compareDataChangeEvent(DataChangeEvent left, DataChangeEvent right) { + assertEquals(left.getData(), right.getData()); + compareDataChangeEventQualifier((DataChangeEventQualifier) left, (DataChangeEventQualifier) right); + compareMetadata(left.getMetadata(), right.getMetadata()); + assertTrue(left.equals(right)); + } + + private void compareDataChangeEventQualifier(DataChangeEventQualifier left, DataChangeEventQualifier right) { + assertEquals(left.getDataOperation(), right.getDataOperation()); + assertEquals(left.getDataType(), right.getDataType()); + assertTrue(left.equals(right)); + } + + private void compareEventStreamBatch(EventStreamBatch left, EventStreamBatch right) { + compareCursor(left.getCursor(), right.getCursor()); + assertTrue(left.equals(right)); + + } + + private void compareSimpleEvent(SimpleEvent left, SimpleEvent right) { + assertEquals(left.getId(), left.getId()); + assertTrue(left.equals(right)); + } + + private void compareSimpleEvents(List left, List right) { + for (SimpleEvent simpleEvent : left) { + Stream rightStream = right.stream(); + Predicate predicate = sameEventPredicate(simpleEvent); + Stream result = rightStream.filter(predicate); + assertEquals("Should find exact one single", result.count(), 1); + } + } + + private Predicate sameEventPredicate(SimpleEvent left) { + return new Predicate() { + + @Override + public boolean test(SimpleEvent right) { + return left.equals(right); + } + + }; + } + +} diff --git a/client/src/test/java/org/zalando/nakadi/client/java/ModelFactory.java b/client/src/test/java/org/zalando/nakadi/client/java/ModelFactory.java new file mode 100644 index 0000000..a3726d8 --- /dev/null +++ b/client/src/test/java/org/zalando/nakadi/client/java/ModelFactory.java @@ -0,0 +1,124 @@ +package org.zalando.nakadi.client.java; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Random; +import java.util.UUID; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.zalando.nakadi.client.java.enumerator.BatchItemPublishingStatus; +import org.zalando.nakadi.client.java.enumerator.BatchItemStep; +import org.zalando.nakadi.client.java.enumerator.DataOperation; +import org.zalando.nakadi.client.java.enumerator.SchemaType; +import org.zalando.nakadi.client.java.model.BatchItemResponse; +import org.zalando.nakadi.client.java.model.BusinessEvent; +import org.zalando.nakadi.client.java.model.Cursor; +import org.zalando.nakadi.client.java.model.DataChangeEvent; +import org.zalando.nakadi.client.java.model.Event; +import org.zalando.nakadi.client.java.model.EventMetadata; +import org.zalando.nakadi.client.java.model.EventStreamBatch; +import org.zalando.nakadi.client.java.model.EventTypeSchema; +import org.zalando.nakadi.client.java.model.EventTypeStatistics; +import org.zalando.nakadi.client.java.model.Metrics; +import org.zalando.nakadi.client.java.model.Partition; +import org.zalando.nakadi.client.java.model.Problem; + +import com.google.common.collect.Lists; + +public final class ModelFactory { + private ModelFactory() { + } + + private static Random randomizer = new Random(); + + public static String randomUUID() { + return UUID.randomUUID().toString(); + } + + public static BatchItemResponse newBatchItemResponse() { + String detail = "detail"; + BatchItemStep step = BatchItemStep.ENRICHING; + BatchItemPublishingStatus publishingStatus = BatchItemPublishingStatus.SUBMITTED; + String eid = randomUUID(); + return new BatchItemResponse(eid, publishingStatus, step, detail); + } + + public static BusinessEvent newBusinessEvent() { + return new BusinessEventImpl(newEventMetadata()); + } + + public static EventMetadata newEventMetadata() { + return new EventMetadata(randomUUID(), // + "eventType", "occurredAt", // + "receivedAt", Lists.newArrayList("parentEids"), "flowId", "partition"); + } + + public static Cursor newCursor() { + return new Cursor(randomUUID(), randomUUID()); + } + + public static SimpleEvent newSimpleEvent() { + return new SimpleEvent(randomUUID()); + } + + public static DataChangeEvent newDataChangeEvent(T event) { + return new DataChangeEvent(event, randomUUID(), DataOperation.CREATE, newEventMetadata()); + } + + public static EventStreamBatch newEventStreamBatch(List events, Cursor cursor) { + return new EventStreamBatch(cursor, events); + } + + public static EventTypeSchema newEventTypeSchema() { + String schema = "{'propeties':{'key':'value'}}".replaceAll("'", "\""); + return new EventTypeSchema(SchemaType.JSON, schema); + } + + public static Integer randomInt() { + return randomizer.nextInt(); + } + + public static EventTypeStatistics newEventTypeStatistics() { + return new EventTypeStatistics(randomInt(), randomInt(), randomInt(), randomInt()); + } + + public static Metrics newMetrics() { + int depth= 5; + Map gauges=new HashMap(); + gauges.put("normalString",randomUUID()); + gauges.put("SimpleEvent",newSimpleEvent()); + gauges.put("List",randomListOfString(depth)); + gauges.put("List",randomListOfString(depth)); + gauges.put("MapOfMaps",randomMapOfString(depth)); + return new Metrics("version",gauges); + } + + public static List randomListOfString(int nrOfItems){ + return Stream.generate(()-> randomUUID()).limit(nrOfItems).collect(Collectors.toList()); + + } + + /** + * A function that call itself to generate maps that contain other maps as values. + * @param nrOfItems + * @return + */ + public static Map randomMapOfString(int nrOfItems){ + return Stream.generate(()-> randomUUID()).limit(nrOfItems).collect( + Collectors.toMap(i->"nrOfItems-"+nrOfItems+"--"+i,i->randomMapOfString(nrOfItems-1))); + } + + public static Partition newPartition() { + + return new Partition(randomUUID(),randomUUID(),randomUUID()); + } + + public static Problem newProblem() { + + + return new Problem(randomUUID(), randomUUID(), randomInt(), randomUUID(), randomUUID()); + } + +} diff --git a/client/src/test/java/org/zalando/nakadi/client/java/SimpleEvent.java b/client/src/test/java/org/zalando/nakadi/client/java/SimpleEvent.java new file mode 100644 index 0000000..22f20f4 --- /dev/null +++ b/client/src/test/java/org/zalando/nakadi/client/java/SimpleEvent.java @@ -0,0 +1,54 @@ +package org.zalando.nakadi.client.java; + +import org.zalando.nakadi.client.java.model.Event; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +public class SimpleEvent implements Event { + private String id; + + @JsonCreator + public SimpleEvent(@JsonProperty("id") String id) { + this.id = id; + } + + public String getId() { + return id; + } + + + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((id == null) ? 0 : id.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + SimpleEvent other = (SimpleEvent) obj; + if (id == null) { + if (other.id != null) + return false; + } else if (!id.equals(other.id)) + return false; + return true; + } + + @Override + public String toString() { + return "SimpleEvent [id=" + id + "]"; + } + + + +} diff --git a/client/src/test/scala/org/zalando/nakadi/client/scala/EnumModule.scala b/client/src/test/scala/org/zalando/nakadi/client/scala/EnumModule.scala index 0d99b62..9c5cd34 100644 --- a/client/src/test/scala/org/zalando/nakadi/client/scala/EnumModule.scala +++ b/client/src/test/scala/org/zalando/nakadi/client/scala/EnumModule.scala @@ -21,45 +21,42 @@ import org.zalando.nakadi.client.scala.model.BatchItemPublishingStatus import org.zalando.nakadi.client.scala.model.EventTypeCategory import org.zalando.nakadi.client.scala.model.SchemaType import com.fasterxml.jackson.module.scala.JsonScalaEnumeration +import org.zalando.nakadi.client.scala.model.JacksonJsonMarshaller +import org.zalando.nakadi.client.scala.model.EventEnrichmentStrategy +import org.zalando.nakadi.client.scala.model.EventEnrichmentStrategyType -case object EnumModule extends SimpleModule() { - // addSerializer(ser) -} - -object FunninessLevel extends Enumeration { - type FunninessLevel = Value - val LOL = Value("LOL") - val ROFL = Value("ROFL") - val LMAO = Value("LMAO") -} - -class EnumSerializer extends JsonSerializer[FunninessLevel.FunninessLevel] { - def serialize(id: FunninessLevel.FunninessLevel, json: JsonGenerator, provider: SerializerProvider) = { - json.writeString(id.toString()) - } -} -class EnumDeserializer extends JsonDeserializer[FunninessLevel.FunninessLevel] { - def deserialize(jp: JsonParser, ctxt: DeserializationContext): FunninessLevel.FunninessLevel = { - FunninessLevel.withName(jp.getText()) - } -} - -//object Test extends App with JacksonJsonMarshaller { +//case object EnumModule extends SimpleModule() { +// // addSerializer(ser) +//} // -// val marshaller = new JacksonJsonMarshaller {} +//object FunninessLevel extends Enumeration { +// type FunninessLevel = Value +// val LOL = Value("LOL") +// val ROFL = Value("ROFL") +// val LMAO = Value("LMAO") +//} +// +//class EnumSerializer extends JsonSerializer[EventEnrichmentStrategy.Value] { +// def serialize(id: EventEnrichmentStrategy.Value, json: JsonGenerator, provider: SerializerProvider) = { +// json.writeString(id.toString()) +// } +//} +//class EnumDeserializer extends JsonDeserializer[EventEnrichmentStrategy.Value] { +// def deserialize(jp: JsonParser, ctxt: DeserializationContext): EventEnrichmentStrategy.Value = { +// EventEnrichmentStrategy.withName(jp.getText()) +// } +//} // -// val mapper = marshaller.defaultObjectMapper +//object Test extends App { +// import JacksonJsonMarshaller._ // -// implicit val personType = new TypeReference[Person] {} -// -// case class Person(name: String, @JsonScalaEnumeration(classOf[UserStatusType]) humor: DataOperation.Value) -// val person = Person("Fernando", DataOperation.CREATE) -// val json = marshaller.serializer[Person].toJson(person) +// val mapper = defaultObjectMapper // -// println(" #### -- PERSON >> " + person) -// println(" #### -- JSON-PERSON >> " + json) -// val personResult = marshaller.deserializer[Person].fromJson(json) -// println(" #### -- PERSON >> " + personResult) +// case class EnumHolder(@JsonScalaEnumeration(classOf[EventEnrichmentStrategyType]) humor: Seq[EventEnrichmentStrategy.Value]) +// val holder = EnumHolder(List(EventEnrichmentStrategy.METADATA)) +// val json = """["metadata_enrichment"]""" +// println("#### EnumHolder toJson #####" + mapper.writeValueAsString(holder)) +// println("#### EnumHolder toEnum #####" + mapper.readValue(json, new TypeReference[EventEnrichmentStrategy.Value]{})) // //} diff --git a/it/src/main/java/org/zalando/nakadi/client/java/test/event/generator/SimpleEventListener.java b/it/src/main/java/org/zalando/nakadi/client/java/test/event/generator/SimpleEventListener.java new file mode 100644 index 0000000..4728c9d --- /dev/null +++ b/it/src/main/java/org/zalando/nakadi/client/java/test/event/generator/SimpleEventListener.java @@ -0,0 +1,42 @@ +package org.zalando.nakadi.client.java.test.event.generator; + +import java.util.List; +import java.util.Optional; + +import org.zalando.nakadi.client.java.ClientError; +import org.zalando.nakadi.client.java.Listener; +import org.zalando.nakadi.client.java.model.Cursor; +import org.zalando.nakadi.client.java.model.Event; + +public class SimpleEventListener implements Listener{ + + @Override + public String getId() { + // TODO Auto-generated method stub + return null; + } + + + + @Override + public void onSubscribed(String endpoint, Optional cursor) { + // TODO Auto-generated method stub + + } + + @Override + public void onError(String endpoint, Optional error) { + // TODO Auto-generated method stub + + } + + + + @Override + public void onReceive(String endpoint, Cursor cursor, List events) { + // TODO Auto-generated method stub + + } + + +} diff --git a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/ClientCreationExample.scala b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/ClientCreationExample.scala index cb3acba..0c4afeb 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/ClientCreationExample.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/ClientCreationExample.scala @@ -5,9 +5,6 @@ import scala.concurrent.Await import scala.concurrent.duration.DurationInt import org.zalando.nakadi.client.utils.ClientBuilder - - - object ClientCreationExample extends App { import JacksonJsonMarshaller._ val a = ClientBuilder() @@ -17,10 +14,12 @@ object ClientCreationExample extends App { .withTokenProvider(ClientFactory.OAuth2Token()) // // .build(); .build() - val eventTypeName = "test-client-integration-event-1936085527-148383828851369665" - val url = "/event-types/test-client-integration-event-1936085527-148383828851369665/events" + val eventTypeName = + "test-client-integration-event-1936085527-148383828851369665" + val url = + "/event-types/test-client-integration-event-1936085527-148383828851369665/events" val result = Await.result(a.getPartitions(eventTypeName), 5.seconds) println("########################################") println("" + result) println("########################################") -} \ No newline at end of file +} diff --git a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala index 0e93fea..e307368 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala @@ -55,14 +55,14 @@ object EventCreationExample extends App { val paritionKeyFields = List("date", "topic") val eventType = new EventType(eventTypeName, - owner, - category, - enrichmentStrategies, - partitionStrategy, - eventTypeSchema, - dataKeyFields, - paritionKeyFields, - None) + owner, + category, + enrichmentStrategies, + partitionStrategy, + eventTypeSchema, + dataKeyFields, + paritionKeyFields, + None) //You need to import the default Serializer if you don't sepecify your own! import JacksonJsonMarshaller._ @@ -77,10 +77,11 @@ object EventCreationExample extends App { var events = ListBuffer[MeetingsEvent]() for (a <- 1 to 10) { counter += 1 - events += MeetingsEvent("2016-04-28T13:28:15+00:00", "Hackaton" + counter) + events += MeetingsEvent("2016-04-28T13:28:15+00:00", + "Hackaton" + counter) } Await.result(client.publishEvents(eventTypeName, events), 120.seconds) } client.stop() -} \ No newline at end of file +} diff --git a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala index 9b65994..e8c4d38 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala @@ -11,8 +11,8 @@ import java.util.concurrent.atomic.AtomicLong import org.slf4j.LoggerFactory /** - * Your listener will have to implement the necessary - */ + * Your listener will have to implement the necessary + */ class EventCounterListener(val id: String) extends Listener[MeetingsEvent] { val log = LoggerFactory.getLogger(this.getClass) private var eventCount: AtomicLong = new AtomicLong(0); @@ -22,7 +22,9 @@ class EventCounterListener(val id: String) extends Listener[MeetingsEvent] { println("Error %s %s".format(sourceUrl, error)) } - def onReceive(sourceUrl: String, cursor: Cursor, events: Seq[MeetingsEvent]): Unit = { + def onReceive(sourceUrl: String, + cursor: Cursor, + events: Seq[MeetingsEvent]): Unit = { eventCount.addAndGet(events.size.toLong) // log.debug("#####################################") // log.debug(s"Received " + events.size.toLong) @@ -47,37 +49,38 @@ class EventCounterListener(val id: String) extends Listener[MeetingsEvent] { object EventListenerExample extends App { /** - * Create our client - */ + * Create our client + */ val client: Client = ClientFactory.getScalaClient() /** - * Initialize our Listener - */ + * Initialize our Listener + */ val listener = new EventCounterListener("Test") /** - * Create the Parameters with the cursor. - */ - + * Create the Parameters with the cursor. + */ val cursor = Cursor("0", "BEGIN") val parameters = new StreamParameters( - cursor = Some(cursor) // + cursor = Some(cursor) // // cursor = None // - , batchLimit = Some(400) // Maximum number of `Event`s in each chunk (and therefore per partition) of the stream. - // , streamLimit = Some(500) // Maximum number of `Event`s to stream (over all partitions being streamed in this - //connection). + , + batchLimit = Some(400) // Maximum number of `Event`s in each chunk (and therefore per partition) of the stream. + // , streamLimit = Some(500) // Maximum number of `Event`s to stream (over all partitions being streamed in this + //connection). // , batchFlushTimeout = Some(10) // Maximum time in seconds to wait for the flushing of each chunk (per partition). - // ,streamKeepAliveLimit=Some(4) - , streamTimeout = Some(0) - ) + // ,streamKeepAliveLimit=Some(4) + , + streamTimeout = Some(0) + ) /** - * Create TypeReference for the JacksonObjectMapper - */ - - implicit def typeRef: TypeReference[EventStreamBatch[MeetingsEvent]] = new TypeReference[EventStreamBatch[MeetingsEvent]] {} + * Create TypeReference for the JacksonObjectMapper + */ + implicit def typeRef: TypeReference[EventStreamBatch[MeetingsEvent]] = + new TypeReference[EventStreamBatch[MeetingsEvent]] {} import org.zalando.nakadi.client.scala.model.JacksonJsonMarshaller._ // val eventTypeName = "Event-example-with-0-messages" @@ -91,4 +94,4 @@ object EventListenerExample extends App { // client.unsubscribe(eventTypeName, Some("0"), listener) // client.subscribe(eventTypeName, parameters, listener) -} \ No newline at end of file +} diff --git a/it/src/main/scala/org/zalando/nakadi/client/integration/java/GetEnventTypes.scala b/it/src/main/scala/org/zalando/nakadi/client/integration/java/GetEnventTypes.scala index b0a087d..f97c4fe 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/integration/java/GetEnventTypes.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/integration/java/GetEnventTypes.scala @@ -4,9 +4,9 @@ import org.zalando.nakadi.client.scala.ClientFactory object GetEnventTypes extends App { - val client = ClientFactory.getJavaClient(); + val client = ClientFactory.getJavaClient(); - val t = client.getEventTypes.get - println(">> " + t) + val t = client.getEventTypes.get + println(">> " + t) -} \ No newline at end of file +} diff --git a/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala b/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala index 8ca7864..1b80cf5 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala @@ -6,14 +6,15 @@ import java.util.function.Supplier object ClientFactory { import sys.process._ import scala.language.postfixOps - def OAuth2Token(): Option[() => String] = Option(() => "*") + def OAuth2Token(): Option[() => String] = + Option(() => "*") def getJavaClient() = builder().buildJavaClient(); def getScalaClient() = builder().build() private def builder() = { -// useSandbox() +// useSandbox() // useStaging() useLocal() } @@ -33,7 +34,6 @@ object ClientFactory { .withTokenProvider(ClientFactory.OAuth2Token()) } private def useStaging() = { - useSandbox() - .withHost("nakadi-staging.aruha-test.zalan.do") + useSandbox().withHost("nakadi-staging.aruha-test.zalan.do") } -} \ No newline at end of file +} diff --git a/it/src/main/scala/org/zalando/nakadi/client/scala/TestFactory.scala b/it/src/main/scala/org/zalando/nakadi/client/scala/TestFactory.scala index 6082669..a2cf6ff 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/scala/TestFactory.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/scala/TestFactory.scala @@ -11,22 +11,20 @@ import org.zalando.nakadi.client.scala.model._ import com.fasterxml.jackson.core.`type`.TypeReference import org.zalando.nakadi.client.scala.model._ - - sealed trait TestUtil { - - def executeCall[T](call: => Future[T]): T = { + + def executeCall[T](call: => Future[T]): T = { Await.result(call, 10.second) } } -case class EventActions(client: Client)extends TestUtil { +case class EventActions(client: Client) extends TestUtil { def publish[T <: Event](name: String, event: Seq[T]) = { executeCall(client.publishEvents[T](name, event)) } } -case class EventTypesActions(client: Client)extends TestUtil { +case class EventTypesActions(client: Client) extends TestUtil { def create(event: EventType) = { executeCall(client.createEventType(event)) } @@ -43,26 +41,33 @@ case class EventTypesActions(client: Client)extends TestUtil { executeCall(client.deleteEventType(name)) } - } - trait ModelFactory { val x = Random.alphanumeric case class MyEventExample(orderNumber: String) extends Event - implicit def myEventExampleTR: TypeReference[EventStreamBatch[MyEventExample]] = new TypeReference[EventStreamBatch[MyEventExample]] {} + implicit def myEventExampleTR: TypeReference[ + EventStreamBatch[MyEventExample]] = + new TypeReference[EventStreamBatch[MyEventExample]] {} //EventType def paritionKeyFields() = List("order_number") - def eventTypeSchema() = new EventTypeSchema(SchemaType.JSON, schemaDefinition) - def schemaDefinition() = """{ "properties": { "order_number": { "type": "string" } } }""" + def eventTypeSchema() = + new EventTypeSchema(SchemaType.JSON, schemaDefinition) + def schemaDefinition() = + """{ "properties": { "order_number": { "type": "string" } } }""" - def createEventType(name: String, - owningApplication: String = "nakadi-klients"): EventType = { + def createEventType( + name: String, + owningApplication: String = "nakadi-klients"): EventType = { new EventType(name, // - owningApplication, // - EventTypeCategory.UNDEFINED, Nil, // - Some(PartitionStrategy.RANDOM), eventTypeSchema, // - Nil, paritionKeyFields, None) + owningApplication, // + EventTypeCategory.UNDEFINED, + Nil, // + Some(PartitionStrategy.RANDOM), + eventTypeSchema, // + Nil, + paritionKeyFields, + None) } def createEventMetadata(): EventMetadata = { val length = 5 @@ -70,4 +75,4 @@ trait ModelFactory { val occurredAt = Calendar.getInstance().toString() new EventMetadata(eid, None, occurredAt, None, Nil, None, None) } -} \ No newline at end of file +} diff --git a/it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/ClientActions.scala b/it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/ClientActions.scala index 0afd8e8..f69cb90 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/ClientActions.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/ClientActions.scala @@ -32,4 +32,4 @@ case class ClientActions(client: Client) { Await.result(call, 10.second) } -} \ No newline at end of file +} diff --git a/it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/EventGenerator.scala b/it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/EventGenerator.scala index a7bcfa4..f2046f5 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/EventGenerator.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/EventGenerator.scala @@ -16,90 +16,90 @@ trait EventGenerator { //################################################## /** - * Should generate an eventTypeName once, - * this value should be cached(calculated only once) - * because it will be called often. - */ + * Should generate an eventTypeName once, + * this value should be cached(calculated only once) + * because it will be called often. + */ def eventTypeId: String /** - * Should generate an uniqueId that can be used for uniqueness of the Event, - */ + * Should generate an uniqueId that can be used for uniqueness of the Event, + */ def newId(): String /** - * Should generate an new Event. - */ + * Should generate an new Event. + */ def newEvent(): Event /** - * Returns the EventTypeName. - */ - + * Returns the EventTypeName. + */ def eventTypeName: String + /** - * Returns the schema definition of the Event. - */ + * Returns the schema definition of the Event. + */ def schemaDefinition: String /** - * Returns the EventType. - */ + * Returns the EventType. + */ def eventType: EventType = new EventType(eventTypeName, // - owner, // - category, // - enrichmentStrategies, // - partitionStrategy, // - schemaType, // - dataKeyFields, // - partitionKeyFields, // - statistics) + owner, // + category, // + enrichmentStrategies, // + partitionStrategy, // + schemaType, // + dataKeyFields, // + partitionKeyFields, // + statistics) //#################################################################### // METHODS WITH DEFAULTS //#################################################################### /** - * Returns the owningApplication value. Default = "Nakadi-klients(integration-test-suite)" - */ + * Returns the owningApplication value. Default = "Nakadi-klients(integration-test-suite)" + */ def owner: String = "Nakadi-klients(integration-test-suite)" /** - * Returns the category value. Default = UNDEFINED - */ + * Returns the category value. Default = UNDEFINED + */ def category: EventTypeCategory.Value = EventTypeCategory.UNDEFINED /** - * Returns the enrichmentStrategies value. Default = Nil - */ - def enrichmentStrategies: Seq[EventEnrichmentStrategy.Value] =Nil + * Returns the enrichmentStrategies value. Default = Nil + */ + def enrichmentStrategies: Seq[EventEnrichmentStrategy.Value] = Nil /** - * Returns the partitionStrategy value. Default = Random - */ - def partitionStrategy: Option[PartitionStrategy.Value] = Some(PartitionStrategy.RANDOM) + * Returns the partitionStrategy value. Default = Random + */ + def partitionStrategy: Option[PartitionStrategy.Value] = + Some(PartitionStrategy.RANDOM) /** - * Returns the eventSchemaType value. Default = new EventTypeSchema(SchemaType.JSON, schemaDefinition) - */ - def schemaType: EventTypeSchema = new EventTypeSchema(SchemaType.JSON, schemaDefinition) + * Returns the eventSchemaType value. Default = new EventTypeSchema(SchemaType.JSON, schemaDefinition) + */ + def schemaType: EventTypeSchema = + new EventTypeSchema(SchemaType.JSON, schemaDefinition) /** - * Returns the dataKeyFields value. Default = Nil - */ + * Returns the dataKeyFields value. Default = Nil + */ def dataKeyFields: Seq[String] = Nil /** - * Returns the partitionKeyFields value. Default =Nil - */ + * Returns the partitionKeyFields value. Default =Nil + */ def partitionKeyFields: Seq[String] = Nil /** - * Returns the partitionKeyFields value. Default = None - */ + * Returns the partitionKeyFields value. Default = None + */ def statistics: Option[EventTypeStatistics] = None - - -} \ No newline at end of file +} diff --git a/it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/EventIntegrationHelper.scala b/it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/EventIntegrationHelper.scala index eadcfc8..512bf1d 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/EventIntegrationHelper.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/EventIntegrationHelper.scala @@ -16,71 +16,84 @@ import scala.concurrent.Await import scala.concurrent.duration.DurationInt import scala.concurrent.Future -class EventIntegrationHelper(generator: EventGenerator, client: Client) extends WordSpec with Matchers { +class EventIntegrationHelper(generator: EventGenerator, client: Client) + extends WordSpec + with Matchers { private val log = LoggerFactory.getLogger(this.getClass) val eventType = generator.eventType def createEventType(): Boolean = { - wasItsuccessfull(executeCall(client.createEventType(eventType)), "createEventType") + wasItsuccessfull(executeCall(client.createEventType(eventType)), + "createEventType") } - def getEventType(eventTypeName: String = eventType.name): Option[EventType] = executeCall(client.getEventType(eventTypeName)) match { - case Left(clientError) => - wasItsuccessfull(Option(clientError), "getEventType") - None - case Right(result) => result - } + def getEventType(eventTypeName: String = eventType.name): Option[EventType] = + executeCall(client.getEventType(eventTypeName)) match { + case Left(clientError) => + wasItsuccessfull(Option(clientError), "getEventType") + None + case Right(result) => result + } - def deleteEventType() = wasItsuccessfull(executeCall(client.deleteEventType(eventType.name)), "deleteEventType") + def deleteEventType() = + wasItsuccessfull(executeCall(client.deleteEventType(eventType.name)), + "deleteEventType") def publishEvents(nrOfEvents: Int): Seq[Event] = { val events = for { a <- 1 to nrOfEvents } yield generator.newEvent() - wasItsuccessfull(executeCall(client.publishEvents(eventType.name, events)), "publishEvents") + wasItsuccessfull(executeCall(client.publishEvents(eventType.name, events)), + "publishEvents") log.info(s"EVENTS published: $events") events } def updateEventType(eType: EventType): Boolean = { - wasItsuccessfull(executeCall(client.updateEventType(eventType.name, eType)), "updateEventType") + wasItsuccessfull( + executeCall(client.updateEventType(eventType.name, eType)), + "updateEventType") } def eventTypeExist(eventTypeName: String = eventType.name): Boolean = { getEventType(eventTypeName) match { - case None => false + case None => false case Some(_) => true } } - def getNumberOfPartitions(): Int = extractFromRightOptional(() => client.getPartitions(eventType.name)).size + def getNumberOfPartitions(): Int = + extractFromRightOptional(() => client.getPartitions(eventType.name)).size - private def extractFromRightOptional[T](function: () => Future[Either[ClientError, Option[T]]]) = { + private def extractFromRightOptional[T]( + function: () => Future[Either[ClientError, Option[T]]]) = { val received = executeCall(function()) assert(received.isRight == true, s"ClientErrror ${received}") val Right(opt) = received - assert(opt.isDefined == true, "because it expected Some, but received None") + assert(opt.isDefined == true, + "because it expected Some, but received None") val Some(eventTypes) = opt eventTypes } - - def getEnrichmentStrategies(): Seq[EventEnrichmentStrategy.Value] = extractFromRightOptional(() => client.getEnrichmentStrategies()) - def getPartitionStrategies(): Seq[PartitionStrategy.Value] = extractFromRightOptional(() => client.getPartitioningStrategies()) + def getEnrichmentStrategies(): Seq[EventEnrichmentStrategy.Value] = + extractFromRightOptional(() => client.getEnrichmentStrategies()) + def getPartitionStrategies(): Seq[PartitionStrategy.Value] = + extractFromRightOptional(() => client.getPartitioningStrategies()) //Private methods - private def wasItsuccessfull(in: Option[ClientError], msg: String): Boolean = in match { - case Some(clientError) => - log.error(s"${generator.eventTypeId} - ${msg} => ClientError: $clientError") - false - case None => - true - } + private def wasItsuccessfull(in: Option[ClientError], msg: String): Boolean = + in match { + case Some(clientError) => + log.error( + s"${generator.eventTypeId} - ${msg} => ClientError: $clientError") + false + case None => + true + } private def executeCall[T](call: => Future[T]): T = { Await.result(call, 10.second) } } - - \ No newline at end of file diff --git a/it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/events/MySimpleEvent.scala b/it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/events/MySimpleEvent.scala index e9bed5d..b34adfa 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/events/MySimpleEvent.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/events/MySimpleEvent.scala @@ -21,12 +21,16 @@ object MySimpleEvent { def newEvent(): Event = new MySimpleEvent(newId) - lazy val eventTypeName: String = eventTypeId + Random.alphanumeric.take(12).mkString + lazy val eventTypeName: String = eventTypeId + Random.alphanumeric + .take(12) + .mkString - def schemaDefinition: String = """{ "properties": { "order_number": { "type": "string" } } }""" + def schemaDefinition: String = + """{ "properties": { "order_number": { "type": "string" } } }""" } - implicit def myEventExampleTR: TypeReference[EventStreamBatch[MySimpleEvent]] = new TypeReference[EventStreamBatch[MySimpleEvent]] {} + implicit def myEventExampleTR: TypeReference[EventStreamBatch[MySimpleEvent]] = + new TypeReference[EventStreamBatch[MySimpleEvent]] {} } case class MySimpleEvent(orderNumber: String) extends Event @@ -39,7 +43,9 @@ class SimpleEventListener extends Listener[MySimpleEvent] { log.error(s"Error $sourceUrl $error") } - def onReceive(sourceUrl: String, cursor: Cursor, events: Seq[MySimpleEvent]): Unit = { + def onReceive(sourceUrl: String, + cursor: Cursor, + events: Seq[MySimpleEvent]): Unit = { receivedEvents = receivedEvents ++ events log.info(s"Received ${events.size}") log.info(s"Total ${receivedEvents.size}") @@ -55,10 +61,9 @@ class SimpleEventListener extends Listener[MySimpleEvent] { Thread.sleep(2000) // Wait 2 seconds } log.info("############") - log.info(s"Waited to receive $nrOfEvents events, actual size =${receivedEvents.size}") + log.info( + s"Waited to receive $nrOfEvents events, actual size =${receivedEvents.size}") log.info("############") receivedEvents } } - - \ No newline at end of file diff --git a/it/src/test/java/org/zalando/client/java/ClientIntegrationTest.java b/it/src/test/java/org/zalando/client/java/ClientIntegrationTest.java index 374fae4..ee4741e 100644 --- a/it/src/test/java/org/zalando/client/java/ClientIntegrationTest.java +++ b/it/src/test/java/org/zalando/client/java/ClientIntegrationTest.java @@ -3,12 +3,16 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; +import java.util.List; import java.util.Optional; import java.util.concurrent.ExecutionException; import org.junit.After; import org.junit.Test; import org.zalando.nakadi.client.java.Client; +import org.zalando.nakadi.client.java.enumerator.EventEnrichmentStrategy; +import org.zalando.nakadi.client.java.enumerator.PartitionStrategy; +import org.zalando.nakadi.client.java.model.EventType; import org.zalando.nakadi.client.java.model.Metrics; import org.zalando.nakadi.client.scala.ClientFactory; @@ -22,14 +26,38 @@ public void shutdown() throws InterruptedException, ExecutionException { @Test public void getMetrics() throws InterruptedException, ExecutionException { - Optional result = client.getMetrics().get(); assertTrue("Metrics should be returned", result.isPresent()); - Metrics metrics = result.get(); assertNotNull("Version should be available", metrics.getVersion()); - assertTrue("Gauges should not be empty", metrics.getGauges().size() > 0); } + + @Test + public void getEventTypes() throws InterruptedException, ExecutionException{ + Optional> result = client.getEventTypes().get(); + List events = result.get(); + assertTrue(result.isPresent()); + assertTrue(events.size()>=0); + + } + + @Test + public void getEnrichmentStrategies() throws InterruptedException, ExecutionException{ + Optional> result = client.getEnrichmentStrategies().get(); + assertTrue(result.isPresent()); + List enrichtmentStrategies = result.get(); + assertTrue("EventEnrichmentStrategy",enrichtmentStrategies.size() == 1); + } + @Test + public void getPartitioningStrategies() throws InterruptedException, ExecutionException{ + Optional> result = client.getPartitioningStrategies().get(); + assertTrue(result.isPresent()); + List partitioningStrategies = result.get(); + assertTrue("PartitionStrategy",partitioningStrategies.size() ==3); + } + + + } diff --git a/it/src/test/scala/org/zalando/nakadi/client/scala/ClientIntegrationTest.scala b/it/src/test/scala/org/zalando/nakadi/client/scala/ClientIntegrationTest.scala index 92ddb28..d9d709b 100644 --- a/it/src/test/scala/org/zalando/nakadi/client/scala/ClientIntegrationTest.scala +++ b/it/src/test/scala/org/zalando/nakadi/client/scala/ClientIntegrationTest.scala @@ -44,6 +44,18 @@ class ClientIntegrationTest extends WordSpec with Matchers with BeforeAndAfterAl val Some(eventTypes) = eventTypesOpt eventTypes.size should (equal(0) or (be > 0)) } + +// "GET /registry/partitions-strategies" in { +// val result = Await.result(client.getPartitioningStrategies(), 10.seconds) +// result.isRight shouldBe true +// } + +// "GET /registry/enrichment-strategies" in { +// val result = Await.result(client.getEnrichmentStrategies(), 10.seconds) +// result.isRight shouldBe true +// } +// + } From ccb769266af062ecda04773f4ad7323d93a5e157 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Fri, 1 Jul 2016 15:09:50 +0200 Subject: [PATCH 119/183] techjira:LAAS-60 Added findbugs to the project. #69 --- project/Build.scala | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/project/Build.scala b/project/Build.scala index 0672065..0a0f112 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -3,6 +3,8 @@ import Keys._ import com.typesafe.sbteclipse.plugin.EclipsePlugin.EclipseKeys import com.typesafe.sbteclipse.plugin.EclipsePlugin.EclipseCreateSrc import Dependencies._ +import de.johoop.findbugs4sbt.FindBugs._ +import de.johoop.findbugs4sbt.ReportType object NakadiClient extends Build { @@ -54,12 +56,16 @@ lazy val client = withDefaults( ).settings(libraryDependencies ++= itDeps) def withDefaults(projectName:String, project:sbt.Project)={ - project.settings( + project + .settings(findbugsSettings: _*) + .settings( name := projectName, organization := "org.zalando.nakadi.client", version := "2.0.0-pre-alpha.17", crossPaths := false, scalaVersion := "2.11.8", + findbugsReportType := Some(ReportType.FancyHtml), + findbugsReportPath := Some(target.value / "findbugs-report.html"), publishTo := whereToPublishTo(isSnapshot.value), resolvers += Resolver.mavenLocal, resolvers += "Maven Central Server" at "http://repo1.maven.org/maven2", From 5d91d8191d91536bbed048ed4c4851c7d41cd68c Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Fri, 1 Jul 2016 15:10:34 +0200 Subject: [PATCH 120/183] techjira:LAAS-60 Added junit to api model. #88 --- project/Dependencies.scala | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/project/Dependencies.scala b/project/Dependencies.scala index c5c7b59..cb8f875 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -4,10 +4,14 @@ object Dependencies { val akkaVersion = "2.4.7" val jacksonVersion = "2.7.3" + val scalaTestVersion = "2.2.6" + val junitVersion = "4.12" val apiDeps = { Seq( "com.fasterxml.jackson.module" %% "jackson-module-scala" % jacksonVersion, - "com.fasterxml.jackson.core" % "jackson-annotations" % jacksonVersion) + "com.fasterxml.jackson.core" % "jackson-annotations" % jacksonVersion, + "org.scalatest" %% "scalatest" % scalaTestVersion % "test", + "junit" % "junit" % junitVersion % "test") } val clientDeps = { @@ -22,9 +26,9 @@ object Dependencies { "com.typesafe.akka" %% "akka-stream" % akkaVersion, "com.typesafe.akka" % "akka-slf4j_2.11" % "2.4.7", "com.typesafe.akka" %% "akka-testkit" % akkaVersion % "test", - "org.scalatest" %% "scalatest" % "2.2.6" % "test", + "org.scalatest" %% "scalatest" % scalaTestVersion % "test", "com.google.code.findbugs" % "jsr305" % "1.3.9" % "test", - "junit" % "junit" % "4.12" % "test", + "junit" % "junit" % junitVersion % "test", "org.mockito" % "mockito-core" % "1.10.19" % "test", "com.novocode" % "junit-interface" % "0.11" % "test" ) @@ -34,9 +38,10 @@ object Dependencies { Seq( "org.zalando.stups" % "tokens" % "0.9.9", "org.apache.httpcomponents" % "httpclient" % "4.5.2", - "org.scalatest" %% "scalatest" % "2.2.6", + "org.scalatest" %% "scalatest" % scalaTestVersion, "commons-lang" % "commons-lang" % "2.6", - "org.zalando" % "jackson-datatype-money" % "0.6.0" + "org.zalando" % "jackson-datatype-money" % "0.6.0", + "junit" % "junit" % junitVersion % "test" ) } } From d404fb825acd6725aa71da8243bb9ae7941da8e0 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Fri, 1 Jul 2016 15:15:15 +0200 Subject: [PATCH 121/183] techjira:LAAS-60 Make choice of SLF4J implementation plugable. Fixes #82, #79 --- project/Dependencies.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/Dependencies.scala b/project/Dependencies.scala index cb8f875..c4026b7 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -20,7 +20,7 @@ object Dependencies { "com.google.guava" % "guava" % "19.0", "com.fasterxml.jackson.core" % "jackson-core" % jacksonVersion, "com.fasterxml.jackson.module" %% "jackson-module-scala" % jacksonVersion, - "ch.qos.logback" % "logback-classic" % "1.1.7" % "runtime", + "ch.qos.logback" % "logback-classic" % "1.1.7", "com.typesafe.akka" %% "akka-actor" % akkaVersion, "com.typesafe.akka" %% "akka-http-experimental" % akkaVersion, "com.typesafe.akka" %% "akka-stream" % akkaVersion, From 7f1998079dfccda19893362e1efee1b2026d51f1 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Fri, 1 Jul 2016 15:16:01 +0200 Subject: [PATCH 122/183] techjira:LAAS-60 Development release 2.0.0-pre-alpha.18 --- project/Build.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/Build.scala b/project/Build.scala index 0a0f112..5d6de32 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -61,7 +61,7 @@ lazy val client = withDefaults( .settings( name := projectName, organization := "org.zalando.nakadi.client", - version := "2.0.0-pre-alpha.17", + version := "2.0.0-pre-alpha.18", crossPaths := false, scalaVersion := "2.11.8", findbugsReportType := Some(ReportType.FancyHtml), From dee285f4443d12c17c206482c9c1eda4e7e6fab8 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Tue, 5 Jul 2016 11:03:16 +0200 Subject: [PATCH 123/183] techjira:LAAS-60 adding extra tests for Java Models. #54 --- .../nakadi/client/java/ClientError.java | 38 ++++++++ .../nakadi/client/java/model/EventType.java | 69 ++++++++++++++ .../model/JavaJacksonJsonMarshaller.scala | 23 ++--- .../java/JavaJacksonJsonMarshallerTest.java | 91 +++++++++++++------ .../nakadi/client/java/ModelFactory.java | 32 +++++++ .../nakadi/client/scala/ClientFactory.scala | 4 +- .../client/java/ClientIntegrationTest.java | 3 +- .../client/scala/ClientIntegrationTest.scala | 1 + project/Build.scala | 2 +- 9 files changed, 219 insertions(+), 44 deletions(-) diff --git a/api/src/main/java/org/zalando/nakadi/client/java/ClientError.java b/api/src/main/java/org/zalando/nakadi/client/java/ClientError.java index 2dda0a4..bd50812 100644 --- a/api/src/main/java/org/zalando/nakadi/client/java/ClientError.java +++ b/api/src/main/java/org/zalando/nakadi/client/java/ClientError.java @@ -32,4 +32,42 @@ public Optional getException() { return exception; } + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((msg == null) ? 0 : msg.hashCode()); + result = prime * result + ((status == null) ? 0 : status.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + ClientError other = (ClientError) obj; + if (msg == null) { + if (other.msg != null) + return false; + } else if (!msg.equals(other.msg)) + return false; + if (status == null) { + if (other.status != null) + return false; + } else if (!status.equals(other.status)) + return false; + return true; + } + + @Override + public String toString() { + return "ClientError [msg=" + msg + ", status=" + status + ", exception=" + exception + "]"; + } + + + } \ No newline at end of file diff --git a/api/src/main/java/org/zalando/nakadi/client/java/model/EventType.java b/api/src/main/java/org/zalando/nakadi/client/java/model/EventType.java index 43a03f1..4889660 100644 --- a/api/src/main/java/org/zalando/nakadi/client/java/model/EventType.java +++ b/api/src/main/java/org/zalando/nakadi/client/java/model/EventType.java @@ -113,6 +113,75 @@ public List getPartitionKeyFields() { public EventTypeStatistics getStatistics() { return statistics; } + + + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((category == null) ? 0 : category.hashCode()); + result = prime * result + ((dataKeyFields == null) ? 0 : dataKeyFields.hashCode()); + result = prime * result + ((enrichmentStrategies == null) ? 0 : enrichmentStrategies.hashCode()); + result = prime * result + ((name == null) ? 0 : name.hashCode()); + result = prime * result + ((owningApplication == null) ? 0 : owningApplication.hashCode()); + result = prime * result + ((partitionKeyFields == null) ? 0 : partitionKeyFields.hashCode()); + result = prime * result + ((partitionStrategy == null) ? 0 : partitionStrategy.hashCode()); + result = prime * result + ((schema == null) ? 0 : schema.hashCode()); + result = prime * result + ((statistics == null) ? 0 : statistics.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + EventType other = (EventType) obj; + if (category != other.category) + return false; + if (dataKeyFields == null) { + if (other.dataKeyFields != null) + return false; + } else if (!dataKeyFields.equals(other.dataKeyFields)) + return false; + if (enrichmentStrategies == null) { + if (other.enrichmentStrategies != null) + return false; + } else if (!enrichmentStrategies.equals(other.enrichmentStrategies)) + return false; + if (name == null) { + if (other.name != null) + return false; + } else if (!name.equals(other.name)) + return false; + if (owningApplication == null) { + if (other.owningApplication != null) + return false; + } else if (!owningApplication.equals(other.owningApplication)) + return false; + if (partitionKeyFields == null) { + if (other.partitionKeyFields != null) + return false; + } else if (!partitionKeyFields.equals(other.partitionKeyFields)) + return false; + if (partitionStrategy != other.partitionStrategy) + return false; + if (schema == null) { + if (other.schema != null) + return false; + } else if (!schema.equals(other.schema)) + return false; + if (statistics == null) { + if (other.statistics != null) + return false; + } else if (!statistics.equals(other.statistics)) + return false; + return true; + } @Override public String toString() { diff --git a/client/src/main/scala/org/zalando/nakadi/client/java/model/JavaJacksonJsonMarshaller.scala b/client/src/main/scala/org/zalando/nakadi/client/java/model/JavaJacksonJsonMarshaller.scala index 90553fc..d835961 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/java/model/JavaJacksonJsonMarshaller.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/java/model/JavaJacksonJsonMarshaller.scala @@ -29,18 +29,15 @@ object JavaJacksonJsonMarshaller { def cursorTR: TypeReference[Cursor] = new TypeReference[Cursor] {} def eventTypeSchemaTR: TypeReference[EventTypeSchema] = new TypeReference[EventTypeSchema] {} - def partitionResolutionStrategyTR: TypeReference[PartitionStrategy] = - new TypeReference[PartitionStrategy] {} - def eventEnrichmentStrategyTR: TypeReference[EventEnrichmentStrategy] = - new TypeReference[EventEnrichmentStrategy] {} - def dataChangeEventQualifierTR: TypeReference[DataChangeEventQualifier] = - new TypeReference[DataChangeEventQualifier] {} - def eventTypeStatisticsTR: TypeReference[EventTypeStatistics] = - new TypeReference[EventTypeStatistics] {} + def eventTypeCategoryTR: TypeReference[EventTypeCategory] = + new TypeReference[EventTypeCategory] {} + def partitionResolutionStrategyTR: TypeReference[PartitionStrategy] = new TypeReference[PartitionStrategy] {} + def eventEnrichmentStrategyTR: TypeReference[EventEnrichmentStrategy] = new TypeReference[EventEnrichmentStrategy] {} + def dataChangeEventQualifierTR: TypeReference[DataChangeEventQualifier] = new TypeReference[DataChangeEventQualifier] {} + def eventTypeStatisticsTR: TypeReference[EventTypeStatistics] = new TypeReference[EventTypeStatistics] {} def eventTypeTR: TypeReference[EventType] = new TypeReference[EventType] {} def eventTR: TypeReference[Event] = new TypeReference[Event] {} - def eventStreamBatchTR: TypeReference[EventStreamBatch[_]] = - new TypeReference[EventStreamBatch[_]] {} + def eventStreamBatchTR: TypeReference[EventStreamBatch[_]] = new TypeReference[EventStreamBatch[_]] {} def eventMetadataTR: TypeReference[EventMetadata] = new TypeReference[EventMetadata] {} @@ -82,11 +79,11 @@ object JavaJacksonJsonMarshaller { } } def deserializer[T](expectedType: JavaType): Deserializer[T] = - new Deserializer[T] { + new Deserializer[T] { def from(from: String): T = { - defaultObjectMapper.readValue[T](from, expectedType) + defaultObjectMapper.readValue[T](from, expectedType) } - } + } lazy val defaultObjectMapper: ObjectMapper = new ObjectMapper() // .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) diff --git a/client/src/test/java/org/zalando/nakadi/client/java/JavaJacksonJsonMarshallerTest.java b/client/src/test/java/org/zalando/nakadi/client/java/JavaJacksonJsonMarshallerTest.java index 374c483..83011ea 100644 --- a/client/src/test/java/org/zalando/nakadi/client/java/JavaJacksonJsonMarshallerTest.java +++ b/client/src/test/java/org/zalando/nakadi/client/java/JavaJacksonJsonMarshallerTest.java @@ -1,7 +1,6 @@ package org.zalando.nakadi.client.java; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; import java.util.List; import java.util.function.Predicate; @@ -10,6 +9,9 @@ import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.zalando.nakadi.client.java.enumerator.EventEnrichmentStrategy; +import org.zalando.nakadi.client.java.enumerator.EventTypeCategory; +import org.zalando.nakadi.client.java.enumerator.PartitionStrategy; import org.zalando.nakadi.client.java.model.BatchItemResponse; import org.zalando.nakadi.client.java.model.BusinessEvent; import org.zalando.nakadi.client.java.model.Cursor; @@ -18,6 +20,7 @@ import org.zalando.nakadi.client.java.model.Event; import org.zalando.nakadi.client.java.model.EventMetadata; import org.zalando.nakadi.client.java.model.EventStreamBatch; +import org.zalando.nakadi.client.java.model.EventType; import org.zalando.nakadi.client.java.model.EventTypeSchema; import org.zalando.nakadi.client.java.model.EventTypeStatistics; import org.zalando.nakadi.client.java.model.JavaJacksonJsonMarshaller; @@ -56,7 +59,7 @@ public void testBatchItemResponse() throws JsonProcessingException { // Check compareBatchItemResponse(in, out); - assertEquals(json,toJson(out)); + assertEquals(json, toJson(out)); } @Test @@ -66,7 +69,7 @@ public void testBusinessEvent() throws JsonProcessingException { BusinessEvent out = toObject(json, new TypeReference() { }); compareMetadata(in.metadata(), out.metadata()); - assertEquals(json,toJson(out)); + assertEquals(json, toJson(out)); } @Test @@ -75,7 +78,7 @@ public void testCursor() throws JsonProcessingException { String json = toJson(in); Cursor out = toObject(json, JavaJacksonJsonMarshaller.cursorTR()); compareCursor(in, out); - assertEquals(json,toJson(out)); + assertEquals(json, toJson(out)); } @Test @@ -88,7 +91,7 @@ public void testDataChangeEvent() throws JsonProcessingException { compareDataChangeEvent(in, out); compareSimpleEvent(in.getData(), out.getData()); - assertEquals(json,toJson(out)); + assertEquals(json, toJson(out)); } @@ -98,7 +101,7 @@ public void testEventMetadata() throws JsonProcessingException { String json = toJson(in); EventMetadata out = toObject(json, JavaJacksonJsonMarshaller.eventMetadataTR()); compareMetadata(in, out); - assertEquals(json,toJson(out)); + assertEquals(json, toJson(out)); } @Test @@ -112,7 +115,7 @@ public void testEventStreamBatch() throws JsonProcessingException { EventStreamBatch out = toObject(json, expectedType); compareEventStreamBatch(in, out); compareSimpleEvents(in.getEvents(), out.getEvents()); - assertEquals(json,toJson(out)); + assertEquals(json, toJson(out)); } @Test @@ -121,7 +124,7 @@ public void testEventTypeSchema() throws JsonProcessingException { String json = toJson(in); EventTypeSchema out = toObject(json, JavaJacksonJsonMarshaller.eventTypeSchemaTR()); compareEventTypeSchema(in, out); - assertEquals(json,toJson(out)); + assertEquals(json, toJson(out)); } @@ -131,7 +134,7 @@ public void testEventTypeStatistics() throws JsonProcessingException { String json = toJson(in); EventTypeStatistics out = toObject(json, JavaJacksonJsonMarshaller.eventTypeStatisticsTR()); compareEventTypeStatistics(in, out); - assertEquals(json,toJson(out)); + assertEquals(json, toJson(out)); } @Test @@ -139,7 +142,7 @@ public void testMetrics() throws JsonProcessingException { Metrics in = ModelFactory.newMetrics(); String json = toJson(in); Metrics out = toObject(json, JavaJacksonJsonMarshaller.metricsTR()); - assertEquals(json,toJson(out)); + assertEquals(json, toJson(out)); } @Test @@ -147,22 +150,52 @@ public void testPartition() throws JsonProcessingException { Partition in = ModelFactory.newPartition(); String json = toJson(in); Partition out = toObject(json, JavaJacksonJsonMarshaller.partitionTR()); - comparePartition(in,out); - assertEquals(json,toJson(out)); + comparePartition(in, out); + assertEquals(json, toJson(out)); } - - @Test public void testProblem() throws JsonProcessingException { Problem in = ModelFactory.newProblem(); String json = toJson(in); Problem out = toObject(json, JavaJacksonJsonMarshaller.problemTR()); - compareProblem(in,out); - assertEquals(json,toJson(out)); - + compareProblem(in, out); + assertEquals(json, toJson(out)); + + } + + @Test + public void testPartitionStrategy() throws JsonProcessingException { + PartitionStrategy in = ModelFactory.newPartitionStrategy(); + String json = toJson(in); + PartitionStrategy out = toObject(json, JavaJacksonJsonMarshaller.partitionResolutionStrategyTR()); + assertEquals(json, toJson(out)); + } + + @Test + public void testEventEnrichmentStrategy() throws JsonProcessingException { + EventEnrichmentStrategy in = ModelFactory.newEventEnrichmentStrategy(); + String json = toJson(in); + EventEnrichmentStrategy out = toObject(json, JavaJacksonJsonMarshaller.eventEnrichmentStrategyTR()); + assertEquals(json, toJson(out)); + } + + @Test + public void testEventTypeCategory() throws JsonProcessingException { + EventTypeCategory in = ModelFactory.newEventTypeCategory(); + String json = toJson(in); + EventTypeCategory out = toObject(json, JavaJacksonJsonMarshaller.eventTypeCategoryTR()); + assertEquals(json, toJson(out)); + } + + @Test + public void testEventType() throws JsonProcessingException { + EventType in = ModelFactory.newEventType(); + String json = toJson(in); + EventType out = toObject(json, JavaJacksonJsonMarshaller.eventTypeTR()); + assertTrue(in.equals(out)); } - + private void compareProblem(Problem left, Problem right) { assertEquals(left.getDetail(), right.getDetail()); assertEquals(left.getInstance(), right.getInstance()); @@ -170,7 +203,8 @@ private void compareProblem(Problem left, Problem right) { assertEquals(left.getTitle(), right.getTitle()); assertEquals(left.getType(), right.getType()); assertTrue(left.equals(right)); - + assertTrue(left.hashCode() == right.hashCode()); + } private void comparePartition(Partition left, Partition right) { @@ -178,13 +212,14 @@ private void comparePartition(Partition left, Partition right) { assertEquals(left.getOldestAvailableOffset(), right.getOldestAvailableOffset()); assertEquals(left.getPartition(), right.getPartition()); assertTrue(left.equals(right)); - - } + assertEquals(left.hashCode(), right.hashCode()); + } private void compareEventTypeSchema(EventTypeSchema left, EventTypeSchema right) { assertEquals(left.getType(), right.getType()); assertEquals(left.getSchema(), right.getSchema()); assertTrue(left.equals(right)); + assertEquals(left.hashCode(), right.hashCode()); } private void compareBatchItemResponse(BatchItemResponse left, BatchItemResponse right) { @@ -194,6 +229,7 @@ private void compareBatchItemResponse(BatchItemResponse left, BatchItemResponse assertEquals(left.getStep(), right.getStep()); assertEquals(left.toString(), right.toString()); assertTrue(left.equals(right)); + assertEquals(left.hashCode(), right.hashCode()); } private void compareMetadata(EventMetadata left, EventMetadata right) { @@ -206,7 +242,7 @@ private void compareMetadata(EventMetadata left, EventMetadata right) { assertEquals(left.getReceivedAt(), right.getReceivedAt()); assertEquals(left.toString(), right.toString()); assertTrue(left.equals(right)); - assertTrue(left.equals(right)); + assertEquals(left.hashCode(), right.hashCode()); } private void compareEventTypeStatistics(EventTypeStatistics left, EventTypeStatistics right) { @@ -215,15 +251,14 @@ private void compareEventTypeStatistics(EventTypeStatistics left, EventTypeStati assertEquals(left.getReadParallelism(), right.getReadParallelism()); assertEquals(left.getWriteParallelism(), right.getWriteParallelism()); assertTrue(left.equals(right)); - + assertEquals(left.hashCode(), right.hashCode()); } - - private void compareCursor(Cursor left, Cursor right) { assertEquals(left.getOffset(), right.getOffset()); assertEquals(left.getPartition(), right.getPartition()); assertTrue(left.equals(right)); + assertEquals(left.hashCode(), right.hashCode()); } private void compareDataChangeEvent(DataChangeEvent left, DataChangeEvent right) { @@ -231,23 +266,27 @@ private void compareDataChangeEvent(DataChangeEvent left, DataChang compareDataChangeEventQualifier((DataChangeEventQualifier) left, (DataChangeEventQualifier) right); compareMetadata(left.getMetadata(), right.getMetadata()); assertTrue(left.equals(right)); + assertEquals(left.hashCode(), right.hashCode()); } private void compareDataChangeEventQualifier(DataChangeEventQualifier left, DataChangeEventQualifier right) { assertEquals(left.getDataOperation(), right.getDataOperation()); assertEquals(left.getDataType(), right.getDataType()); assertTrue(left.equals(right)); + assertEquals(left.hashCode(), right.hashCode()); } private void compareEventStreamBatch(EventStreamBatch left, EventStreamBatch right) { compareCursor(left.getCursor(), right.getCursor()); assertTrue(left.equals(right)); + assertEquals(left.hashCode(), right.hashCode()); } private void compareSimpleEvent(SimpleEvent left, SimpleEvent right) { assertEquals(left.getId(), left.getId()); assertTrue(left.equals(right)); + assertEquals(left.hashCode(), right.hashCode()); } private void compareSimpleEvents(List left, List right) { diff --git a/client/src/test/java/org/zalando/nakadi/client/java/ModelFactory.java b/client/src/test/java/org/zalando/nakadi/client/java/ModelFactory.java index a3726d8..f6f820e 100644 --- a/client/src/test/java/org/zalando/nakadi/client/java/ModelFactory.java +++ b/client/src/test/java/org/zalando/nakadi/client/java/ModelFactory.java @@ -3,6 +3,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Random; import java.util.UUID; import java.util.stream.Collectors; @@ -11,6 +12,9 @@ import org.zalando.nakadi.client.java.enumerator.BatchItemPublishingStatus; import org.zalando.nakadi.client.java.enumerator.BatchItemStep; import org.zalando.nakadi.client.java.enumerator.DataOperation; +import org.zalando.nakadi.client.java.enumerator.EventEnrichmentStrategy; +import org.zalando.nakadi.client.java.enumerator.EventTypeCategory; +import org.zalando.nakadi.client.java.enumerator.PartitionStrategy; import org.zalando.nakadi.client.java.enumerator.SchemaType; import org.zalando.nakadi.client.java.model.BatchItemResponse; import org.zalando.nakadi.client.java.model.BusinessEvent; @@ -19,6 +23,7 @@ import org.zalando.nakadi.client.java.model.Event; import org.zalando.nakadi.client.java.model.EventMetadata; import org.zalando.nakadi.client.java.model.EventStreamBatch; +import org.zalando.nakadi.client.java.model.EventType; import org.zalando.nakadi.client.java.model.EventTypeSchema; import org.zalando.nakadi.client.java.model.EventTypeStatistics; import org.zalando.nakadi.client.java.model.Metrics; @@ -121,4 +126,31 @@ public static Problem newProblem() { return new Problem(randomUUID(), randomUUID(), randomInt(), randomUUID(), randomUUID()); } + public static EventType newEventType() { + List newEnrichmentStrategy=Lists.newArrayList(EventEnrichmentStrategy.METADATA); + return new EventType(randomUUID(), randomUUID(), EventTypeCategory.BUSINESS, // + newEnrichmentStrategy, newPartitionStrategy(), newEventTypeSchema(), randomListOfString(12), randomListOfString(21), newEventTypeStatistics()); + } + + + + public static PartitionStrategy newPartitionStrategy() { + return PartitionStrategy.values()[randomizer.nextInt(PartitionStrategy.values().length)]; + } + + public static EventEnrichmentStrategy newEventEnrichmentStrategy() { + + return EventEnrichmentStrategy.values()[randomizer.nextInt(EventEnrichmentStrategy.values().length)]; + } + + public static EventTypeCategory newEventTypeCategory() { + return EventTypeCategory.values()[randomizer.nextInt(EventTypeCategory.values().length)]; + } + + public static ClientError newClientError() { + return new ClientError(randomUUID(), Optional.of(randomInt()), Optional.of(new IllegalStateException())); + } + + + } diff --git a/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala b/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala index 1b80cf5..9985279 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala @@ -14,8 +14,8 @@ object ClientFactory { def getScalaClient() = builder().build() private def builder() = { -// useSandbox() -// useStaging() + // useSandbox() + // useStaging() useLocal() } private def useLocal() = { diff --git a/it/src/test/java/org/zalando/client/java/ClientIntegrationTest.java b/it/src/test/java/org/zalando/client/java/ClientIntegrationTest.java index ee4741e..b6a6dcf 100644 --- a/it/src/test/java/org/zalando/client/java/ClientIntegrationTest.java +++ b/it/src/test/java/org/zalando/client/java/ClientIntegrationTest.java @@ -31,7 +31,6 @@ public void getMetrics() throws InterruptedException, ExecutionException { Metrics metrics = result.get(); assertNotNull("Version should be available", metrics.getVersion()); assertTrue("Gauges should not be empty", metrics.getGauges().size() > 0); - } @Test @@ -40,7 +39,6 @@ public void getEventTypes() throws InterruptedException, ExecutionException{ List events = result.get(); assertTrue(result.isPresent()); assertTrue(events.size()>=0); - } @Test @@ -50,6 +48,7 @@ public void getEnrichmentStrategies() throws InterruptedException, ExecutionExce List enrichtmentStrategies = result.get(); assertTrue("EventEnrichmentStrategy",enrichtmentStrategies.size() == 1); } + @Test public void getPartitioningStrategies() throws InterruptedException, ExecutionException{ Optional> result = client.getPartitioningStrategies().get(); diff --git a/it/src/test/scala/org/zalando/nakadi/client/scala/ClientIntegrationTest.scala b/it/src/test/scala/org/zalando/nakadi/client/scala/ClientIntegrationTest.scala index d9d709b..c8dc082 100644 --- a/it/src/test/scala/org/zalando/nakadi/client/scala/ClientIntegrationTest.scala +++ b/it/src/test/scala/org/zalando/nakadi/client/scala/ClientIntegrationTest.scala @@ -32,6 +32,7 @@ class ClientIntegrationTest extends WordSpec with Matchers with BeforeAndAfterAl val Right(metricsOpt) = result metricsOpt.isDefined shouldBe true val Some(metrics) = metricsOpt + println(metrics) metrics.version shouldNot be (null) metrics.gauges.size should be > 0 } diff --git a/project/Build.scala b/project/Build.scala index 5d6de32..09817a0 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -38,7 +38,7 @@ val defaultOptions= Seq( lazy val root = project.in(file(".")) .settings(publishTo := whereToPublishTo(isSnapshot.value)) - .aggregate(api, client) + .aggregate(api, client, it) lazy val api = withDefaults( "nakadi-klients-api", From 05ecb5f1041bdf55391c134ed6969344857a6812 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Tue, 5 Jul 2016 11:03:37 +0200 Subject: [PATCH 124/183] techjira:LAAS-60 adding extra tests for Java Models. #54 --- project/Build.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/Build.scala b/project/Build.scala index 09817a0..5d6de32 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -38,7 +38,7 @@ val defaultOptions= Seq( lazy val root = project.in(file(".")) .settings(publishTo := whereToPublishTo(isSnapshot.value)) - .aggregate(api, client, it) + .aggregate(api, client) lazy val api = withDefaults( "nakadi-klients-api", From 3ebe8d277666f530e2b5cceb9d7ee3a0dbb911b1 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Tue, 5 Jul 2016 11:37:02 +0200 Subject: [PATCH 125/183] techjira:LAAS-60 adding ScalaFMT. #54 --- .scalafmt | 3 + .../nakadi/client/scala/model/Model.scala | 78 +++++++++---------- .../model/JavaJacksonJsonMarshaller.scala | 24 +++--- .../model/ScalaJacksonJsonMarshaller.scala | 30 +++---- project/Build.scala | 2 + 5 files changed, 63 insertions(+), 74 deletions(-) create mode 100644 .scalafmt diff --git a/.scalafmt b/.scalafmt new file mode 100644 index 0000000..7d1ad18 --- /dev/null +++ b/.scalafmt @@ -0,0 +1,3 @@ +# Example .scalafmt, comments are supported. +--style defaultWithAlign # For pretty alignment. +--maxColumn 60 # For my wide 30" display. diff --git a/api/src/main/scala/org/zalando/nakadi/client/scala/model/Model.scala b/api/src/main/scala/org/zalando/nakadi/client/scala/model/Model.scala index 5c66ce2..d01b346 100644 --- a/api/src/main/scala/org/zalando/nakadi/client/scala/model/Model.scala +++ b/api/src/main/scala/org/zalando/nakadi/client/scala/model/Model.scala @@ -40,14 +40,13 @@ case class Cursor(partition: String, offset: String) * @param metadata This Metadata contains common fields unrelated to Nakadi logic. Should be mainly enriched by the Consumer. * */ -case class EventMetadata( - eid: String, - @JsonProperty("event_type") eventTypeName: Option[String], - occurredAt: String, - receivedAt: Option[String], - parentEids: Seq[String], - flowId: Option[String], - partition: Option[String]) +case class EventMetadata(eid: String, + @JsonProperty("event_type") eventTypeName: Option[String], + occurredAt: String, + receivedAt: Option[String], + parentEids: Seq[String], + flowId: Option[String], + partition: Option[String]) /** * A Business Event. Usually represents a status transition in a Business process. @@ -74,11 +73,11 @@ trait DataChangeEventQualifier { * @param eventQualifier Indicators of a `DataChangeEvent`'s referred data type and the type of operations done on them. * @param metadata Metadata for this Event. Contains commons fields for both Business and DataChange Events. Most are enriched by Nakadi upon reception, but they in general MIGHT be set by the client */ -case class DataChangeEvent[T](data: T, - dataType: String, - @JsonProperty("data_op") @JsonScalaEnumeration( - classOf[DataOperationType]) dataOperation: DataOperation.Value, - metadata: Option[EventMetadata]) +case class DataChangeEvent[T]( + data: T, + dataType: String, + @JsonProperty("data_op") @JsonScalaEnumeration(classOf[DataOperationType]) dataOperation: DataOperation.Value, + metadata: Option[EventMetadata]) extends DataChangeEventQualifier with Event @@ -103,9 +102,7 @@ case class Metrics(version: String, gauges: Map[String, Any]) * @param oldestAvailableOffset An offset of the oldest available Event in that partition. This value will be changing upon removal of Events from the partition by the background archiving/cleanup mechanism. * @param newestAvailableOffset An offset of the newest available Event in that partition. This value will be changing upon reception of new events for this partition by Nakadi. This value can be used to construct a cursor when opening streams (see `GET /event-type/{name}/events` for details). Might assume the special name BEGIN, meaning a pointer to the offset of the oldest available event in the partition. */ -case class Partition(partition: String, - oldestAvailableOffset: String, - newestAvailableOffset: String) +case class Partition(partition: String, oldestAvailableOffset: String, newestAvailableOffset: String) /** * One chunk of events in a stream. A batch consists of an array of `Event`s plus a `Cursor` pointing to the offset of the last Event in the stream. The size of the array of Event is limited by the parameters used to initialize a Stream. If acting as a keep alive message (see `GET /event-type/{name}/events`) the events array will be omitted. Sequential batches might repeat the cursor if no new events arrive. @@ -134,13 +131,11 @@ case class EventType( @JsonScalaEnumeration(classOf[EventTypeCategoryType]) category: EventTypeCategory.Value, @JsonScalaEnumeration(classOf[EventEnrichmentStrategyType]) enrichmentStrategies: Seq[ EventEnrichmentStrategy.Value], - @JsonScalaEnumeration(classOf[PartitionStrategyType]) partitionStrategy: Option[ - PartitionStrategy.Value], + @JsonScalaEnumeration(classOf[PartitionStrategyType]) partitionStrategy: Option[PartitionStrategy.Value], schema: EventTypeSchema, dataKeyFields: Seq[String], partitionKeyFields: Seq[String], - @JsonProperty("default_statistics") statistics: Option[ - EventTypeStatistics]) + @JsonProperty("default_statistics") statistics: Option[EventTypeStatistics]) /** * The schema for an EventType, expected to be a json schema in yaml @@ -149,9 +144,9 @@ case class EventType( * @param schema The schema as string in the syntax defined in the field type. * Failure to respect the syntax will fail any operation on an EventType. */ -case class EventTypeSchema(@JsonProperty("type") @JsonScalaEnumeration( - classOf[SchemaTypeType]) schemaType: SchemaType.Value, - schema: String) +case class EventTypeSchema( + @JsonProperty("type") @JsonScalaEnumeration(classOf[SchemaTypeType]) schemaType: SchemaType.Value, + schema: String) /** * Operational statistics for an EventType. This data is generated by Nakadi @@ -167,7 +162,7 @@ case class EventTypeStatistics(messagesPerMinute: Integer, readParallelism: Integer, writeParallelism: Integer) - /** +/** * A status corresponding to one individual Event's publishing attempt. * @param eid Eid of the corresponding item. Will be absent if missing on the incoming Event. * @param publishingStatus Indicator of the submission of the Event within a Batch. - SUBMITTED indicates successful submission, including commit on he underlying broker. - FAILED indicates the message submission was not possible and can be resubmitted if so desired. - ABORTED indicates that the submission of this item was not attempted any further due to a failure on another item in the batch. @@ -178,23 +173,21 @@ case class EventTypeStatistics(messagesPerMinute: Integer, case class BatchItemResponse( eid: Option[String], @JsonScalaEnumeration(classOf[BatchItemPublishingStatusType]) publishingStatus: BatchItemPublishingStatus.Value, - @JsonScalaEnumeration(classOf[BatchItemStepType]) step: Option[ - BatchItemStep.Value], + @JsonScalaEnumeration(classOf[BatchItemStepType]) step: Option[BatchItemStep.Value], detail: Option[String]) ///////////////////////////////// // ENUMS //////////////////////// ///////////////////////////////// - /** * Defines a rule for the resolution of incoming Events into partitions. Rules might require additional parameters; see the `doc` field of the existing rules for details. See `GET /registry/partition-strategies` for a list of available rules. */ case object PartitionStrategy extends Enumeration { type PartitionStrategy = Value - val HASH = Value("hash") + val HASH = Value("hash") val USER_DEFINED = Value("user_defined") - val RANDOM = Value("random") + val RANDOM = Value("random") } class PartitionStrategyType extends TypeReference[PartitionStrategy.type] @@ -206,7 +199,7 @@ case object EventEnrichmentStrategy extends Enumeration { val METADATA = Value("metadata_enrichment") } class EventEnrichmentStrategyType extends TypeReference[EventEnrichmentStrategy.type] - + /** * Identifier for the type of operation to executed on the entity.
          * C: Creation
          @@ -217,9 +210,9 @@ class EventEnrichmentStrategyType extends TypeReference[EventEnrichmentStrategy. */ case object DataOperation extends Enumeration { type DataOperation = Value - val CREATE = Value("C") - val UPDATE = Value("U") - val DELETE = Value("D") + val CREATE = Value("C") + val UPDATE = Value("U") + val DELETE = Value("D") val SNAPSHOT = Value("S") } @@ -232,8 +225,8 @@ class DataOperationType extends TypeReference[DataOperation.type] case object EventTypeCategory extends Enumeration { type EventTypeCategory = Value val UNDEFINED = Value("undefined") - val DATA = Value("data") - val BUSINESS = Value("business") + val DATA = Value("data") + val BUSINESS = Value("business") } class EventTypeCategoryType extends TypeReference[EventTypeCategory.type] @@ -249,12 +242,11 @@ class EventTypeCategoryType extends TypeReference[EventTypeCategory.type] case object BatchItemPublishingStatus extends Enumeration { type BatchItemPublishingStatus = Value val SUBMITTED = Value("submitted") - val FAILED = Value("failed") - val ABORTED = Value("aborted") + val FAILED = Value("failed") + val ABORTED = Value("aborted") } -class BatchItemPublishingStatusType - extends TypeReference[BatchItemPublishingStatus.type] +class BatchItemPublishingStatusType extends TypeReference[BatchItemPublishingStatus.type] /** * Indicator of the step in the pulbishing process this Event reached. @@ -267,11 +259,11 @@ class BatchItemPublishingStatusType */ case object BatchItemStep extends Enumeration { type BatchItemStep = Value - val NONE = Value("none") - val VALIDATING = Value("validating") - val ENRICHING = Value("enriching") + val NONE = Value("none") + val VALIDATING = Value("validating") + val ENRICHING = Value("enriching") val PARTITIONING = Value("partitioning") - val PUBLISHING = Value("publishing") + val PUBLISHING = Value("publishing") } class BatchItemStepType extends TypeReference[BatchItemStep.type] diff --git a/client/src/main/scala/org/zalando/nakadi/client/java/model/JavaJacksonJsonMarshaller.scala b/client/src/main/scala/org/zalando/nakadi/client/java/model/JavaJacksonJsonMarshaller.scala index d835961..6241f28 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/java/model/JavaJacksonJsonMarshaller.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/java/model/JavaJacksonJsonMarshaller.scala @@ -23,21 +23,22 @@ import com.fasterxml.jackson.databind.JavaType object JavaJacksonJsonMarshaller { val logger = LoggerFactory.getLogger(this.getClass) // All TypeReferences - def problemTR: TypeReference[Problem] = new TypeReference[Problem] {} - def metricsTR: TypeReference[Metrics] = new TypeReference[Metrics] {} + def problemTR: TypeReference[Problem] = new TypeReference[Problem] {} + def metricsTR: TypeReference[Metrics] = new TypeReference[Metrics] {} def partitionTR: TypeReference[Partition] = new TypeReference[Partition] {} - def cursorTR: TypeReference[Cursor] = new TypeReference[Cursor] {} + def cursorTR: TypeReference[Cursor] = new TypeReference[Cursor] {} def eventTypeSchemaTR: TypeReference[EventTypeSchema] = new TypeReference[EventTypeSchema] {} def eventTypeCategoryTR: TypeReference[EventTypeCategory] = - new TypeReference[EventTypeCategory] {} - def partitionResolutionStrategyTR: TypeReference[PartitionStrategy] = new TypeReference[PartitionStrategy] {} + new TypeReference[EventTypeCategory] {} + def partitionResolutionStrategyTR: TypeReference[PartitionStrategy] = new TypeReference[PartitionStrategy] {} def eventEnrichmentStrategyTR: TypeReference[EventEnrichmentStrategy] = new TypeReference[EventEnrichmentStrategy] {} - def dataChangeEventQualifierTR: TypeReference[DataChangeEventQualifier] = new TypeReference[DataChangeEventQualifier] {} + def dataChangeEventQualifierTR: TypeReference[DataChangeEventQualifier] = + new TypeReference[DataChangeEventQualifier] {} def eventTypeStatisticsTR: TypeReference[EventTypeStatistics] = new TypeReference[EventTypeStatistics] {} - def eventTypeTR: TypeReference[EventType] = new TypeReference[EventType] {} - def eventTR: TypeReference[Event] = new TypeReference[Event] {} - def eventStreamBatchTR: TypeReference[EventStreamBatch[_]] = new TypeReference[EventStreamBatch[_]] {} + def eventTypeTR: TypeReference[EventType] = new TypeReference[EventType] {} + def eventTR: TypeReference[Event] = new TypeReference[Event] {} + def eventStreamBatchTR: TypeReference[EventStreamBatch[_]] = new TypeReference[EventStreamBatch[_]] {} def eventMetadataTR: TypeReference[EventMetadata] = new TypeReference[EventMetadata] {} @@ -60,8 +61,7 @@ object JavaJacksonJsonMarshaller { def listOfDataChangeEventTR: TypeReference[java.util.List[DataChangeEvent[Any]]] = new TypeReference[java.util.List[DataChangeEvent[Any]]] {} - def optionalDeserializer[T]( - expectedType: TypeReference[T]): Deserializer[Option[T]] = + def optionalDeserializer[T](expectedType: TypeReference[T]): Deserializer[Option[T]] = new Deserializer[Option[T]] { def from(from: String): Option[T] = { defaultObjectMapper.readValue[Option[T]](from, expectedType) @@ -97,7 +97,7 @@ object JavaJacksonJsonMarshaller { beanOrClass: AnyRef, propertyName: String): Boolean = { logger.warn( - s"unknown property occurred in JSON representation: [beanOrClass=$beanOrClass, property=$propertyName]") + s"unknown property occurred in JSON representation: [beanOrClass=$beanOrClass, property=$propertyName]") true } }) diff --git a/client/src/main/scala/org/zalando/nakadi/client/scala/model/ScalaJacksonJsonMarshaller.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/model/ScalaJacksonJsonMarshaller.scala index 1cc58e3..cdb6599 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/scala/model/ScalaJacksonJsonMarshaller.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/scala/model/ScalaJacksonJsonMarshaller.scala @@ -27,20 +27,17 @@ object JacksonJsonMarshaller { // All TypeReferences implicit def problemTR: TypeReference[Problem] = new TypeReference[Problem] {} - implicit val metricsTR: TypeReference[Metrics] = - new TypeReference[Metrics] {} + implicit val metricsTR: TypeReference[Metrics] = new TypeReference[Metrics] {} implicit def partitionTR: TypeReference[Partition] = new TypeReference[Partition] {} implicit def cursorTR: TypeReference[Cursor] = new TypeReference[Cursor] {} implicit def eventTypeSchemaTR: TypeReference[EventTypeSchema] = new TypeReference[EventTypeSchema] {} - implicit def partitionResolutionStrategyTR: TypeReference[ - PartitionStrategy.Value] = new TypeReference[PartitionStrategy.Value] {} - implicit def eventEnrichmentStrategyTR: TypeReference[ - EventEnrichmentStrategy.Value] = + implicit def partitionResolutionStrategyTR: TypeReference[PartitionStrategy.Value] = + new TypeReference[PartitionStrategy.Value] {} + implicit def eventEnrichmentStrategyTR: TypeReference[EventEnrichmentStrategy.Value] = new TypeReference[EventEnrichmentStrategy.Value] {} - implicit def dataChangeEventQualifierTR: TypeReference[ - DataChangeEventQualifier] = + implicit def dataChangeEventQualifierTR: TypeReference[DataChangeEventQualifier] = new TypeReference[DataChangeEventQualifier] {} implicit def eventTypeStatisticsTR: TypeReference[EventTypeStatistics] = new TypeReference[EventTypeStatistics] {} @@ -60,19 +57,16 @@ object JacksonJsonMarshaller { new TypeReference[DataChangeEvent[Any]] {} //Lists - implicit def listOfPartitionStrategyTR: TypeReference[ - Seq[PartitionStrategy.Value]] = + implicit def listOfPartitionStrategyTR: TypeReference[Seq[PartitionStrategy.Value]] = new TypeReference[Seq[PartitionStrategy.Value]] {} - implicit def listOfEventEnrichmentStrategyTR: TypeReference[ - Seq[EventEnrichmentStrategy.Value]] = + implicit def listOfEventEnrichmentStrategyTR: TypeReference[Seq[EventEnrichmentStrategy.Value]] = new TypeReference[Seq[EventEnrichmentStrategy.Value]] {} implicit def listOfEventTypeTR: TypeReference[Seq[EventType]] = new TypeReference[Seq[EventType]] {} implicit def listOfPartitionTR: TypeReference[Seq[Partition]] = new TypeReference[Seq[Partition]] {} - implicit def optionalDeserializer[T]( - implicit expectedType: TypeReference[T]): Deserializer[Option[T]] = + implicit def optionalDeserializer[T](implicit expectedType: TypeReference[T]): Deserializer[Option[T]] = new Deserializer[Option[T]] { def from(from: String): Option[T] = { @@ -84,16 +78,14 @@ object JacksonJsonMarshaller { def to(from: T): String = defaultObjectMapper.writeValueAsString(from) } - implicit def deserializer[T]( - implicit expectedType: TypeReference[T]): Deserializer[T] = + implicit def deserializer[T](implicit expectedType: TypeReference[T]): Deserializer[T] = new Deserializer[T] { def from(from: String): T = defaultObjectMapper.readValue[T](from, expectedType) } lazy val defaultObjectMapper: ObjectMapper = new ObjectMapper() { - private val module = new OptionModule with MapModule with SeqModule - with IteratorModule + private val module = new OptionModule with MapModule with SeqModule with IteratorModule this.registerModule(module) @@ -101,7 +93,7 @@ object JacksonJsonMarshaller { .registerModule(new DefaultScalaModule) .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) .configure(DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT, true) - .configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY,true) + .configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true) .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) .setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE) .setSerializationInclusion(JsonInclude.Include.NON_EMPTY) diff --git a/project/Build.scala b/project/Build.scala index 5d6de32..604ef70 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -4,6 +4,7 @@ import com.typesafe.sbteclipse.plugin.EclipsePlugin.EclipseKeys import com.typesafe.sbteclipse.plugin.EclipsePlugin.EclipseCreateSrc import Dependencies._ import de.johoop.findbugs4sbt.FindBugs._ +import org.scalafmt.sbt.ScalaFmtPlugin.autoImport._ import de.johoop.findbugs4sbt.ReportType object NakadiClient extends Build { @@ -66,6 +67,7 @@ lazy val client = withDefaults( scalaVersion := "2.11.8", findbugsReportType := Some(ReportType.FancyHtml), findbugsReportPath := Some(target.value / "findbugs-report.html"), + scalafmtConfig := Some(baseDirectory.value / ".scalafmt"), publishTo := whereToPublishTo(isSnapshot.value), resolvers += Resolver.mavenLocal, resolvers += "Maven Central Server" at "http://repo1.maven.org/maven2", From 202fc843d95b7388247a55515a37199eee2daa27 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Fri, 8 Jul 2016 09:26:57 +0200 Subject: [PATCH 126/183] techjira:LAAS-60 Finished testing Scala Models. #54 --- .../nakadi/client/scala/ClientImpl.scala | 4 +- .../model/ScalaJacksonJsonMarshaller.scala | 20 ++- .../java/{ => model}/BusinessEventImpl.java | 2 +- .../JavaJacksonJsonMarshallerTest.java | 4 +- .../client/java/{ => model}/ModelFactory.java | 60 ++++----- .../client/java/{ => model}/SimpleEvent.java | 2 +- .../nakadi/client/scala/EnumModule.scala | 4 +- .../nakadi/client/scala/ScalaClientTest.scala | 4 +- .../scala/SerializerDeserializerTest.scala | 4 +- .../client/scala/model/ModelFactory.scala | 72 ++++++++++ .../ScalaJacksonJsonMarshallerTest.scala | 125 ++++++++++++++++++ .../scala/model/ScalaJacksonJsonModule.scala | 2 +- .../scala/ClientCreationExample.scala | 4 +- .../examples/scala/EventCreationExample.scala | 4 +- .../examples/scala/EventListenerExample.scala | 2 +- .../nakadi/client/scala/SimpleEventTest.scala | 4 +- project/Build.scala | 2 +- project/Dependencies.scala | 1 + 18 files changed, 254 insertions(+), 66 deletions(-) rename client/src/test/java/org/zalando/nakadi/client/java/{ => model}/BusinessEventImpl.java (94%) rename client/src/test/java/org/zalando/nakadi/client/java/{ => model}/JavaJacksonJsonMarshallerTest.java (99%) rename client/src/test/java/org/zalando/nakadi/client/java/{ => model}/ModelFactory.java (78%) rename client/src/test/java/org/zalando/nakadi/client/java/{ => model}/SimpleEvent.java (96%) create mode 100644 client/src/test/scala/org/zalando/nakadi/client/scala/model/ModelFactory.scala create mode 100644 client/src/test/scala/org/zalando/nakadi/client/scala/model/ScalaJacksonJsonMarshallerTest.scala diff --git a/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala index 514a1f1..c54da4d 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala @@ -16,7 +16,7 @@ import org.zalando.nakadi.client.scala.model.Event import org.zalando.nakadi.client.scala.model.EventEnrichmentStrategy import org.zalando.nakadi.client.scala.model.EventStreamBatch import org.zalando.nakadi.client.scala.model.EventType -import org.zalando.nakadi.client.scala.model.JacksonJsonMarshaller +import org.zalando.nakadi.client.scala.model.ScalaJacksonJsonMarshaller import org.zalando.nakadi.client.scala.model.Metrics import org.zalando.nakadi.client.scala.model.Partition import org.zalando.nakadi.client.scala.model.PartitionStrategy @@ -35,7 +35,7 @@ class ClientImpl(connection: Connection, charSet: String = "UTF-8") extends Client { import Uri._ - import JacksonJsonMarshaller._ + import ScalaJacksonJsonMarshaller._ import HttpFactory._ implicit val materializer = connection.materializer() diff --git a/client/src/main/scala/org/zalando/nakadi/client/scala/model/ScalaJacksonJsonMarshaller.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/model/ScalaJacksonJsonMarshaller.scala index cdb6599..b3eef0d 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/scala/model/ScalaJacksonJsonMarshaller.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/scala/model/ScalaJacksonJsonMarshaller.scala @@ -21,22 +21,18 @@ import com.fasterxml.jackson.module.scala.OptionModule import com.fasterxml.jackson.module.scala.SeqModule import com.fasterxml.jackson.module.scala.MapModule -object JacksonJsonMarshaller { +object ScalaJacksonJsonMarshaller { val logger = LoggerFactory.getLogger(this.getClass) // All TypeReferences - implicit def problemTR: TypeReference[Problem] = - new TypeReference[Problem] {} + implicit def problemTR: TypeReference[Problem] = new TypeReference[Problem] {} implicit val metricsTR: TypeReference[Metrics] = new TypeReference[Metrics] {} - implicit def partitionTR: TypeReference[Partition] = - new TypeReference[Partition] {} + implicit def partitionTR: TypeReference[Partition] = new TypeReference[Partition] {} implicit def cursorTR: TypeReference[Cursor] = new TypeReference[Cursor] {} - implicit def eventTypeSchemaTR: TypeReference[EventTypeSchema] = - new TypeReference[EventTypeSchema] {} - implicit def partitionResolutionStrategyTR: TypeReference[PartitionStrategy.Value] = - new TypeReference[PartitionStrategy.Value] {} - implicit def eventEnrichmentStrategyTR: TypeReference[EventEnrichmentStrategy.Value] = - new TypeReference[EventEnrichmentStrategy.Value] {} + implicit def eventTypeSchemaTR: TypeReference[EventTypeSchema] = new TypeReference[EventTypeSchema] {} + implicit def partitionResolutionStrategyTR: TypeReference[PartitionStrategy.Value] = new TypeReference[PartitionStrategy.Value] {} + implicit def eventEnrichmentStrategyTR: TypeReference[EventEnrichmentStrategy.Value] = new TypeReference[EventEnrichmentStrategy.Value] {} + implicit def eventTypeCategoryTR: TypeReference[EventTypeCategory.Value] = new TypeReference[EventTypeCategory.Value] {} implicit def dataChangeEventQualifierTR: TypeReference[DataChangeEventQualifier] = new TypeReference[DataChangeEventQualifier] {} implicit def eventTypeStatisticsTR: TypeReference[EventTypeStatistics] = @@ -104,7 +100,7 @@ object JacksonJsonMarshaller { beanOrClass: AnyRef, propertyName: String): Boolean = { logger.warn( - s"unknown property occurred in JSON representation: [beanOrClass=$beanOrClass, property=$propertyName]") + s"unknown property occurred in JSON representation: [beanOrClass=$beanOrClass, property=$propertyName]") true } }) diff --git a/client/src/test/java/org/zalando/nakadi/client/java/BusinessEventImpl.java b/client/src/test/java/org/zalando/nakadi/client/java/model/BusinessEventImpl.java similarity index 94% rename from client/src/test/java/org/zalando/nakadi/client/java/BusinessEventImpl.java rename to client/src/test/java/org/zalando/nakadi/client/java/model/BusinessEventImpl.java index aa4355a..c41dca9 100644 --- a/client/src/test/java/org/zalando/nakadi/client/java/BusinessEventImpl.java +++ b/client/src/test/java/org/zalando/nakadi/client/java/model/BusinessEventImpl.java @@ -1,4 +1,4 @@ -package org.zalando.nakadi.client.java; +package org.zalando.nakadi.client.java.model; import org.zalando.nakadi.client.java.model.BusinessEvent; import org.zalando.nakadi.client.java.model.EventMetadata; diff --git a/client/src/test/java/org/zalando/nakadi/client/java/JavaJacksonJsonMarshallerTest.java b/client/src/test/java/org/zalando/nakadi/client/java/model/JavaJacksonJsonMarshallerTest.java similarity index 99% rename from client/src/test/java/org/zalando/nakadi/client/java/JavaJacksonJsonMarshallerTest.java rename to client/src/test/java/org/zalando/nakadi/client/java/model/JavaJacksonJsonMarshallerTest.java index 83011ea..bee7b9a 100644 --- a/client/src/test/java/org/zalando/nakadi/client/java/JavaJacksonJsonMarshallerTest.java +++ b/client/src/test/java/org/zalando/nakadi/client/java/model/JavaJacksonJsonMarshallerTest.java @@ -1,4 +1,4 @@ -package org.zalando.nakadi.client.java; +package org.zalando.nakadi.client.java.model; import static org.junit.Assert.*; @@ -33,7 +33,7 @@ import com.google.common.collect.Lists; public class JavaJacksonJsonMarshallerTest { - Logger log = LoggerFactory.getLogger(this.getClass()); + private Logger log = LoggerFactory.getLogger(this.getClass()); private String toJson(T in) throws JsonProcessingException { log.info("ToJson - in {}", in.toString()); diff --git a/client/src/test/java/org/zalando/nakadi/client/java/ModelFactory.java b/client/src/test/java/org/zalando/nakadi/client/java/model/ModelFactory.java similarity index 78% rename from client/src/test/java/org/zalando/nakadi/client/java/ModelFactory.java rename to client/src/test/java/org/zalando/nakadi/client/java/model/ModelFactory.java index f6f820e..59bdcff 100644 --- a/client/src/test/java/org/zalando/nakadi/client/java/ModelFactory.java +++ b/client/src/test/java/org/zalando/nakadi/client/java/model/ModelFactory.java @@ -1,4 +1,4 @@ -package org.zalando.nakadi.client.java; +package org.zalando.nakadi.client.java.model; import java.util.HashMap; import java.util.List; @@ -9,6 +9,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; +import org.zalando.nakadi.client.java.ClientError; import org.zalando.nakadi.client.java.enumerator.BatchItemPublishingStatus; import org.zalando.nakadi.client.java.enumerator.BatchItemStep; import org.zalando.nakadi.client.java.enumerator.DataOperation; @@ -33,8 +34,6 @@ import com.google.common.collect.Lists; public final class ModelFactory { - private ModelFactory() { - } private static Random randomizer = new Random(); @@ -90,56 +89,53 @@ public static EventTypeStatistics newEventTypeStatistics() { } public static Metrics newMetrics() { - int depth= 5; - Map gauges=new HashMap(); - gauges.put("normalString",randomUUID()); - gauges.put("SimpleEvent",newSimpleEvent()); - gauges.put("List",randomListOfString(depth)); - gauges.put("List",randomListOfString(depth)); - gauges.put("MapOfMaps",randomMapOfString(depth)); - return new Metrics("version",gauges); - } - - public static List randomListOfString(int nrOfItems){ - return Stream.generate(()-> randomUUID()).limit(nrOfItems).collect(Collectors.toList()); - - } - + int depth = 5; + Map gauges = new HashMap(); + gauges.put("normalString", randomUUID()); + gauges.put("SimpleEvent", newSimpleEvent()); + gauges.put("List", randomListOfString(depth)); + gauges.put("List", randomListOfString(depth)); + gauges.put("MapOfMaps", randomMapOfString(depth)); + return new Metrics("version", gauges); + } + + public static List randomListOfString(int nrOfItems) { + return Stream.generate(() -> randomUUID()).limit(nrOfItems).collect(Collectors.toList()); + + } + /** - * A function that call itself to generate maps that contain other maps as values. + * A function that call itself to generate maps that contain other maps as values. + * * @param nrOfItems * @return */ - public static Map randomMapOfString(int nrOfItems){ - return Stream.generate(()-> randomUUID()).limit(nrOfItems).collect( - Collectors.toMap(i->"nrOfItems-"+nrOfItems+"--"+i,i->randomMapOfString(nrOfItems-1))); + public static Map randomMapOfString(int nrOfItems) { + return Stream.generate(() -> randomUUID()).limit(nrOfItems).collect(Collectors.toMap(i -> "nrOfItems-" + nrOfItems + "--" + i, i -> randomMapOfString(nrOfItems - 1))); } public static Partition newPartition() { - - return new Partition(randomUUID(),randomUUID(),randomUUID()); + + return new Partition(randomUUID(), randomUUID(), randomUUID()); } public static Problem newProblem() { - - + return new Problem(randomUUID(), randomUUID(), randomInt(), randomUUID(), randomUUID()); } public static EventType newEventType() { - List newEnrichmentStrategy=Lists.newArrayList(EventEnrichmentStrategy.METADATA); + List newEnrichmentStrategy = Lists.newArrayList(EventEnrichmentStrategy.METADATA); return new EventType(randomUUID(), randomUUID(), EventTypeCategory.BUSINESS, // - newEnrichmentStrategy, newPartitionStrategy(), newEventTypeSchema(), randomListOfString(12), randomListOfString(21), newEventTypeStatistics()); + newEnrichmentStrategy, newPartitionStrategy(), newEventTypeSchema(), randomListOfString(12), randomListOfString(21), newEventTypeStatistics()); } - - public static PartitionStrategy newPartitionStrategy() { return PartitionStrategy.values()[randomizer.nextInt(PartitionStrategy.values().length)]; } public static EventEnrichmentStrategy newEventEnrichmentStrategy() { - + return EventEnrichmentStrategy.values()[randomizer.nextInt(EventEnrichmentStrategy.values().length)]; } @@ -151,6 +147,4 @@ public static ClientError newClientError() { return new ClientError(randomUUID(), Optional.of(randomInt()), Optional.of(new IllegalStateException())); } - - } diff --git a/client/src/test/java/org/zalando/nakadi/client/java/SimpleEvent.java b/client/src/test/java/org/zalando/nakadi/client/java/model/SimpleEvent.java similarity index 96% rename from client/src/test/java/org/zalando/nakadi/client/java/SimpleEvent.java rename to client/src/test/java/org/zalando/nakadi/client/java/model/SimpleEvent.java index 22f20f4..dbd47ce 100644 --- a/client/src/test/java/org/zalando/nakadi/client/java/SimpleEvent.java +++ b/client/src/test/java/org/zalando/nakadi/client/java/model/SimpleEvent.java @@ -1,4 +1,4 @@ -package org.zalando.nakadi.client.java; +package org.zalando.nakadi.client.java.model; import org.zalando.nakadi.client.java.model.Event; diff --git a/client/src/test/scala/org/zalando/nakadi/client/scala/EnumModule.scala b/client/src/test/scala/org/zalando/nakadi/client/scala/EnumModule.scala index 9c5cd34..4b48a24 100644 --- a/client/src/test/scala/org/zalando/nakadi/client/scala/EnumModule.scala +++ b/client/src/test/scala/org/zalando/nakadi/client/scala/EnumModule.scala @@ -2,7 +2,7 @@ package org.zalando.nakadi.client.scala import scala.reflect._ import scala.reflect.runtime.universe._ -import org.zalando.nakadi.client.scala.model.JacksonJsonMarshaller +import org.zalando.nakadi.client.scala.model.ScalaJacksonJsonMarshaller import com.fasterxml.jackson.core.JsonGenerator import com.fasterxml.jackson.core.JsonParser import com.fasterxml.jackson.core.Version @@ -21,7 +21,7 @@ import org.zalando.nakadi.client.scala.model.BatchItemPublishingStatus import org.zalando.nakadi.client.scala.model.EventTypeCategory import org.zalando.nakadi.client.scala.model.SchemaType import com.fasterxml.jackson.module.scala.JsonScalaEnumeration -import org.zalando.nakadi.client.scala.model.JacksonJsonMarshaller +import org.zalando.nakadi.client.scala.model.ScalaJacksonJsonMarshaller import org.zalando.nakadi.client.scala.model.EventEnrichmentStrategy import org.zalando.nakadi.client.scala.model.EventEnrichmentStrategyType diff --git a/client/src/test/scala/org/zalando/nakadi/client/scala/ScalaClientTest.scala b/client/src/test/scala/org/zalando/nakadi/client/scala/ScalaClientTest.scala index 658e432..bc7b1a9 100644 --- a/client/src/test/scala/org/zalando/nakadi/client/scala/ScalaClientTest.scala +++ b/client/src/test/scala/org/zalando/nakadi/client/scala/ScalaClientTest.scala @@ -21,7 +21,7 @@ import akka.http.scaladsl.model.HttpProtocol import akka.http.scaladsl.model.HttpProtocols import akka.http.scaladsl.model.HttpEntity import akka.http.scaladsl.model.ContentTypes -import org.zalando.nakadi.client.scala.model.JacksonJsonMarshaller +import org.zalando.nakadi.client.scala.model.ScalaJacksonJsonMarshaller import org.zalando.nakadi.client.scala.model._ import org.zalando.nakadi.client.scala._ import org.zalando.nakadi.client.utils.Uri @@ -31,7 +31,7 @@ import org.mockito.ArgumentCaptor import org.zalando.nakadi.client.Deserializer class ScalaClientTest extends WordSpec with Matchers with MockitoSugar with BeforeAndAfter { - import JacksonJsonMarshaller._ + import ScalaJacksonJsonMarshaller._ import Uri._ private var connection: Connection = mock[Connection] private var subscriptionHandler: SubscriptionHandler = mock[SubscriptionHandler] diff --git a/client/src/test/scala/org/zalando/nakadi/client/scala/SerializerDeserializerTest.scala b/client/src/test/scala/org/zalando/nakadi/client/scala/SerializerDeserializerTest.scala index a9fb64e..4cf1785 100644 --- a/client/src/test/scala/org/zalando/nakadi/client/scala/SerializerDeserializerTest.scala +++ b/client/src/test/scala/org/zalando/nakadi/client/scala/SerializerDeserializerTest.scala @@ -4,7 +4,7 @@ import scala.concurrent.duration.DurationInt import org.scalatest.Matchers import org.scalatest.WordSpec import org.zalando.nakadi.client.scala.model._ -import org.zalando.nakadi.client.scala.model.JacksonJsonMarshaller +import org.zalando.nakadi.client.scala.model.ScalaJacksonJsonMarshaller import org.zalando.nakadi.client.utils.AkkaConfig import org.zalando.nakadi.client.utils.TestScalaEntity import com.fasterxml.jackson.core.`type`.TypeReference @@ -20,7 +20,7 @@ import org.zalando.nakadi.client.Serializer * unexpected results. */ class SerializerDeserializerTest extends WordSpec with Matchers with AkkaConfig { -import JacksonJsonMarshaller._ +import ScalaJacksonJsonMarshaller._ import TestScalaEntity._ "When an entity(scala object) is marshalled and unmarshalled it" should { diff --git a/client/src/test/scala/org/zalando/nakadi/client/scala/model/ModelFactory.scala b/client/src/test/scala/org/zalando/nakadi/client/scala/model/ModelFactory.scala new file mode 100644 index 0000000..dd8123c --- /dev/null +++ b/client/src/test/scala/org/zalando/nakadi/client/scala/model/ModelFactory.scala @@ -0,0 +1,72 @@ +package org.zalando.nakadi.client.scala.model + +import java.util.UUID +import com.fasterxml.jackson.annotation.JsonProperty +import scala.util.Random + +case class BusinessEventImpl(metadata: Option[EventMetadata]) extends BusinessEvent +case class SimpleEvent(id: String, name: String) extends Event + +object ModelFactory { + def randomUUID(): String = UUID.randomUUID().toString(); + def randomInt(): Integer = Random.nextInt() + def randomListOfStrings(nrOfItems: Int): Seq[String] = for (i <- 1 to nrOfItems) yield randomUUID() + def randomMapOfStrings(nrOfItems: Int): Map[String, Any] = (for (i <- 1 to nrOfItems) yield (s"itemNr $i" -> randomMapOfStrings(nrOfItems - 1))).toMap + + def newBatchItemResponse(): BatchItemResponse = { + val detail = Option("detail") + val step = Option(BatchItemStep.ENRICHING) + val publishingStatus = BatchItemPublishingStatus.SUBMITTED + val eid = Option(randomUUID()) + new BatchItemResponse(eid, publishingStatus, step, detail) + } + def newBusinessEvent() = new BusinessEventImpl(Option(newEventMetadata)) + def newEventMetadata(): EventMetadata = new EventMetadata(randomUUID(), Option("eventType"), "occurredAt", Option("receivedAt"), List("parentEids"), Option(randomUUID()), Option("partition")) + def newCursor(): Cursor = new Cursor("partition", "offset") + def newSimpleEvent(): SimpleEvent = SimpleEvent(randomUUID(), randomUUID()) + def newDataChangeEvent[T <: Event](in: T): DataChangeEvent[T] = DataChangeEvent(in, randomUUID(), DataOperation.CREATE, Option(newEventMetadata())) + def newEventStreamBatch[T <: Event](events: List[T], cursor: Cursor): EventStreamBatch[T] = EventStreamBatch(cursor, Option(events)) + def newEventTypeSchema(): EventTypeSchema = { + val schema = "{'propeties':{'key':'value'}}".replaceAll("'", "\""); + EventTypeSchema(SchemaType.JSON, schema); + } + def newEventTypeStatistics(): EventTypeStatistics = EventTypeStatistics(randomInt(), randomInt(), randomInt(), randomInt()) + def newMetrics(): Metrics = { + val depth = 5 + val gauges: Map[String, Any] = Map[String, Any]( + "normalString" -> randomUUID(), + "SimpleEvent" -> newSimpleEvent(), + "List" -> randomListOfStrings(depth), + "MapOfMaps" -> randomMapOfStrings(depth)) + Metrics(randomUUID(), gauges) + } + def newPartition(): Partition = Partition(randomUUID(), randomUUID(), randomUUID()) + def newProblem(): Problem = Problem(randomUUID(), randomUUID(), randomInt(), Option(randomUUID()), Option(randomUUID())) + + def newPartitionStrategy(): PartitionStrategy.Value = { + val values = PartitionStrategy.values.toArray + values(Random.nextInt(values.length)) + } + + def newEventEnrichmentStrategy(): EventEnrichmentStrategy.Value = { + val values = EventEnrichmentStrategy.values.toArray + values(Random.nextInt(values.length)) + } + + def newEventTypeCategory(): EventTypeCategory.Value = { + val values = EventTypeCategory.values.toArray + values(Random.nextInt(values.length)) + } + + def newEventType(): EventType = new EventType(randomUUID(), //name + randomUUID(), //owningApplication + newEventTypeCategory(), //category + List(newEventEnrichmentStrategy(), newEventEnrichmentStrategy()), //enrichmentStrategies + Option(newPartitionStrategy()), // + newEventTypeSchema(), // + List(randomUUID(), + randomUUID()), //dataKeyFields + List(randomUUID(), + randomUUID()), //dataKeyFields + Option(newEventTypeStatistics)) +} \ No newline at end of file diff --git a/client/src/test/scala/org/zalando/nakadi/client/scala/model/ScalaJacksonJsonMarshallerTest.scala b/client/src/test/scala/org/zalando/nakadi/client/scala/model/ScalaJacksonJsonMarshallerTest.scala new file mode 100644 index 0000000..c8196c6 --- /dev/null +++ b/client/src/test/scala/org/zalando/nakadi/client/scala/model/ScalaJacksonJsonMarshallerTest.scala @@ -0,0 +1,125 @@ +package org.zalando.nakadi.client.scala.model + +import org.scalatest.Matchers +import org.scalatest.WordSpec +import com.fasterxml.jackson.annotation.JsonProperty +import com.fasterxml.jackson.annotation.JsonIgnore +import com.fasterxml.jackson.databind.DeserializationFeature +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import com.fasterxml.jackson.core.`type`.TypeReference + +class ScalaJacksonJsonMarshallerTest extends WordSpec with Matchers { + private val log = LoggerFactory.getLogger(this.getClass()); + + val mapper = ScalaJacksonJsonMarshaller.defaultObjectMapper + + private def toJson[T](in: T): String = { + log.info("ToJson - in {}", in.toString()); + val result = ScalaJacksonJsonMarshaller.serializer.to(in); + log.info("ToJson - out {}", result); + result + } + + private def toObject[T](json: String, expectedType: TypeReference[T]): T = { + log.info("toObject - in {}", json); + val result = ScalaJacksonJsonMarshaller.deserializer(expectedType).from(json); + log.info("toObject - out {}", result.toString()); + result + } + + private def testMarshallingUnmarshalling[T](in: T, expectedType: TypeReference[T]): Unit = { + val json = toJson(in) + val out = toObject(json, expectedType) + in shouldBe out + } + + "Serialize/Deserialize BatchItemResponse" in { + val in = ModelFactory.newBatchItemResponse + val expectedType = ScalaJacksonJsonMarshaller.batchItemResponseTR + testMarshallingUnmarshalling(in, expectedType) + } + "Serialize/Deserialize BusinessEvent" in { + val in = ModelFactory.newBusinessEvent + val expectedType = new TypeReference[BusinessEventImpl] {} + testMarshallingUnmarshalling(in, expectedType) + + } + "Serialize/Deserialize Cursor" in { + val in = ModelFactory.newCursor + val expectedType = ScalaJacksonJsonMarshaller.cursorTR + testMarshallingUnmarshalling(in, expectedType) + + } + "Serialize/Deserialize DataChangeEvent" in { + val event = ModelFactory.newSimpleEvent + val in = ModelFactory.newDataChangeEvent(event) + val expectedType = new TypeReference[DataChangeEvent[SimpleEvent]] {} + testMarshallingUnmarshalling(in, expectedType) + + } + "Serialize/Deserialize EventMetadata" in { + val in = ModelFactory.newEventMetadata() + val expectedType = ScalaJacksonJsonMarshaller.eventMetadataTR + testMarshallingUnmarshalling(in, expectedType) + + } + "Serialize/Deserialize EventStreamBatch" in { + val events = List(ModelFactory.newSimpleEvent(), ModelFactory.newSimpleEvent(), ModelFactory.newSimpleEvent()) + val cursor = ModelFactory.newCursor() + val in = ModelFactory.newEventStreamBatch(events, cursor) + val expectedType = new TypeReference[EventStreamBatch[SimpleEvent]] {} + testMarshallingUnmarshalling(in, expectedType) + + } + "Serialize/Deserialize EventTypeSchema" in { + val in = ModelFactory.newEventTypeSchema() + val expectedType = ScalaJacksonJsonMarshaller.eventTypeSchemaTR + testMarshallingUnmarshalling(in, expectedType) + } + "Serialize/Deserialize EventTypeStatistics" in { + val in = ModelFactory.newEventTypeStatistics() + val expectedType = ScalaJacksonJsonMarshaller.eventTypeStatisticsTR + testMarshallingUnmarshalling(in, expectedType) + } + "Serialize/Deserialize Metrics" in { + val in = ModelFactory.newMetrics() + val expectedType = ScalaJacksonJsonMarshaller.metricsTR + val json = toJson(in) + val out = toObject(json, expectedType) + val jsonOut = toJson(out) + json shouldBe jsonOut + } + "Serialize/Deserialize Partition" in { + val in = ModelFactory.newPartition() + val expectedType = ScalaJacksonJsonMarshaller.partitionTR + testMarshallingUnmarshalling(in, expectedType) + } + "Serialize/Deserialize Problem" in { + val in = ModelFactory.newProblem() + val expectedType = ScalaJacksonJsonMarshaller.problemTR + testMarshallingUnmarshalling(in, expectedType) + } + "Serialize/Deserialize PartitionStrategy" in { + val in = ModelFactory.newPartitionStrategy() + val expectedType = ScalaJacksonJsonMarshaller.partitionResolutionStrategyTR + testMarshallingUnmarshalling(in, expectedType) + } + "Serialize/Deserialize EventEnrichmentStrategy" in { + val in = ModelFactory.newEventEnrichmentStrategy() + val expectedType = ScalaJacksonJsonMarshaller.eventEnrichmentStrategyTR + testMarshallingUnmarshalling(in, expectedType) + } + "Serialize/Deserialize EventTypeCategory" in { + val in = ModelFactory.newEventTypeCategory() + val expectedType = ScalaJacksonJsonMarshaller.eventTypeCategoryTR + testMarshallingUnmarshalling(in, expectedType) + } + "Serialize/Deserialize EventType" in { + val in = ModelFactory.newEventType() + val expectedType = ScalaJacksonJsonMarshaller.eventTypeTR + testMarshallingUnmarshalling(in, expectedType) + } + + +} \ No newline at end of file diff --git a/client/src/test/scala/org/zalando/nakadi/client/scala/model/ScalaJacksonJsonModule.scala b/client/src/test/scala/org/zalando/nakadi/client/scala/model/ScalaJacksonJsonModule.scala index d3ca937..9dd6a2c 100644 --- a/client/src/test/scala/org/zalando/nakadi/client/scala/model/ScalaJacksonJsonModule.scala +++ b/client/src/test/scala/org/zalando/nakadi/client/scala/model/ScalaJacksonJsonModule.scala @@ -11,7 +11,7 @@ case class Example private (enrichment_strategies: Seq[String]) class ScalaJacksonJsonModuleTest extends WordSpec with Matchers { val emptyJsonList = """{"enrichment_strategies": []}""" val emptyJson = """{}""" - val mapper = JacksonJsonMarshaller.defaultObjectMapper + val mapper = ScalaJacksonJsonMarshaller.defaultObjectMapper "Deserialize from empty array to Nil " in { val actual = mapper.readValue(emptyJsonList, classOf[Example]) diff --git a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/ClientCreationExample.scala b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/ClientCreationExample.scala index 0c4afeb..d858b76 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/ClientCreationExample.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/ClientCreationExample.scala @@ -1,12 +1,12 @@ package org.zalando.nakadi.client.scala -import org.zalando.nakadi.client.scala.model.JacksonJsonMarshaller +import org.zalando.nakadi.client.scala.model.ScalaJacksonJsonMarshaller import scala.concurrent.Await import scala.concurrent.duration.DurationInt import org.zalando.nakadi.client.utils.ClientBuilder object ClientCreationExample extends App { - import JacksonJsonMarshaller._ + import ScalaJacksonJsonMarshaller._ val a = ClientBuilder() .withHost("nakadi-sandbox.aruha-test.zalan.do") .withSecuredConnection(true) //s diff --git a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala index e307368..05ef71e 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventCreationExample.scala @@ -6,7 +6,7 @@ import org.zalando.nakadi.client.scala.model.Event import org.zalando.nakadi.client.scala.model.EventType import org.zalando.nakadi.client.scala.model.EventTypeCategory import org.zalando.nakadi.client.scala.model.EventTypeSchema -import org.zalando.nakadi.client.scala.model.JacksonJsonMarshaller +import org.zalando.nakadi.client.scala.model.ScalaJacksonJsonMarshaller import org.zalando.nakadi.client.scala.model.PartitionStrategy import org.zalando.nakadi.client.scala.model.SchemaType import org.zalando.nakadi.client.utils.ClientBuilder @@ -65,7 +65,7 @@ object EventCreationExample extends App { None) //You need to import the default Serializer if you don't sepecify your own! - import JacksonJsonMarshaller._ + import ScalaJacksonJsonMarshaller._ client.createEventType(eventType) Thread.sleep(3000) diff --git a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala index e8c4d38..f960500 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/examples/scala/EventListenerExample.scala @@ -81,7 +81,7 @@ object EventListenerExample extends App { */ implicit def typeRef: TypeReference[EventStreamBatch[MeetingsEvent]] = new TypeReference[EventStreamBatch[MeetingsEvent]] {} - import org.zalando.nakadi.client.scala.model.JacksonJsonMarshaller._ + import org.zalando.nakadi.client.scala.model.ScalaJacksonJsonMarshaller._ // val eventTypeName = "Event-example-with-0-messages" val eventTypeName = "Example-2000" diff --git a/it/src/test/scala/org/zalando/nakadi/client/scala/SimpleEventTest.scala b/it/src/test/scala/org/zalando/nakadi/client/scala/SimpleEventTest.scala index b82e32e..1a680f5 100644 --- a/it/src/test/scala/org/zalando/nakadi/client/scala/SimpleEventTest.scala +++ b/it/src/test/scala/org/zalando/nakadi/client/scala/SimpleEventTest.scala @@ -3,7 +3,7 @@ package org.zalando.nakadi.client.scala import org.scalatest.Matchers import org.scalatest.WordSpec import org.zalando.nakadi.client.scala.model.Cursor -import org.zalando.nakadi.client.scala.model.JacksonJsonMarshaller +import org.zalando.nakadi.client.scala.model.ScalaJacksonJsonMarshaller import org.zalando.nakadi.client.scala.model.PartitionStrategyType import org.zalando.nakadi.client.scala.test.factory.EventIntegrationHelper import org.zalando.nakadi.client.scala.test.factory.events.MySimpleEvent @@ -16,7 +16,7 @@ class SimpleEventTest extends WordSpec with Matchers with BeforeAndAfterAll { import org.scalatest.Matchers._ import ClientFactory._ - import JacksonJsonMarshaller._ + import ScalaJacksonJsonMarshaller._ import MySimpleEvent._ val client = ClientFactory.getScalaClient() diff --git a/project/Build.scala b/project/Build.scala index 604ef70..5b90f65 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -39,7 +39,7 @@ val defaultOptions= Seq( lazy val root = project.in(file(".")) .settings(publishTo := whereToPublishTo(isSnapshot.value)) - .aggregate(api, client) + .aggregate(api, client, it) lazy val api = withDefaults( "nakadi-klients-api", diff --git a/project/Dependencies.scala b/project/Dependencies.scala index c4026b7..6b5f129 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -24,6 +24,7 @@ object Dependencies { "com.typesafe.akka" %% "akka-actor" % akkaVersion, "com.typesafe.akka" %% "akka-http-experimental" % akkaVersion, "com.typesafe.akka" %% "akka-stream" % akkaVersion, + "org.slf4j" % "slf4j-api" % "1.7.21" "com.typesafe.akka" % "akka-slf4j_2.11" % "2.4.7", "com.typesafe.akka" %% "akka-testkit" % akkaVersion % "test", "org.scalatest" %% "scalatest" % scalaTestVersion % "test", From 3ef3baf1decac041bb41099ee6aadab0d24b2882 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Fri, 8 Jul 2016 09:47:05 +0200 Subject: [PATCH 127/183] techjira:LAAS-60 Finished testing Scala Models. #54 --- project/Build.scala | 2 +- project/Dependencies.scala | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/project/Build.scala b/project/Build.scala index 5b90f65..604ef70 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -39,7 +39,7 @@ val defaultOptions= Seq( lazy val root = project.in(file(".")) .settings(publishTo := whereToPublishTo(isSnapshot.value)) - .aggregate(api, client, it) + .aggregate(api, client) lazy val api = withDefaults( "nakadi-klients-api", diff --git a/project/Dependencies.scala b/project/Dependencies.scala index 6b5f129..4d68118 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -24,7 +24,7 @@ object Dependencies { "com.typesafe.akka" %% "akka-actor" % akkaVersion, "com.typesafe.akka" %% "akka-http-experimental" % akkaVersion, "com.typesafe.akka" %% "akka-stream" % akkaVersion, - "org.slf4j" % "slf4j-api" % "1.7.21" + "org.slf4j" % "slf4j-api" % "1.7.21", "com.typesafe.akka" % "akka-slf4j_2.11" % "2.4.7", "com.typesafe.akka" %% "akka-testkit" % akkaVersion % "test", "org.scalatest" %% "scalatest" % scalaTestVersion % "test", From a150094c5698caa24161facdd1daab7725270488 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Fri, 8 Jul 2016 09:56:39 +0200 Subject: [PATCH 128/183] techjira:LAAS-60 Formatting with scalafmt --- .scalafmt | 2 +- .../zalando/nakadi/client/scala/Client.scala | 49 ++---- build | 50 ------ .../nakadi/client/actor/ConsumingActor.scala | 26 +-- .../client/actor/SubscriptionHolder.scala | 17 +- .../client/actor/SupervisingActor.scala | 97 ++++------- .../client/handler/SubscriptionHandler.scala | 55 ++---- .../client/java/JavaClientHandler.scala | 107 +++++------- .../model/JavaJacksonJsonMarshaller.scala | 33 ++-- .../nakadi/client/scala/ClientImpl.scala | 158 ++++++------------ .../nakadi/client/scala/Connection.scala | 98 ++++------- .../nakadi/client/scala/HttpFactory.scala | 55 +++--- .../nakadi/client/scala/MessageHandler.scala | 50 +++--- .../model/ScalaJacksonJsonMarshaller.scala | 34 ++-- .../nakadi/client/utils/ClientBuilder.scala | 55 ++---- .../client/utils/FutureConversions.scala | 31 ++-- .../client/utils/GeneralConversions.scala | 12 +- .../nakadi/client/utils/ModelConverter.scala | 63 ++++--- .../org/zalando/nakadi/client/utils/Uri.scala | 17 +- project/Build.scala | 2 +- 20 files changed, 365 insertions(+), 646 deletions(-) delete mode 100644 build diff --git a/.scalafmt b/.scalafmt index 7d1ad18..c726670 100644 --- a/.scalafmt +++ b/.scalafmt @@ -1,3 +1,3 @@ # Example .scalafmt, comments are supported. --style defaultWithAlign # For pretty alignment. ---maxColumn 60 # For my wide 30" display. +--maxColumn 120 # For my wide 30" display. diff --git a/api/src/main/scala/org/zalando/nakadi/client/scala/Client.scala b/api/src/main/scala/org/zalando/nakadi/client/scala/Client.scala index d6f2aef..d1c15db 100644 --- a/api/src/main/scala/org/zalando/nakadi/client/scala/Client.scala +++ b/api/src/main/scala/org/zalando/nakadi/client/scala/Client.scala @@ -7,9 +7,7 @@ import org.zalando.nakadi.client.scala.model._ import org.zalando.nakadi.client._ import com.fasterxml.jackson.core.`type`.TypeReference -case class ClientError(msg: String, - status: Option[Integer] = None, - exception: Option[Throwable] = None) +case class ClientError(msg: String, status: Option[Integer] = None, exception: Option[Throwable] = None) trait Client { @@ -51,8 +49,7 @@ trait Client { * }}} * @param eventTypeName - Name of the EventType */ - def getEventType( - eventTypeName: String): Future[Either[ClientError, Option[EventType]]] + def getEventType(eventTypeName: String): Future[Either[ClientError, Option[EventType]]] /** * Updates the EventType identified by its name. @@ -62,8 +59,7 @@ trait Client { * @param eventTypeName - Name of the EventType * @param event - Event to update */ - def updateEventType(eventTypeName: String, - eventType: EventType): Future[Option[ClientError]] + def updateEventType(eventTypeName: String, eventType: EventType): Future[Option[ClientError]] /** * Deletes an EventType identified by its name. @@ -84,10 +80,9 @@ trait Client { * @param eventTypeName - Name of the EventType * @param event - Event to publish */ - def publishEvents[T <: Event]( - eventTypeName: String, - events: Seq[T], - ser: Serializer[Seq[T]]): Future[Option[ClientError]] + def publishEvents[T <: Event](eventTypeName: String, + events: Seq[T], + ser: Serializer[Seq[T]]): Future[Option[ClientError]] /** * Publishes multiple Events for the given EventType. @@ -97,8 +92,7 @@ trait Client { * @param eventTypeName - Name of the EventType * @param event - Event to publish */ - def publishEvents[T <: Event](eventTypeName: String, - events: Seq[T]): Future[Option[ClientError]] + def publishEvents[T <: Event](eventTypeName: String, events: Seq[T]): Future[Option[ClientError]] /** * List the partitions tola the given EventType. @@ -107,8 +101,7 @@ trait Client { * }}} * @param eventTypeName - Name of the EventType */ - def getPartitions(eventTypeName: String) - : Future[Either[ClientError, Option[Seq[Partition]]]] + def getPartitions(eventTypeName: String): Future[Either[ClientError, Option[Seq[Partition]]]] /** * Returns all of the enrichment strategies supported by this installation of Nakadi. @@ -116,8 +109,7 @@ trait Client { * curl --request GET /registry/enrichment-strategies * }}} */ - def getEnrichmentStrategies() - : Future[Either[ClientError, Option[Seq[EventEnrichmentStrategy.Value]]]] + def getEnrichmentStrategies(): Future[Either[ClientError, Option[Seq[EventEnrichmentStrategy.Value]]]] /** * Returns all of the partitioning strategies supported by this installation of Nakadi. @@ -125,8 +117,7 @@ trait Client { * curl --request GET /registry/partitioning-strategies * }}} */ - def getPartitioningStrategies() - : Future[Either[ClientError, Option[Seq[PartitionStrategy.Value]]]] + def getPartitioningStrategies(): Future[Either[ClientError, Option[Seq[PartitionStrategy.Value]]]] /** * Shuts down the communication system of the client @@ -141,9 +132,7 @@ trait Client { * @param listener - Listener to pass the event to when it is received. * @param des - Json Marshaller(implicit) to deserialize the event to Json. */ - def subscribe[T <: Event](eventTypeName: String, - parameters: StreamParameters, - listener: Listener[T])( + def subscribe[T <: Event](eventTypeName: String, parameters: StreamParameters, listener: Listener[T])( implicit des: Deserializer[EventStreamBatch[T]]): Option[ClientError] /** @@ -154,11 +143,10 @@ trait Client { * @param listener - Listener to pass the event to when it is received. * @param typeRef - TypeReference/Helper for using with the Jackson-Objectmapper to deserializing the event to json. */ - def subscribe[T <: Event]( - eventTypeName: String, - parameters: StreamParameters, - listener: Listener[T], - typeRef: TypeReference[EventStreamBatch[T]]): Option[ClientError] + def subscribe[T <: Event](eventTypeName: String, + parameters: StreamParameters, + listener: Listener[T], + typeRef: TypeReference[EventStreamBatch[T]]): Option[ClientError] /** * Removes the subscription of a listener, to stop streaming events from a partition. @@ -167,9 +155,8 @@ trait Client { * @param partition The partition assigned to this listener. * @param listener - Listener to unsubscribe from the streaming events. */ - def unsubscribe[T <: Event]( - eventTypeName: String, - partition: Option[String], - listener: Listener[T]): Future[Option[ClientError]] + def unsubscribe[T <: Event](eventTypeName: String, + partition: Option[String], + listener: Listener[T]): Future[Option[ClientError]] } diff --git a/build b/build deleted file mode 100644 index 6d812e2..0000000 --- a/build +++ /dev/null @@ -1,50 +0,0 @@ - -name := "nakadi-klients" - -organization := "org.zalando.nakadi.client" - -scalaVersion := "2.11.6" - -lazy val root = (project in file(".")).enablePlugins(GitVersioning) - -crossPaths := false - -scalacOptions ++= Seq( - "-deprecation", // Emit warning and location for usages of deprecated APIs. - "-feature", // Emit warning and location for usages of features that should be imported explicitly. - "-unchecked", // Enable additional warnings where generated code depends on assumptions. - "-Xfatal-warnings", // Fail the compilation if there are any warnings. - "-Xlint", // Enable recommended additional warnings. - "-Ywarn-adapted-args", // Warn if an argument list is modified to match the receiver. - "-Ywarn-dead-code", // Warn when dead code is identified. - "-Ywarn-inaccessible", // Warn about inaccessible types in method signatures. - "-Ywarn-nullary-override", // Warn when non-nullary overrides nullary, e.g. def foo() over def foo. - "-Ywarn-numeric-widen" // Warn when numerics are widened. -) - -resolvers += Resolver.mavenLocal -resolvers += "Maven Central Server" at "http://repo1.maven.org/maven2" - -libraryDependencies ++= { - val akkaVersion = "2.4.2" - Seq( - "com.typesafe.akka" %% "akka-actor" % akkaVersion, - "com.google.guava" % "guava" % "19.0", - "com.typesafe.scala-logging" %% "scala-logging" % "3.1.0", - "com.fasterxml.jackson.core" % "jackson-core" % "2.7.0", - "com.fasterxml.jackson.module" %% "jackson-module-scala" % "2.6.3", - "ch.qos.logback" % "logback-classic" % "1.1.3", - "com.typesafe.akka" %% "akka-http-experimental" % akkaVersion withSources() withJavadoc(), - "com.typesafe" % "config" % "1.3.0", // good to mention to fix the version (though Akka brings it in as a transitive dependency) - "com.typesafe.akka" %% "akka-cluster-metrics" % akkaVersion withSources() withJavadoc(), - "com.typesafe.akka" %% "akka-testkit" % akkaVersion % "test", - "org.scalatest" %% "scalatest" % "2.2.6" % "test", - "io.undertow" % "undertow-core" % "1.2.12.Final" % "test", - "io.undertow" % "undertow-servlet" % "1.2.12.Final" % "test", - "org.apache.commons" % "commons-io" % "1.3.2" % "test", - "com.google.code.findbugs" % "jsr305" % "1.3.9" % "test", - "junit" % "junit" % "4.12" % "test" - ) -} - -git.baseVersion := "0.0.0" diff --git a/client/src/main/scala/org/zalando/nakadi/client/actor/ConsumingActor.scala b/client/src/main/scala/org/zalando/nakadi/client/actor/ConsumingActor.scala index 91ceecb..8322905 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/actor/ConsumingActor.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/actor/ConsumingActor.scala @@ -52,34 +52,22 @@ class ConsumingActor(subscription: SubscriptionKey, handler: EventHandler) override def receive: Receive = { case OnNext(msg: ByteString) => val message = msg.utf8String - log.info("Event - prevCursor [{}] - [{}] - msg [{}]", - lastCursor, - subscription, - message) + log.info("Event - prevCursor [{}] - [{}] - msg [{}]", lastCursor, subscription, message) handler.handleOnReceive(subscription.toString(), message) match { case Right(cursor) => lastCursor = Some(cursor) context.parent ! OffsetMsg(cursor, subscription) - case Left(error) => log.error(error.error.getMessage) + case Left(error) => + log.error(error.error.getMessage) } case OnError(err: Throwable) => - log.error("onError - cursor [{}] - [{}] - error [{}]", - lastCursor, - subscription, - err.getMessage) + log.error("onError - cursor [{}] - [{}] - error [{}]", lastCursor, subscription, err.getMessage) context.stop(self) case OnComplete => - log.info("onComplete - connection closed by server - cursor [{}] - [{}]", - lastCursor, - subscription) - context.parent ! UnsubscribeMsg(subscription.eventTypeName, - subscription.partition, - handler.id()) + log.info("onComplete - connection closed by server - cursor [{}] - [{}]", lastCursor, subscription) + context.parent ! UnsubscribeMsg(subscription.eventTypeName, subscription.partition, handler.id()) case Terminated => - log.info( - "Received Terminated msg - subscription [{}] with listener-id [{}] ", - subscription, - handler.id()) + log.info("Received Terminated msg - subscription [{}] with listener-id [{}] ", subscription, handler.id()) context.stop(self) case a => log.error("Could not handle message: [{}]", a) diff --git a/client/src/main/scala/org/zalando/nakadi/client/actor/SubscriptionHolder.scala b/client/src/main/scala/org/zalando/nakadi/client/actor/SubscriptionHolder.scala index 1c8b155..5856fce 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/actor/SubscriptionHolder.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/actor/SubscriptionHolder.scala @@ -25,8 +25,8 @@ class SubscriptionHolderImpl extends SubscriptionHolder { import SupervisingActor._ import Preconditions._ private var subscriptions: Map[SubscriptionKey, SubscriptionEntry] = Map() //EventTypeName+Partition - private var cursors: Map[SubscriptionKey, Option[Cursor]] = Map() - private var actors: Map[String, SubscriptionKey] = Map() + private var cursors: Map[SubscriptionKey, Option[Cursor]] = Map() + private var actors: Map[String, SubscriptionKey] = Map() private val logger = LoggerFactory.getLogger(this.getClass) def addCursor(key: SubscriptionKey, cursor: Option[Cursor]): Unit = { @@ -57,7 +57,8 @@ class SubscriptionHolderImpl extends SubscriptionHolder { private def removeCursor(key: SubscriptionKey): Unit = cursors.get(key) match { - case None => logger.warn(s"Cursor subscription for $key not found!") + case None => + logger.warn(s"Cursor subscription for $key not found!") case Some(cursor) => cursors = cursors - (key) logger.info(s"Removed cursor [$cursor] with subscription [$key]!") @@ -66,12 +67,10 @@ class SubscriptionHolderImpl extends SubscriptionHolder { private def removeSubscriptionKey(actor: ActorRef): Unit = actors.get(actor.path.toString()) match { case None => - logger.warn( - s"SubscriptionKey for actor [${actor.path.toString()}] not found!") + logger.warn(s"SubscriptionKey for actor [${actor.path.toString()}] not found!") case Some(key) => actors = actors - actor.path.toString() - logger.info( - s"Removed subscriptionKey [$key] for actor [${actor.path.toString()}]!") + logger.info(s"Removed subscriptionKey [$key] for actor [${actor.path.toString()}]!") } def entry(key: SubscriptionKey): Option[SubscriptionEntry] = { @@ -90,9 +89,7 @@ class SubscriptionHolderImpl extends SubscriptionHolder { } def cursorByActor(actor: ActorRef): Option[Cursor] = { - actors - .get(actor.path.toString()) - .flatMap(x => cursors.get(x).flatMap(a => a)) + actors.get(actor.path.toString()).flatMap(x => cursors.get(x).flatMap(a => a)) } } diff --git a/client/src/main/scala/org/zalando/nakadi/client/actor/SupervisingActor.scala b/client/src/main/scala/org/zalando/nakadi/client/actor/SupervisingActor.scala index 5c5843a..b49baab 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/actor/SupervisingActor.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/actor/SupervisingActor.scala @@ -25,29 +25,24 @@ import akka.stream.actor.ActorSubscriber import akka.util.ByteString object SupervisingActor { - case class SubscribeMsg(eventTypeName: String, - endpoint: String, - cursor: Option[Cursor], - handler: EventHandler) { + case class SubscribeMsg(eventTypeName: String, endpoint: String, cursor: Option[Cursor], handler: EventHandler) { override def toString(): String = s"SubscriptionKey(eventTypeName: $eventTypeName - endpoint: $endpoint - cursor: $cursor - listener: ${handler.id})" } - case class UnsubscribeMsg(eventTypeName: String, - partition: Option[String], - eventHandlerId: String) + case class UnsubscribeMsg(eventTypeName: String, partition: Option[String], eventHandlerId: String) case class OffsetMsg(cursor: Cursor, subKey: SubscriptionKey) case class SubscriptionKey(eventTypeName: String, partition: Option[String]) { override def toString(): String = partition match { case Some(p) => s"SubscriptionKey(eventTypeName:$eventTypeName - Partition:$p)"; - case None => s"SubscriptionKey(eventTypeName:$eventTypeName)"; + case None => + s"SubscriptionKey(eventTypeName:$eventTypeName)"; } } case class SubscriptionEntry(subuscription: SubscribeMsg, actor: ActorRef) } -class SupervisingActor(val connection: Connection, - val subscriptionHandler: SubscriptionHandler) +class SupervisingActor(val connection: Connection, val subscriptionHandler: SubscriptionHandler) extends Actor with ActorLogging { import SupervisingActor._ @@ -56,10 +51,10 @@ class SupervisingActor(val connection: Connection, override val supervisorStrategy: SupervisorStrategy = { def defaultDecider: Decider = { case _: ActorInitializationException ⇒ Stop - case _: ActorKilledException ⇒ Stop - case _: IllegalStateException ⇒ Stop - case _: Exception ⇒ Stop - case _: Throwable ⇒ Stop + case _: ActorKilledException ⇒ Stop + case _: IllegalStateException ⇒ Stop + case _: Exception ⇒ Stop + case _: Throwable ⇒ Stop } OneForOneStrategy()(defaultDecider) } @@ -72,44 +67,31 @@ class SupervisingActor(val connection: Connection, val before = subscriptions.size subscribe(subscrition) val after = subscriptions.size - log.info( - s"SubscribeMsg - nr of subscriptions before [$before] - after [$after]") + log.info(s"SubscribeMsg - nr of subscriptions before [$before] - after [$after]") case unsubscription: UnsubscribeMsg => val before = subscriptions.size unsubscribe(unsubscription) val after = subscriptions.size - log.info( - s"UnsubscribeMsg - nr of subscriptions before [$before] - after [$after]") + log.info(s"UnsubscribeMsg - nr of subscriptions before [$before] - after [$after]") case Terminated(terminatedActor) => log.info(s"Actor [{}] terminated", terminatedActor.path.name) subscriptions.entryByActor(terminatedActor) match { case Some( - SubscriptionEntry(SubscribeMsg(eventTypeName, - endpoint, - Some(Cursor(partition, offset)), - handler), + SubscriptionEntry(SubscribeMsg(eventTypeName, endpoint, Some(Cursor(partition, offset)), handler), actor: ActorRef)) => - val cursor = subscriptions.cursorByActor(terminatedActor) - val unsubscription = - UnsubscribeMsg(eventTypeName, Option(partition), handler.id()) + val cursor = subscriptions.cursorByActor(terminatedActor) + val unsubscription = UnsubscribeMsg(eventTypeName, Option(partition), handler.id()) unsubscribe(unsubscription) - val newSubscription = - SubscribeMsg(eventTypeName, endpoint, cursor, handler) + val newSubscription = SubscribeMsg(eventTypeName, endpoint, cursor, handler) subscribe(newSubscription) - case Some( - SubscriptionEntry( - SubscribeMsg(eventTypeName, endpoint, None, handler), - actor: ActorRef)) => - val cursor = subscriptions.cursorByActor(terminatedActor) - val unsubscription = - UnsubscribeMsg(eventTypeName, None, handler.id()) + case Some(SubscriptionEntry(SubscribeMsg(eventTypeName, endpoint, None, handler), actor: ActorRef)) => + val cursor = subscriptions.cursorByActor(terminatedActor) + val unsubscription = UnsubscribeMsg(eventTypeName, None, handler.id()) unsubscribe(unsubscription) - val newSubscription = - SubscribeMsg(eventTypeName, endpoint, cursor, handler) + val newSubscription = SubscribeMsg(eventTypeName, endpoint, cursor, handler) subscribe(newSubscription) case None => - log.warning("Did not find any SubscriptionKey for [{}]", - terminatedActor.path.name) + log.warning("Did not find any SubscriptionKey for [{}]", terminatedActor.path.name) case e => log.error("Received unexpected message! [{}]", e) } @@ -117,14 +99,12 @@ class SupervisingActor(val connection: Connection, def subscribe(subscribe: SubscribeMsg) = { subscriptionCounter += 1 - val SubscribeMsg(eventTypeName, endpoint, optCursor, eventHandler) = - subscribe - log.info( - "Subscription nr [{}] - cursor [{}] - eventType [{}] - listener [{}]", - subscriptionCounter, - optCursor, - eventTypeName, - eventHandler.id()) + val SubscribeMsg(eventTypeName, endpoint, optCursor, eventHandler) = subscribe + log.info("Subscription nr [{}] - cursor [{}] - eventType [{}] - listener [{}]", + subscriptionCounter, + optCursor, + eventTypeName, + eventHandler.id()) val subscriptionKey: SubscriptionKey = optCursor match { case Some(Cursor(partition, _)) => @@ -133,37 +113,28 @@ class SupervisingActor(val connection: Connection, } //Create the Consumer - val consumingActor = context.actorOf( - Props(classOf[ConsumingActor], subscriptionKey, eventHandler), - "ConsumingActor-" + subscriptionCounter) + val consumingActor = context + .actorOf(Props(classOf[ConsumingActor], subscriptionKey, eventHandler), "ConsumingActor-" + subscriptionCounter) context.watch(consumingActor) - val subEntry: SubscriptionEntry = - SubscriptionEntry(subscribe, consumingActor) + val subEntry: SubscriptionEntry = SubscriptionEntry(subscribe, consumingActor) // Notify listener it is subscribed eventHandler.handleOnSubscribed(endpoint, optCursor) //Create the pipeline - subscriptionHandler - .createPipeline(optCursor, consumingActor, endpoint, eventHandler) + subscriptionHandler.createPipeline(optCursor, consumingActor, endpoint, eventHandler) subscriptions.add(subscriptionKey, consumingActor, subEntry) subscriptions.addCursor(subscriptionKey, optCursor) } def unsubscribe(unsubscription: UnsubscribeMsg): Unit = { - val UnsubscribeMsg(eventTypeName, partition, eventHandlerId) = - unsubscription - val key: SubscriptionKey = SubscriptionKey(eventTypeName, partition) + val UnsubscribeMsg(eventTypeName, partition, eventHandlerId) = unsubscription + val key: SubscriptionKey = SubscriptionKey(eventTypeName, partition) subscriptions.entry(key) match { - case Some( - SubscriptionEntry( - SubscribeMsg(eventTypeName, endpoint, cursor, handler), - actor: ActorRef)) => - log.info("Unsubscribing Listener : [{}] from actor: [{}]", - handler.id(), - actor.path.name) + case Some(SubscriptionEntry(SubscribeMsg(eventTypeName, endpoint, cursor, handler), actor: ActorRef)) => + log.info("Unsubscribing Listener : [{}] from actor: [{}]", handler.id(), actor.path.name) subscriptions.remove(key) actor ! PoisonPill case None => diff --git a/client/src/main/scala/org/zalando/nakadi/client/handler/SubscriptionHandler.scala b/client/src/main/scala/org/zalando/nakadi/client/handler/SubscriptionHandler.scala index de27110..28ef217 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/handler/SubscriptionHandler.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/handler/SubscriptionHandler.scala @@ -41,44 +41,29 @@ trait SubscriptionHandler { endpoint: String, cursor: Option[Cursor], eventHandler: EventHandler): Option[ClientError] - def unsubscribe(eventTypeName: String, - partition: Option[String], - listenerId: String): Option[ClientError] - def createPipeline(cursor: Option[Cursor], - consumingActor: ActorRef, - url: String, - eventHandler: EventHandler) + def unsubscribe(eventTypeName: String, partition: Option[String], listenerId: String): Option[ClientError] + def createPipeline(cursor: Option[Cursor], consumingActor: ActorRef, url: String, eventHandler: EventHandler) } -class SubscriptionHandlerImpl(val connection: Connection) - extends SubscriptionHandler { +class SubscriptionHandlerImpl(val connection: Connection) extends SubscriptionHandler { import HttpFactory._ import SupervisingActor._ //Local variables private implicit val materializer = connection.materializer(decider()) - private val supervisingActor = connection.actorSystem.actorOf( - Props(classOf[SupervisingActor], connection, this), - "SupervisingActor" + System.currentTimeMillis()) + private val supervisingActor = connection.actorSystem + .actorOf(Props(classOf[SupervisingActor], connection, this), "SupervisingActor" + System.currentTimeMillis()) private val RECEIVE_BUFFER_SIZE = 40960 - private val EVENT_DELIMITER = "\n" + private val EVENT_DELIMITER = "\n" val logger = LoggerFactory.getLogger(this.getClass) - def subscribe(eventTypeName: String, - endpoint: String, - cursor: Option[Cursor], - eventHandler: EventHandler) = { - supervisingActor ! SubscribeMsg(eventTypeName, - endpoint, - cursor, - eventHandler) + def subscribe(eventTypeName: String, endpoint: String, cursor: Option[Cursor], eventHandler: EventHandler) = { + supervisingActor ! SubscribeMsg(eventTypeName, endpoint, cursor, eventHandler) None } - def unsubscribe(eventTypeName: String, - partition: Option[String], - listenerId: String) = { + def unsubscribe(eventTypeName: String, partition: Option[String], listenerId: String) = { supervisingActor ! UnsubscribeMsg(eventTypeName, partition, listenerId) None } @@ -87,10 +72,7 @@ class SubscriptionHandlerImpl(val connection: Connection) case _ => Supervision.Stop } - def createPipeline(cursor: Option[Cursor], - consumingActor: ActorRef, - url: String, - eventHandler: EventHandler) = { + def createPipeline(cursor: Option[Cursor], consumingActor: ActorRef, url: String, eventHandler: EventHandler) = { val subscriber = ActorSubscriber[ByteString](consumingActor) @@ -126,21 +108,17 @@ class SubscriptionHandlerImpl(val connection: Connection) actor ! PoisonPill } - private def requestCreator( - url: String): Flow[Option[Cursor], HttpRequest, NotUsed] = - Flow[Option[Cursor]] - .map(withHttpRequest(url, _, None, connection.tokenProvider)) + private def requestCreator(url: String): Flow[Option[Cursor], HttpRequest, NotUsed] = + Flow[Option[Cursor]].map(withHttpRequest(url, _, None, connection.tokenProvider)) - private def requestRenderer(handler: EventHandler) - : Flow[HttpResponse, Source[ByteString, Any], NotUsed] = + private def requestRenderer(handler: EventHandler): Flow[HttpResponse, Source[ByteString, Any], NotUsed] = Flow[HttpResponse].filter { x => if (x.status.isSuccess()) { logger.info("Connection established with success!") x.status.isSuccess() } else { x.entity.toStrict(10.second).map { body => - logger.error( - s"http-status: ${x.status.intValue().toString()}, reason[${x.status + logger.error(s"http-status: ${x.status.intValue().toString()}, reason[${x.status .reason()}], body:[${body.data.decodeString("UTF-8")}]") } false @@ -149,7 +127,6 @@ class SubscriptionHandlerImpl(val connection: Connection) private def delimiterFlow = Flow[ByteString].via( - Framing.delimiter(ByteString(EVENT_DELIMITER), - maximumFrameLength = RECEIVE_BUFFER_SIZE, - allowTruncation = true)) + Framing + .delimiter(ByteString(EVENT_DELIMITER), maximumFrameLength = RECEIVE_BUFFER_SIZE, allowTruncation = true)) } diff --git a/client/src/main/scala/org/zalando/nakadi/client/java/JavaClientHandler.scala b/client/src/main/scala/org/zalando/nakadi/client/java/JavaClientHandler.scala index a1d334e..a22e3a6 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/java/JavaClientHandler.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/java/JavaClientHandler.scala @@ -43,88 +43,65 @@ import scala.concurrent.duration.Duration * Handler for mapping(Java<->Scala) and handling http calls and listener subscriptions for the Java API. */ trait JavaClientHandler { - def deserialize[T](response: HttpResponse, - des: Deserializer[T]): Future[Optional[T]] - def get[T](endpoint: String, - des: Deserializer[T]): java.util.concurrent.Future[Optional[T]] + def deserialize[T](response: HttpResponse, des: Deserializer[T]): Future[Optional[T]] + def get[T](endpoint: String, des: Deserializer[T]): java.util.concurrent.Future[Optional[T]] def get[T](endpoint: String, headers: Seq[HttpHeader], des: Deserializer[T]): java.util.concurrent.Future[Optional[T]] - def post[T](endpoint: String, model: T)( - implicit serializer: Serializer[T]): java.util.concurrent.Future[Void] - def subscribe[T <: JEvent](eventTypeName: String, - endpoint: String, - parameters: JStreamParameters, - listener: JListener[T])( - implicit des: Deserializer[JEventStreamBatch[T]]): Optional[ClientError] - def unsubscribe[T <: JEvent](eventTypeName: String, - partition: Optional[String], - listener: JListener[T]) + def post[T](endpoint: String, model: T)(implicit serializer: Serializer[T]): java.util.concurrent.Future[Void] + def subscribe[T <: JEvent]( + eventTypeName: String, + endpoint: String, + parameters: JStreamParameters, + listener: JListener[T])(implicit des: Deserializer[JEventStreamBatch[T]]): Optional[ClientError] + def unsubscribe[T <: JEvent](eventTypeName: String, partition: Optional[String], listener: JListener[T]) def stop(): Unit } -class JavaClientHandlerImpl(val connection: Connection, - subscriber: SubscriptionHandler) - extends JavaClientHandler { +class JavaClientHandlerImpl(val connection: Connection, subscriber: SubscriptionHandler) extends JavaClientHandler { val logger = LoggerFactory.getLogger(this.getClass) import HttpFactory._ import GeneralConversions._ private implicit val mat = connection.materializer() - def deserialize[T](response: HttpResponse, - des: Deserializer[T]): Future[Optional[T]] = + def deserialize[T](response: HttpResponse, des: Deserializer[T]): Future[Optional[T]] = response match { - case HttpResponse(status, headers, entity, protocol) - if (status.isSuccess()) => + case HttpResponse(status, headers, entity, protocol) if (status.isSuccess()) => Try(Unmarshal(entity).to[String].map(body => des.from(body))) match { - case Success(result) => result.map(Optional.of(_)) - case Failure(error) => throw new RuntimeException(error.getMessage) + case Success(result) => + result.map(Optional.of(_)) + case Failure(error) => + throw new RuntimeException(error.getMessage) } case HttpResponse(StatusCodes.NotFound, headers, entity, protocol) => Future.successful(Optional.empty()) - case HttpResponse(status, headers, entity, protocol) - if (status.isFailure()) => + case HttpResponse(status, headers, entity, protocol) if (status.isFailure()) => throw new RuntimeException(status.reason()) } - def get[T]( - endpoint: String, - des: Deserializer[T]): java.util.concurrent.Future[Optional[T]] = { - FutureConversions.fromFuture2Future( - connection.get(endpoint).flatMap(deserialize(_, des))) + def get[T](endpoint: String, des: Deserializer[T]): java.util.concurrent.Future[Optional[T]] = { + FutureConversions.fromFuture2Future(connection.get(endpoint).flatMap(deserialize(_, des))) } - def get[T]( - endpoint: String, - headers: Seq[HttpHeader], - des: Deserializer[T]): java.util.concurrent.Future[Optional[T]] = { + def get[T](endpoint: String, + headers: Seq[HttpHeader], + des: Deserializer[T]): java.util.concurrent.Future[Optional[T]] = { FutureConversions.fromFuture2Future( connection - .executeCall( - withHttpRequest(endpoint, - HttpMethods.GET, - headers, - connection.tokenProvider, - None)) + .executeCall(withHttpRequest(endpoint, HttpMethods.GET, headers, connection.tokenProvider, None)) .flatMap(deserialize(_, des))) } - def post[T](endpoint: String, model: T)(implicit serializer: Serializer[T]) - : java.util.concurrent.Future[Void] = { + def post[T](endpoint: String, model: T)(implicit serializer: Serializer[T]): java.util.concurrent.Future[Void] = { val entity = serializer.to(model) logger.info("Posting to endpoint {}", endpoint) logger.debug("Data to post {}", entity) val result = connection - .executeCall( - withHttpRequestAndPayload(endpoint, - serialize(model), - HttpMethods.POST, - connection.tokenProvider)) + .executeCall(withHttpRequestAndPayload(endpoint, serialize(model), HttpMethods.POST, connection.tokenProvider)) .flatMap(response(_)) FutureConversions.fromOption2Void(result) } - private def serialize[T](model: T)( - implicit serializer: Serializer[T]): String = + private def serialize[T](model: T)(implicit serializer: Serializer[T]): String = Try(serializer.to(model)) match { case Success(result) => result case Failure(error) => @@ -133,18 +110,16 @@ class JavaClientHandlerImpl(val connection: Connection, private def response[T](response: HttpResponse): Future[Option[String]] = response match { - case HttpResponse(status, headers, entity, protocol) - if (status.isSuccess()) => + case HttpResponse(status, headers, entity, protocol) if (status.isSuccess()) => Try(Unmarshal(entity).to[String]) match { case Success(result) => result.map(Option(_)) - case Failure(error) => throw new RuntimeException(error.getMessage) + case Failure(error) => + throw new RuntimeException(error.getMessage) } - case HttpResponse(status, headers, entity, protocol) - if (status.isFailure()) => + case HttpResponse(status, headers, entity, protocol) if (status.isFailure()) => Unmarshal(entity).to[String].map { x => - val msg = "http-stats(%s) - %s - problem: %s " - .format(status.intValue(), x, status.defaultMessage()) + val msg = "http-stats(%s) - %s - problem: %s ".format(status.intValue(), x, status.defaultMessage()) logger.warn(msg) throw new RuntimeException(msg) } @@ -153,21 +128,16 @@ class JavaClientHandlerImpl(val connection: Connection, def subscribe[T <: JEvent](eventTypeName: String, endpoint: String, parameters: JStreamParameters, - listener: JListener[T])( - implicit des: Deserializer[JEventStreamBatch[T]]) = { + listener: JListener[T])(implicit des: Deserializer[JEventStreamBatch[T]]) = { import ModelConverter._ - val params: Option[ScalaStreamParameters] = toScalaStreamParameters( - parameters) - val eventHandler: EventHandler = new JavaEventHandlerImpl(des, listener) - val finalUrl = withUrl(endpoint, params) - val res = subscriber - .subscribe(eventTypeName, finalUrl, getCursor(params), eventHandler) + val params: Option[ScalaStreamParameters] = toScalaStreamParameters(parameters) + val eventHandler: EventHandler = new JavaEventHandlerImpl(des, listener) + val finalUrl = withUrl(endpoint, params) + val res = subscriber.subscribe(eventTypeName, finalUrl, getCursor(params), eventHandler) toJavaClientError(res) } - def unsubscribe[T <: JEvent](eventTypeName: String, - partition: Optional[String], - listener: JListener[T]) = { + def unsubscribe[T <: JEvent](eventTypeName: String, partition: Optional[String], listener: JListener[T]) = { subscriber.unsubscribe(eventTypeName, toOption(partition), listener.getId) } @@ -176,8 +146,7 @@ class JavaClientHandlerImpl(val connection: Connection, Await.ready(connection.actorSystem().terminate(), Duration.Inf) } - private def getCursor( - params: Option[ScalaStreamParameters]): Option[ScalaCursor] = + private def getCursor(params: Option[ScalaStreamParameters]): Option[ScalaCursor] = params match { case Some( ScalaStreamParameters(cursor, diff --git a/client/src/main/scala/org/zalando/nakadi/client/java/model/JavaJacksonJsonMarshaller.scala b/client/src/main/scala/org/zalando/nakadi/client/java/model/JavaJacksonJsonMarshaller.scala index 6241f28..7eedb1e 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/java/model/JavaJacksonJsonMarshaller.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/java/model/JavaJacksonJsonMarshaller.scala @@ -23,22 +23,32 @@ import com.fasterxml.jackson.databind.JavaType object JavaJacksonJsonMarshaller { val logger = LoggerFactory.getLogger(this.getClass) // All TypeReferences - def problemTR: TypeReference[Problem] = new TypeReference[Problem] {} - def metricsTR: TypeReference[Metrics] = new TypeReference[Metrics] {} - def partitionTR: TypeReference[Partition] = new TypeReference[Partition] {} - def cursorTR: TypeReference[Cursor] = new TypeReference[Cursor] {} + def problemTR: TypeReference[Problem] = + new TypeReference[Problem] {} + def metricsTR: TypeReference[Metrics] = + new TypeReference[Metrics] {} + def partitionTR: TypeReference[Partition] = + new TypeReference[Partition] {} + def cursorTR: TypeReference[Cursor] = + new TypeReference[Cursor] {} def eventTypeSchemaTR: TypeReference[EventTypeSchema] = new TypeReference[EventTypeSchema] {} def eventTypeCategoryTR: TypeReference[EventTypeCategory] = new TypeReference[EventTypeCategory] {} - def partitionResolutionStrategyTR: TypeReference[PartitionStrategy] = new TypeReference[PartitionStrategy] {} - def eventEnrichmentStrategyTR: TypeReference[EventEnrichmentStrategy] = new TypeReference[EventEnrichmentStrategy] {} + def partitionResolutionStrategyTR: TypeReference[PartitionStrategy] = + new TypeReference[PartitionStrategy] {} + def eventEnrichmentStrategyTR: TypeReference[EventEnrichmentStrategy] = + new TypeReference[EventEnrichmentStrategy] {} def dataChangeEventQualifierTR: TypeReference[DataChangeEventQualifier] = new TypeReference[DataChangeEventQualifier] {} - def eventTypeStatisticsTR: TypeReference[EventTypeStatistics] = new TypeReference[EventTypeStatistics] {} - def eventTypeTR: TypeReference[EventType] = new TypeReference[EventType] {} - def eventTR: TypeReference[Event] = new TypeReference[Event] {} - def eventStreamBatchTR: TypeReference[EventStreamBatch[_]] = new TypeReference[EventStreamBatch[_]] {} + def eventTypeStatisticsTR: TypeReference[EventTypeStatistics] = + new TypeReference[EventTypeStatistics] {} + def eventTypeTR: TypeReference[EventType] = + new TypeReference[EventType] {} + def eventTR: TypeReference[Event] = + new TypeReference[Event] {} + def eventStreamBatchTR: TypeReference[EventStreamBatch[_]] = + new TypeReference[EventStreamBatch[_]] {} def eventMetadataTR: TypeReference[EventMetadata] = new TypeReference[EventMetadata] {} @@ -69,7 +79,8 @@ object JavaJacksonJsonMarshaller { } def serializer[T]: Serializer[T] = new Serializer[T] { - def to(from: T): String = defaultObjectMapper.writeValueAsString(from) + def to(from: T): String = + defaultObjectMapper.writeValueAsString(from) } def deserializer[T](expectedType: TypeReference[T]): Deserializer[T] = diff --git a/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala index c54da4d..87a9a3a 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala @@ -30,10 +30,7 @@ import akka.http.scaladsl.unmarshalling.Unmarshal import com.fasterxml.jackson.module.scala.JsonScalaEnumeration import org.zalando.nakadi.client.scala.model.EventEnrichmentStrategyType -class ClientImpl(connection: Connection, - subscriber: SubscriptionHandler, - charSet: String = "UTF-8") - extends Client { +class ClientImpl(connection: Connection, subscriber: SubscriptionHandler, charSet: String = "UTF-8") extends Client { import Uri._ import ScalaJacksonJsonMarshaller._ import HttpFactory._ @@ -42,105 +39,74 @@ class ClientImpl(connection: Connection, private val logger = LoggerFactory.getLogger(this.getClass) def getMetrics(): Future[Either[ClientError, Option[Metrics]]] = { - logFutureEither( - connection - .get(URI_METRICS) - .flatMap(mapToEither(_)(deserializer(metricsTR)))) + logFutureEither(connection.get(URI_METRICS).flatMap(mapToEither(_)(deserializer(metricsTR)))) } def getEventTypes(): Future[Either[ClientError, Option[Seq[EventType]]]] = { - logFutureEither( - connection - .get(URI_EVENT_TYPES) - .flatMap(mapToEither(_)(deserializer(listOfEventTypeTR)))) + logFutureEither(connection.get(URI_EVENT_TYPES).flatMap(mapToEither(_)(deserializer(listOfEventTypeTR)))) } def createEventType(eventType: EventType): Future[Option[ClientError]] = { - logFutureOption( - connection.post(URI_EVENT_TYPES, eventType).flatMap(mapToOption(_))) + logFutureOption(connection.post(URI_EVENT_TYPES, eventType).flatMap(mapToOption(_))) } - def getEventType( - name: String): Future[Either[ClientError, Option[EventType]]] = { + def getEventType(name: String): Future[Either[ClientError, Option[EventType]]] = { logFutureEither( - connection - .get(URI_EVENT_TYPE_BY_NAME.format(name)) - .flatMap(in => mapToEither(in)(deserializer(eventTypeTR)))) + connection.get(URI_EVENT_TYPE_BY_NAME.format(name)).flatMap(in => mapToEither(in)(deserializer(eventTypeTR)))) } - def updateEventType(name: String, - eventType: EventType): Future[Option[ClientError]] = { + def updateEventType(name: String, eventType: EventType): Future[Option[ClientError]] = { val result = connection.put(URI_EVENT_TYPE_BY_NAME.format(name), eventType) logFutureOption(result.flatMap(in => mapToOption(in))) } def deleteEventType(name: String): Future[Option[ClientError]] = { - logFutureOption( - connection - .delete(URI_EVENT_TYPE_BY_NAME.format(name)) - .flatMap(in => mapToOption(in))) + logFutureOption(connection.delete(URI_EVENT_TYPE_BY_NAME.format(name)).flatMap(in => mapToOption(in))) } - def publishEvents[T <: Event]( - eventTypeName: String, - events: Seq[T], - ser: Serializer[Seq[T]]): Future[Option[ClientError]] = { + def publishEvents[T <: Event](eventTypeName: String, + events: Seq[T], + ser: Serializer[Seq[T]]): Future[Option[ClientError]] = { logFutureOption( - connection - .post(URI_EVENTS_OF_EVENT_TYPE.format(eventTypeName), events) - .flatMap(in => mapToOption(in))) + connection.post(URI_EVENTS_OF_EVENT_TYPE.format(eventTypeName), events).flatMap(in => mapToOption(in))) } - def publishEvents[T <: Event]( - eventTypeName: String, - events: Seq[T]): Future[Option[ClientError]] = { + def publishEvents[T <: Event](eventTypeName: String, events: Seq[T]): Future[Option[ClientError]] = { logFutureOption( - connection - .post(URI_EVENTS_OF_EVENT_TYPE.format(eventTypeName), events) - .flatMap(in => mapToOption(in))) + connection.post(URI_EVENTS_OF_EVENT_TYPE.format(eventTypeName), events).flatMap(in => mapToOption(in))) } - def getPartitions( - name: String): Future[Either[ClientError, Option[Seq[Partition]]]] = { + def getPartitions(name: String): Future[Either[ClientError, Option[Seq[Partition]]]] = { logFutureEither( connection .get(URI_PARTITIONS_BY_EVENT_TYPE.format(name)) .flatMap(in => mapToEither(in)(deserializer(listOfPartitionTR)))) } - def getEnrichmentStrategies(): Future[ - Either[ClientError, Option[Seq[EventEnrichmentStrategy.Value]]]] = { + def getEnrichmentStrategies(): Future[Either[ClientError, Option[Seq[EventEnrichmentStrategy.Value]]]] = { logFutureEither( connection .get(URI_ENRICHMENT_STRATEGIES) - .flatMap( - mapToEither(_)(deserializer(listOfEventEnrichmentStrategyTR)))) + .flatMap(mapToEither(_)(deserializer(listOfEventEnrichmentStrategyTR)))) } - def getPartitioningStrategies() - : Future[Either[ClientError, Option[Seq[PartitionStrategy.Value]]]] = + def getPartitioningStrategies(): Future[Either[ClientError, Option[Seq[PartitionStrategy.Value]]]] = logFutureEither( - connection - .get(URI_PARTITIONING_STRATEGIES) - .flatMap(mapToEither(_)(deserializer(listOfPartitionStrategyTR)))) + connection.get(URI_PARTITIONING_STRATEGIES).flatMap(mapToEither(_)(deserializer(listOfPartitionStrategyTR)))) def stop(): Option[ClientError] = { materializer.shutdown() - val result = - Await.ready(connection.actorSystem().terminate(), Duration.Inf) + val result = Await.ready(connection.actorSystem().terminate(), Duration.Inf) None } - def subscribe[T <: Event]( - eventTypeName: String, - parameters: StreamParameters, - listener: Listener[T], - typeRef: TypeReference[EventStreamBatch[T]]): Option[ClientError] = { + def subscribe[T <: Event](eventTypeName: String, + parameters: StreamParameters, + listener: Listener[T], + typeRef: TypeReference[EventStreamBatch[T]]): Option[ClientError] = { subscribe(eventTypeName, parameters, listener)(deserializer(typeRef)) } - def subscribe[T <: Event](eventTypeName: String, - params: StreamParameters, - listener: Listener[T])( + def subscribe[T <: Event](eventTypeName: String, params: StreamParameters, listener: Listener[T])( implicit des: Deserializer[EventStreamBatch[T]]): Option[ClientError] = (eventTypeName, params, listener) match { @@ -152,26 +118,22 @@ class ClientImpl(connection: Connection, logger.info("eventType is null") Some(ClientError("Eventype may not be empty(null)!", None)) - case (eventType, StreamParameters(cursor, _, _, _, _, _, _), listener) - if Option(eventType).isDefined => + case (eventType, StreamParameters(cursor, _, _, _, _, _, _), listener) if Option(eventType).isDefined => val url = URI_EVENTS_OF_EVENT_TYPE.format(eventType) - logger.debug( - "Subscribing listener {} - cursor {} - parameters {} - eventType {} - url {}", - listener.id, - cursor, - params, - eventType, - url) - val finalUrl = withUrl(url, Some(params)) - val eventHandler: EventHandler = - new ScalaEventHandlerImpl(des, listener) + logger.debug("Subscribing listener {} - cursor {} - parameters {} - eventType {} - url {}", + listener.id, + cursor, + params, + eventType, + url) + val finalUrl = withUrl(url, Some(params)) + val eventHandler: EventHandler = new ScalaEventHandlerImpl(des, listener) subscriber.subscribe(eventTypeName, finalUrl, cursor, eventHandler) } - def unsubscribe[T <: Event]( - eventTypeName: String, - partition: Option[String], - listener: Listener[T]): Future[Option[ClientError]] = { + def unsubscribe[T <: Event](eventTypeName: String, + partition: Option[String], + listener: Listener[T]): Future[Option[ClientError]] = { subscriber.unsubscribe(eventTypeName, partition, listener.id) Future.successful(None) } @@ -180,8 +142,7 @@ class ClientImpl(connection: Connection, //# HELPER METHODS # //#################### - private def logFutureEither[A, T](future: Future[Either[ClientError, T]]) - : Future[Either[ClientError, T]] = { + private def logFutureEither[A, T](future: Future[Either[ClientError, T]]): Future[Either[ClientError, T]] = { future recover { case e: Throwable => val msg = s"An unexpected error occured: ${e.getMessage}" @@ -189,8 +150,7 @@ class ClientImpl(connection: Connection, Left(ClientError(msg, None)) } } - private def logFutureOption( - future: Future[Option[ClientError]]): Future[Option[ClientError]] = { + private def logFutureOption(future: Future[Option[ClientError]]): Future[Option[ClientError]] = { future recover { case e: Throwable => val msg = s"An unexpected error occured: ${e.getMessage}" @@ -199,12 +159,10 @@ class ClientImpl(connection: Connection, } private def mapToEither[T](response: HttpResponse)( - implicit deserializer: Deserializer[T]) - : Future[Either[ClientError, Option[T]]] = { + implicit deserializer: Deserializer[T]): Future[Either[ClientError, Option[T]]] = { logger.debug("received [response={}]", response) response match { - case HttpResponse(status, headers, entity, protocol) - if (status.isSuccess()) => + case HttpResponse(status, headers, entity, protocol) if (status.isSuccess()) => try { Unmarshal(entity).to[String].map { body => logger.debug(s"Payload: $body") @@ -212,51 +170,40 @@ class ClientImpl(connection: Connection, } } catch { case e: Throwable => - val msg = - "Failed to deserialise the content with error: %s".format( - e.getMessage) + val msg = "Failed to deserialise the content with error: %s".format(e.getMessage) logger.error(msg) Future.successful(Left(ClientError(msg, Some(status.intValue())))) } case HttpResponse(StatusCodes.NotFound, headers, entity, protocol) => - logger.info( - s"Received: httpStatus - Not found ${StatusCodes.NotFound}") + logger.info(s"Received: httpStatus - Not found ${StatusCodes.NotFound}") Future.successful(Right(None)) - case HttpResponse(status, headers, entity, protocol) - if (status.isRedirection()) => - val msg = "Not implemented: http-status (" + status - .intValue() + "}) and reason:" + status.reason() + case HttpResponse(status, headers, entity, protocol) if (status.isRedirection()) => + val msg = "Not implemented: http-status (" + status.intValue() + "}) and reason:" + status.reason() logger.info(msg) Future.successful(Left(ClientError(msg, Some(status.intValue())))) case HttpResponse(status, headers, entity, protocol) => Unmarshal(entity).to[String].map { body => - val msg = "Service return http-status: %s (%s) Message: %s" - .format(status.intValue(), status.reason(), body) + val msg = "Service return http-status: %s (%s) Message: %s".format(status.intValue(), status.reason(), body) logger.warn(msg) Left(ClientError(msg, Some(status.intValue()))) } } } - private[client] def mapToOption[T]( - response: HttpResponse): Future[Option[ClientError]] = { + private[client] def mapToOption[T](response: HttpResponse): Future[Option[ClientError]] = { response.status match { case status if (status.isSuccess()) => logger.info(s"Success. http-status: ${status.intValue()}") response.entity.toStrict(10.second).map { body => - logger.debug( - "Success - http-status: %s, body:[%s]".format( - status.intValue().toString(), - body.data.decodeString(charSet))) + logger.debug("Success - http-status: %s, body:[%s]".format(status.intValue().toString(), + body.data.decodeString(charSet))) } Future.successful(None) case status if (status.isRedirection()) => - val msg = - s"Redirection - http-status: ${status.intValue()}, reason[${status.reason()}]" + val msg = s"Redirection - http-status: ${status.intValue()}, reason[${status.reason()}]" logger.info(msg) response.entity.toStrict(10.second).map { body => - logger.debug( - s"Redirection - http-status: ${status.intValue().toString()}, reason[${status + logger.debug(s"Redirection - http-status: ${status.intValue().toString()}, reason[${status .reason()}], body:[${body.data.decodeString(charSet)}]") } Future.successful(Option(ClientError(msg, Some(status.intValue())))) @@ -264,8 +211,7 @@ class ClientImpl(connection: Connection, logger.warn(s"Failure. http-status: ${status.intValue()}") response.entity.toStrict(10.second).map { body => val msg = - s"Failure - http-status: ${status.intValue()}, reason[${status - .reason()}], body:[${body.data.decodeString(charSet)}]" + s"Failure - http-status: ${status.intValue()}, reason[${status.reason()}], body:[${body.data.decodeString(charSet)}]" logger.warn(msg) Option(ClientError(msg, Some(status.intValue()))) } diff --git a/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala index f848ad8..119e39b 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala @@ -35,7 +35,7 @@ import akka.http.scaladsl.model.{HttpHeader, HttpMethod, HttpMethods, HttpRespon import org.zalando.nakadi.client.handler.SubscriptionHandlerImpl import akka.stream.Supervision -sealed trait EmptyJavaEvent extends JEvent +sealed trait EmptyJavaEvent extends JEvent sealed trait EmptyScalaEvent extends Event trait Connection { @@ -45,12 +45,9 @@ trait Connection { def executeCall(request: HttpRequest): Future[HttpResponse] def get(endpoint: String): Future[HttpResponse] def delete(endpoint: String): Future[HttpResponse] - def put[T](endpoint: String, model: T)( - implicit serializer: Serializer[T]): Future[HttpResponse] - def post[T](endpoint: String, model: T)( - implicit serializer: Serializer[T]): Future[HttpResponse] - def requestFlow() - : Flow[HttpRequest, HttpResponse, Future[Http.OutgoingConnection]] + def put[T](endpoint: String, model: T)(implicit serializer: Serializer[T]): Future[HttpResponse] + def post[T](endpoint: String, model: T)(implicit serializer: Serializer[T]): Future[HttpResponse] + def requestFlow(): Flow[HttpRequest, HttpResponse, Future[Http.OutgoingConnection]] def materializer(decider: Supervision.Decider): ActorMaterializer def materializer(): ActorMaterializer def actorSystem(): ActorSystem @@ -61,30 +58,24 @@ trait Connection { */ object Connection { val RECEIVE_BUFFER_SIZE = 10240 - val EVENT_DELIMITER = "\n" + val EVENT_DELIMITER = "\n" /** * Creates a new SSL context for usage with connections based on the HTTPS protocol. */ - def newSslContext(secured: Boolean, - verified: Boolean): Option[HttpsConnectionContext] = + def newSslContext(secured: Boolean, verified: Boolean): Option[HttpsConnectionContext] = (secured, verified) match { case (true, true) => Some(new HttpsConnectionContext(SSLContext.getDefault)) case (true, false) => val permissiveTrustManager: TrustManager = new X509TrustManager() { - override def checkClientTrusted( - x$1: Array[java.security.cert.X509Certificate], - x$2: String): Unit = {} - override def checkServerTrusted( - x$1: Array[java.security.cert.X509Certificate], - x$2: String): Unit = {} + override def checkClientTrusted(x$1: Array[java.security.cert.X509Certificate], x$2: String): Unit = {} + override def checkServerTrusted(x$1: Array[java.security.cert.X509Certificate], x$2: String): Unit = {} override def getAcceptedIssuers(): Array[X509Certificate] = Array.empty } val sslContext = SSLContext.getInstance("TLS") - sslContext - .init(Array.empty, Array(permissiveTrustManager), new SecureRandom()) + sslContext.init(Array.empty, Array(permissiveTrustManager), new SecureRandom()) Some(new HttpsConnectionContext(sslContext)) case _ => None } @@ -94,27 +85,17 @@ object Connection { tokenProvider: Option[() => String], securedConnection: Boolean, verifySSlCertificate: Boolean): Client = { - val connection = new ConnectionImpl(host, - port, - tokenProvider, - securedConnection, - verifySSlCertificate) + val connection = new ConnectionImpl(host, port, tokenProvider, securedConnection, verifySSlCertificate) new ClientImpl(connection, new SubscriptionHandlerImpl(connection)) } - def newClientHandler4Java( - host: String, - port: Int, - tokenProvider: Option[() => String], - securedConnection: Boolean, - verifySSlCertificate: Boolean): JavaClientHandler = { - val connection = new ConnectionImpl(host, - port, - tokenProvider, - securedConnection, - verifySSlCertificate) - new JavaClientHandlerImpl(connection, - new SubscriptionHandlerImpl(connection)) + def newClientHandler4Java(host: String, + port: Int, + tokenProvider: Option[() => String], + securedConnection: Boolean, + verifySSlCertificate: Boolean): JavaClientHandler = { + val connection = new ConnectionImpl(host, port, tokenProvider, securedConnection, verifySSlCertificate) + new JavaClientHandlerImpl(connection, new SubscriptionHandlerImpl(connection)) } } @@ -130,28 +111,25 @@ sealed class ConnectionImpl(val host: String, import Connection._ import HttpFactory._ var registrationCounter: AtomicLong = new AtomicLong(0); - type HttpFlow[T] = - Flow[(HttpRequest, T), (Try[HttpResponse], T), HostConnectionPool] + type HttpFlow[T] = Flow[(HttpRequest, T), (Try[HttpResponse], T), HostConnectionPool] type StepResult[T] = (T, Option[String]) implicit val actorSystem = ActorSystem("Nakadi-Client-Connections") def materializer(decider: Supervision.Decider): ActorMaterializer = { - ActorMaterializer( - ActorMaterializerSettings(actorSystem).withSupervisionStrategy( - decider)) + ActorMaterializer(ActorMaterializerSettings(actorSystem).withSupervisionStrategy(decider)) } def materializer() = ActorMaterializer(ActorMaterializerSettings(actorSystem)) - private implicit val http = Http(actorSystem) + private implicit val http = Http(actorSystem) private val actors: Map[String, Actor] = Map() val logger = LoggerFactory.getLogger(this.getClass) - def requestFlow() - : Flow[HttpRequest, HttpResponse, Future[Http.OutgoingConnection]] = + def requestFlow(): Flow[HttpRequest, HttpResponse, Future[Http.OutgoingConnection]] = newSslContext(securedConnection, verifySSlCertificate) match { - case Some(result) => http.outgoingConnectionHttps(host, port, result) + case Some(result) => + http.outgoingConnectionHttps(host, port, result) case None => logger.warn("Disabled HTTPS, switching to HTTP only!") http.outgoingConnection(host, port) @@ -159,43 +137,27 @@ sealed class ConnectionImpl(val host: String, def get(endpoint: String): Future[HttpResponse] = { logger.info("Get - URL {}", endpoint) - executeCall( - withHttpRequest(endpoint, HttpMethods.GET, Nil, tokenProvider, None)) + executeCall(withHttpRequest(endpoint, HttpMethods.GET, Nil, tokenProvider, None)) } def delete(endpoint: String): Future[HttpResponse] = { logger.info("Delete: {}", endpoint) - executeCall( - withHttpRequest(endpoint, - HttpMethods.DELETE, - Nil, - tokenProvider, - None)) + executeCall(withHttpRequest(endpoint, HttpMethods.DELETE, Nil, tokenProvider, None)) } - def put[T](endpoint: String, model: T)( - implicit serializer: Serializer[T]): Future[HttpResponse] = { + def put[T](endpoint: String, model: T)(implicit serializer: Serializer[T]): Future[HttpResponse] = { val entity = serializer.to(model) logger.info(s"Put: $endpoint - Data: $entity") - executeCall( - withHttpRequest(endpoint, HttpMethods.PUT, Nil, tokenProvider, None)) + executeCall(withHttpRequest(endpoint, HttpMethods.PUT, Nil, tokenProvider, None)) } - def post[T](endpoint: String, model: T)( - implicit serializer: Serializer[T]): Future[HttpResponse] = { + def post[T](endpoint: String, model: T)(implicit serializer: Serializer[T]): Future[HttpResponse] = { val entity = serializer.to(model) logger.info(s"Put: $endpoint - Data: $entity") - executeCall( - withHttpRequestAndPayload(endpoint, - entity, - HttpMethods.POST, - tokenProvider)) + executeCall(withHttpRequestAndPayload(endpoint, entity, HttpMethods.POST, tokenProvider)) } def executeCall(request: HttpRequest): Future[HttpResponse] = { - val response: Future[HttpResponse] = Source - .single(request) - .via(requestFlow) - .runWith(Sink.head)(materializer()) + val response: Future[HttpResponse] = Source.single(request).via(requestFlow).runWith(Sink.head)(materializer()) logError(response) response } diff --git a/client/src/main/scala/org/zalando/nakadi/client/scala/HttpFactory.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/HttpFactory.scala index e7710e2..a690e14 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/scala/HttpFactory.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/scala/HttpFactory.scala @@ -23,7 +23,9 @@ object HttpFactory { def withUrl(url: String, params: Option[StreamParameters]) = { val paramsList = withQueryParams(params) val urlParams = - if (!paramsList.isEmpty) paramsList.mkString("?", "&", "") else "" + if (!paramsList.isEmpty) + paramsList.mkString("?", "&", "") + else "" url + urlParams } @@ -41,11 +43,10 @@ object HttpFactory { HttpRequest(uri = url, method = httpMethod).withHeaders(allHeaders) } - def withHttpRequestAndPayload( - url: String, - entity: String, - httpMethod: HttpMethod, - tokenProvider: Option[TokenProvider]): HttpRequest = { + def withHttpRequestAndPayload(url: String, + entity: String, + httpMethod: HttpMethod, + tokenProvider: Option[TokenProvider]): HttpRequest = { val request = HttpRequest(uri = url, method = httpMethod) tokenProvider match { case None => @@ -64,9 +65,7 @@ object HttpFactory { cursor: Option[Cursor], flowId: Option[String], tokenProvider: Option[TokenProvider]): HttpRequest = { - val customHeaders = withDefaultHeaders(cursor, flowId) :+ RawHeader( - "Accept", - "application/x-json-stream") + val customHeaders = withDefaultHeaders(cursor, flowId) :+ RawHeader("Accept", "application/x-json-stream") val allHeaders = tokenProvider match { case None => customHeaders case Some(token) => @@ -78,39 +77,31 @@ object HttpFactory { private def withQueryParams(params: Option[StreamParameters]): Seq[String] = { params match { case Some( - StreamParameters(_, - batchLimit, - streamLimit, - batchFlushTimeout, - streamTimeout, - streamKeepAliveLimit, - _)) => - val parameters = List( - ("batch_limit", batchLimit), - ("stream_limit", streamLimit), // - ("batch_flush_timeout", batchFlushTimeout), - ("stream_timeout", streamTimeout), // - ("stream_keep_alive_limit", streamKeepAliveLimit)) - for { (key, optional) <- parameters; value <- optional } yield - (key + "=" + value) + StreamParameters(_, batchLimit, streamLimit, batchFlushTimeout, streamTimeout, streamKeepAliveLimit, _)) => + val parameters = List(("batch_limit", batchLimit), + ("stream_limit", streamLimit), // + ("batch_flush_timeout", batchFlushTimeout), + ("stream_timeout", streamTimeout), // + ("stream_keep_alive_limit", streamKeepAliveLimit)) + for { + (key, optional) <- parameters; value <- optional + } yield (key + "=" + value) case None => Nil } } - private def withDefaultHeaders(cursor: Option[Cursor], - flowId: Option[String]): Seq[HttpHeader] = { + private def withDefaultHeaders(cursor: Option[Cursor], flowId: Option[String]): Seq[HttpHeader] = { val nakadiCursor = cursor match { - case Some(value) - if Option(value.partition).isDefined && Option(value.offset).isDefined => + case Some(value) if Option(value.partition).isDefined && Option(value.offset).isDefined => ("X-Nakadi-Cursors", - Some( - "[{\"partition\":\"" + value.partition + "\", \"offset\": \"" + value.offset + "\"}]")) + Some("[{\"partition\":\"" + value.partition + "\", \"offset\": \"" + value.offset + "\"}]")) case None => ("X-Nakadi-Cursors", None) } val parameters = List(nakadiCursor, ("X-Flow-Id", flowId)) - for { (key, optional) <- parameters; value <- optional } yield - RawHeader(key, value) + for { + (key, optional) <- parameters; value <- optional + } yield RawHeader(key, value) } } diff --git a/client/src/main/scala/org/zalando/nakadi/client/scala/MessageHandler.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/MessageHandler.scala index b5b3e9a..e4ef42e 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/scala/MessageHandler.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/scala/MessageHandler.scala @@ -26,29 +26,27 @@ case class ErrorResult(error: Throwable) */ trait EventHandler { def id(): String - def handleOnReceive(eventTypeName: String, - msg: String): Either[ErrorResult, Cursor] + def handleOnReceive(eventTypeName: String, msg: String): Either[ErrorResult, Cursor] def handleOnSubscribed(endpoint: String, cursor: Option[Cursor]): Unit - def handleOnError(eventTypeName: String, - msg: Option[String], - exception: Throwable) + def handleOnError(eventTypeName: String, msg: Option[String], exception: Throwable) } -class ScalaEventHandlerImpl[S <: Event](des: Deserializer[EventStreamBatch[S]], - listener: Listener[S]) +class ScalaEventHandlerImpl[S <: Event](des: Deserializer[EventStreamBatch[S]], listener: Listener[S]) extends EventHandler { private val log = LoggerFactory.getLogger(this.getClass) - private def createException(msg: String) = new IllegalStateException(msg) + private def createException(msg: String) = + new IllegalStateException(msg) checkArgument(listener != null, "Listener must not be null", null) checkArgument(des != null, "Deserializer must not be null", null) def id: String = listener.id - def handleOnReceive(eventTypeName: String, - msg: String): Either[ErrorResult, Cursor] = { + def handleOnReceive(eventTypeName: String, msg: String): Either[ErrorResult, Cursor] = { Try(des.from(msg)) match { - case Success(EventStreamBatch(cursor, None)) => Right(cursor) - case Success(EventStreamBatch(cursor, Some(Nil))) => Right(cursor) + case Success(EventStreamBatch(cursor, None)) => + Right(cursor) + case Success(EventStreamBatch(cursor, Some(Nil))) => + Right(cursor) case Success(EventStreamBatch(cursor, Some(events))) => listener.onReceive(eventTypeName, cursor, events) Right(cursor) @@ -63,10 +61,8 @@ class ScalaEventHandlerImpl[S <: Event](des: Deserializer[EventStreamBatch[S]], } - def handleOnError(eventTypeName: String, - msg: Option[String], - exception: Throwable) = { - val errorMsg = if (msg.isDefined) msg.get else exception.getMessage + def handleOnError(eventTypeName: String, msg: Option[String], exception: Throwable) = { + val errorMsg = if (msg.isDefined) msg.get else exception.getMessage val clientError = Some(ClientError(errorMsg, exception = Some(exception))) listener.onError(errorMsg, clientError) } @@ -74,25 +70,21 @@ class ScalaEventHandlerImpl[S <: Event](des: Deserializer[EventStreamBatch[S]], listener.onSubscribed(endpoint, cursor) } -class JavaEventHandlerImpl[J <: JEvent]( - des: Deserializer[JEventStreamBatch[J]], - listener: JListener[J]) +class JavaEventHandlerImpl[J <: JEvent](des: Deserializer[JEventStreamBatch[J]], listener: JListener[J]) extends EventHandler { private val log = LoggerFactory.getLogger(this.getClass) - private def createException(msg: String) = new IllegalStateException(msg) + private def createException(msg: String) = + new IllegalStateException(msg) checkArgument(listener != null, "Listener must not be null", null) checkArgument(des != null, "Deserializer must not be null", null) def id: String = listener.getId - def handleOnReceive(eventTypeName: String, - msg: String): Either[ErrorResult, Cursor] = { + def handleOnReceive(eventTypeName: String, msg: String): Either[ErrorResult, Cursor] = { Try(des.from(msg)) match { - case Success(eventBatch) - if (eventBatch.getEvents != null && !eventBatch.getEvents - .isEmpty()) => + case Success(eventBatch) if (eventBatch.getEvents != null && !eventBatch.getEvents.isEmpty()) => val Some(sCursor: Cursor) = toScalaCursor(eventBatch.getCursor) - val jcursor: JCursor = eventBatch.getCursor + val jcursor: JCursor = eventBatch.getCursor listener.onReceive(eventTypeName, jcursor, eventBatch.getEvents) Right(sCursor) case Success(eventBatch) => @@ -108,10 +100,8 @@ class JavaEventHandlerImpl[J <: JEvent]( } } - def handleOnError(eventTypeName: String, - msg: Option[String], - exception: Throwable) = { - val errorMsg = if (msg.isDefined) msg.get else exception.getMessage + def handleOnError(eventTypeName: String, msg: Option[String], exception: Throwable) = { + val errorMsg = if (msg.isDefined) msg.get else exception.getMessage val clientError = Some(ClientError(errorMsg, exception = Some(exception))) listener.onError(errorMsg, toJavaClientError(clientError)) } diff --git a/client/src/main/scala/org/zalando/nakadi/client/scala/model/ScalaJacksonJsonMarshaller.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/model/ScalaJacksonJsonMarshaller.scala index b3eef0d..5bcf013 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/scala/model/ScalaJacksonJsonMarshaller.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/scala/model/ScalaJacksonJsonMarshaller.scala @@ -25,21 +25,29 @@ object ScalaJacksonJsonMarshaller { val logger = LoggerFactory.getLogger(this.getClass) // All TypeReferences - implicit def problemTR: TypeReference[Problem] = new TypeReference[Problem] {} + implicit def problemTR: TypeReference[Problem] = + new TypeReference[Problem] {} implicit val metricsTR: TypeReference[Metrics] = new TypeReference[Metrics] {} - implicit def partitionTR: TypeReference[Partition] = new TypeReference[Partition] {} - implicit def cursorTR: TypeReference[Cursor] = new TypeReference[Cursor] {} - implicit def eventTypeSchemaTR: TypeReference[EventTypeSchema] = new TypeReference[EventTypeSchema] {} - implicit def partitionResolutionStrategyTR: TypeReference[PartitionStrategy.Value] = new TypeReference[PartitionStrategy.Value] {} - implicit def eventEnrichmentStrategyTR: TypeReference[EventEnrichmentStrategy.Value] = new TypeReference[EventEnrichmentStrategy.Value] {} - implicit def eventTypeCategoryTR: TypeReference[EventTypeCategory.Value] = new TypeReference[EventTypeCategory.Value] {} + implicit def partitionTR: TypeReference[Partition] = + new TypeReference[Partition] {} + implicit def cursorTR: TypeReference[Cursor] = + new TypeReference[Cursor] {} + implicit def eventTypeSchemaTR: TypeReference[EventTypeSchema] = + new TypeReference[EventTypeSchema] {} + implicit def partitionResolutionStrategyTR: TypeReference[PartitionStrategy.Value] = + new TypeReference[PartitionStrategy.Value] {} + implicit def eventEnrichmentStrategyTR: TypeReference[EventEnrichmentStrategy.Value] = + new TypeReference[EventEnrichmentStrategy.Value] {} + implicit def eventTypeCategoryTR: TypeReference[EventTypeCategory.Value] = + new TypeReference[EventTypeCategory.Value] {} implicit def dataChangeEventQualifierTR: TypeReference[DataChangeEventQualifier] = new TypeReference[DataChangeEventQualifier] {} implicit def eventTypeStatisticsTR: TypeReference[EventTypeStatistics] = new TypeReference[EventTypeStatistics] {} implicit def eventTypeTR: TypeReference[EventType] = new TypeReference[EventType] {} - implicit def eventTR: TypeReference[Event] = new TypeReference[Event] {} + implicit def eventTR: TypeReference[Event] = + new TypeReference[Event] {} implicit def eventStreamBatchTR: TypeReference[EventStreamBatch[_]] = new TypeReference[EventStreamBatch[_]] {} @@ -70,9 +78,11 @@ object ScalaJacksonJsonMarshaller { } } - implicit def serializer[T]: Serializer[T] = new Serializer[T] { - def to(from: T): String = defaultObjectMapper.writeValueAsString(from) - } + implicit def serializer[T]: Serializer[T] = + new Serializer[T] { + def to(from: T): String = + defaultObjectMapper.writeValueAsString(from) + } implicit def deserializer[T](implicit expectedType: TypeReference[T]): Deserializer[T] = new Deserializer[T] { @@ -100,7 +110,7 @@ object ScalaJacksonJsonMarshaller { beanOrClass: AnyRef, propertyName: String): Boolean = { logger.warn( - s"unknown property occurred in JSON representation: [beanOrClass=$beanOrClass, property=$propertyName]") + s"unknown property occurred in JSON representation: [beanOrClass=$beanOrClass, property=$propertyName]") true } }) diff --git a/client/src/main/scala/org/zalando/nakadi/client/utils/ClientBuilder.scala b/client/src/main/scala/org/zalando/nakadi/client/utils/ClientBuilder.scala index bd71178..979a720 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/utils/ClientBuilder.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/utils/ClientBuilder.scala @@ -15,11 +15,7 @@ object ClientBuilder { tokenProvider: Option[() => String] = None, securedConnection: Boolean = true, verifySSlCertificate: Boolean = true) = - new ClientBuilder(host, - port, - tokenProvider, - securedConnection, - verifySSlCertificate) + new ClientBuilder(host, port, tokenProvider, securedConnection, verifySSlCertificate) private val DEFAULT_PORT = 443 } @@ -29,27 +25,16 @@ class ClientBuilder private (host: String = null, // tokenProvider: Option[() => String] = None, // securedConnection: Boolean = true, // verifySSlCertificate: Boolean = true) { - def this() = this(null, ClientBuilder.DEFAULT_PORT, None, true, true) + def this() = + this(null, ClientBuilder.DEFAULT_PORT, None, true, true) def withHost(host: String): ClientBuilder = - new ClientBuilder(checkNotNull(host), - port, - tokenProvider, - securedConnection, - verifySSlCertificate) + new ClientBuilder(checkNotNull(host), port, tokenProvider, securedConnection, verifySSlCertificate) def withPort(port: Int): ClientBuilder = - new ClientBuilder(host, - port, - tokenProvider, - securedConnection, - verifySSlCertificate) + new ClientBuilder(host, port, tokenProvider, securedConnection, verifySSlCertificate) def withTokenProvider(tokenProvider: Option[() => String]): ClientBuilder = - new ClientBuilder(host, - port, - tokenProvider, - securedConnection, - verifySSlCertificate) + new ClientBuilder(host, port, tokenProvider, securedConnection, verifySSlCertificate) def withTokenProvider4Java(tokenProvider: Supplier[String]): ClientBuilder = withTokenProvider { @@ -61,33 +46,17 @@ class ClientBuilder private (host: String = null, // } def withSecuredConnection(securedConnection: Boolean = true): ClientBuilder = - new ClientBuilder(host, - port, - tokenProvider, - checkNotNull(securedConnection), - verifySSlCertificate) + new ClientBuilder(host, port, tokenProvider, checkNotNull(securedConnection), verifySSlCertificate) - def withVerifiedSslCertificate( - verifySSlCertificate: Boolean = true): ClientBuilder = - new ClientBuilder(host, - port, - tokenProvider, - securedConnection, - checkNotNull(verifySSlCertificate)) + def withVerifiedSslCertificate(verifySSlCertificate: Boolean = true): ClientBuilder = + new ClientBuilder(host, port, tokenProvider, securedConnection, checkNotNull(verifySSlCertificate)) def build(): Client = - Connection.newClient(host, - port, - tokenProvider, - securedConnection, - verifySSlCertificate) + Connection.newClient(host, port, tokenProvider, securedConnection, verifySSlCertificate) def buildJavaClient(): JClient = { - val connection = Connection.newClientHandler4Java(host, - port, - tokenProvider, - securedConnection, - verifySSlCertificate) + val connection = + Connection.newClientHandler4Java(host, port, tokenProvider, securedConnection, verifySSlCertificate) new JClientImpl(connection) } diff --git a/client/src/main/scala/org/zalando/nakadi/client/utils/FutureConversions.scala b/client/src/main/scala/org/zalando/nakadi/client/utils/FutureConversions.scala index 1a62f0e..03f7965 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/utils/FutureConversions.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/utils/FutureConversions.scala @@ -11,49 +11,46 @@ object FutureConversions { // private def extractEither[T](either: Either[String, T]): T = either match { case Left(error) => throw new RuntimeException(error) - case Right(t) => t + case Right(t) => t } - def fromOption2Optional[T](in: scala.concurrent.Future[Option[T]]) - : java.util.concurrent.Future[Optional[T]] = { + def fromOption2Optional[T](in: scala.concurrent.Future[Option[T]]): java.util.concurrent.Future[Optional[T]] = { new MFuture[Option[T], Optional[T]](in, a => fromOptional2Optional(a)) } - def fromOption2Void[T](in: scala.concurrent.Future[Option[T]]) - : java.util.concurrent.Future[Void] = { + def fromOption2Void[T](in: scala.concurrent.Future[Option[T]]): java.util.concurrent.Future[Void] = { new MFuture[Option[T], Void](in, a => null) } - def fromFuture2Future[T]( - in: scala.concurrent.Future[T]): java.util.concurrent.Future[T] = { + def fromFuture2Future[T](in: scala.concurrent.Future[T]): java.util.concurrent.Future[T] = { new MFuture[T, T](in, a => a) } - def fromFuture2FutureVoid[T]( - in: scala.concurrent.Future[T]): java.util.concurrent.Future[Void] = { + def fromFuture2FutureVoid[T](in: scala.concurrent.Future[T]): java.util.concurrent.Future[Void] = { new MFuture[T, Void](in, a => null) } private def fromSequenceToList[T](in: Seq[T]): Optional[java.util.List[T]] = in match { case Nil => Optional.empty() - case seq => Optional.of(new java.util.ArrayList[T](seq)) + case seq => + Optional.of(new java.util.ArrayList[T](seq)) } private def fromOptional2Optional[R](in: Option[R]): Optional[R] = in match { case Some(value) => Optional.of(value) - case None => Optional.empty() + case None => Optional.empty() } - private def convert[T](x: scala.concurrent.Future[Either[String, T]]) - : java.util.concurrent.Future[T] = + private def convert[T](x: scala.concurrent.Future[Either[String, T]]): java.util.concurrent.Future[T] = new MFuture[Either[String, T], T](x, a => extractEither(a)) } -private class MFuture[A, B](f: scala.concurrent.Future[A], converter: A => B) - extends java.util.concurrent.Future[B] { - override def isCancelled: Boolean = throw new UnsupportedOperationException +private class MFuture[A, B](f: scala.concurrent.Future[A], converter: A => B) extends java.util.concurrent.Future[B] { + override def isCancelled: Boolean = + throw new UnsupportedOperationException - override def get(): B = converter.apply(Await.result(f, Duration.Inf)) + override def get(): B = + converter.apply(Await.result(f, Duration.Inf)) override def get(timeout: Long, unit: TimeUnit): B = converter.apply(Await.result(f, Duration.create(timeout, unit))) diff --git a/client/src/main/scala/org/zalando/nakadi/client/utils/GeneralConversions.scala b/client/src/main/scala/org/zalando/nakadi/client/utils/GeneralConversions.scala index 7b1aa7d..cce578f 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/utils/GeneralConversions.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/utils/GeneralConversions.scala @@ -6,11 +6,12 @@ import scala.language.implicitConversions object GeneralConversions { - def toOptional[T](in: Option[T]): Optional[T] = in match { - case None => Optional.empty() - case Some(value) => Optional.of(value) + def toOptional[T](in: Option[T]): Optional[T] = + in match { + case None => Optional.empty() + case Some(value) => Optional.of(value) - } + } def toOption[T](in: Optional[T]): Option[T] = if (in.isPresent()) { Option(in.get) @@ -21,6 +22,7 @@ object GeneralConversions { def toOptionOfSeq[T](in: Option[Seq[T]]): Optional[java.util.List[T]] = in match { case None => Optional.empty() - case Some(seq: Seq[T]) => Optional.of(seqAsJavaList(seq)) + case Some(seq: Seq[T]) => + Optional.of(seqAsJavaList(seq)) } } diff --git a/client/src/main/scala/org/zalando/nakadi/client/utils/ModelConverter.scala b/client/src/main/scala/org/zalando/nakadi/client/utils/ModelConverter.scala index c8fa09c..46b0da9 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/utils/ModelConverter.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/utils/ModelConverter.scala @@ -25,64 +25,61 @@ object ModelConverter { None } - def toScalaCursor(in: JCursor): Option[Cursor] = Option(in) match { - case None => None - case Some(c) => Some(Cursor(c.getPartition, c.getOffset)) - } + def toScalaCursor(in: JCursor): Option[Cursor] = + Option(in) match { + case None => None + case Some(c) => + Some(Cursor(c.getPartition, c.getOffset)) + } - def toScalaStreamParameters( - in: Optional[JStreamParameters]): Option[StreamParameters] = + def toScalaStreamParameters(in: Optional[JStreamParameters]): Option[StreamParameters] = if (in.isPresent()) { toScalaStreamParameters(in.get) } else { None } - def toScalaStreamParameters( - in: JStreamParameters): Option[StreamParameters] = Option(in) match { - case None => None - case Some(c) => - Some( - StreamParameters( - cursor = toScalaCursor(c.getCursor), - batchLimit = toOption(c.getBatchLimit), - streamLimit = toOption(c.getStreamLimit), - batchFlushTimeout = toOption(c.getBatchFlushTimeout), - streamTimeout = toOption(c.getStreamTimeout), - streamKeepAliveLimit = toOption(c.getStreamKeepAliveLimit), - flowId = toOption(c.getFlowId))) - } + def toScalaStreamParameters(in: JStreamParameters): Option[StreamParameters] = + Option(in) match { + case None => None + case Some(c) => + Some( + StreamParameters(cursor = toScalaCursor(c.getCursor), + batchLimit = toOption(c.getBatchLimit), + streamLimit = toOption(c.getStreamLimit), + batchFlushTimeout = toOption(c.getBatchFlushTimeout), + streamTimeout = toOption(c.getStreamTimeout), + streamKeepAliveLimit = toOption(c.getStreamKeepAliveLimit), + flowId = toOption(c.getFlowId))) + } def toJavaCursor(in: Cursor): JCursor = in match { case Cursor(partition, offset) => new JCursor(partition, offset) case null => null } - def toJavaCursor(in: Option[Cursor]): Optional[JCursor] = in match { - case Some(Cursor(partition, offset)) => - Optional.of(new JCursor(partition, offset)) - case None => Optional.empty() - } + def toJavaCursor(in: Option[Cursor]): Optional[JCursor] = + in match { + case Some(Cursor(partition, offset)) => + Optional.of(new JCursor(partition, offset)) + case None => Optional.empty() + } def toScalaCursor[T <: JEvent](in: JEventStreamBatch[T]): Option[Cursor] = Option(in) match { - case None => None + case None => None case Some(in) => toScalaCursor(in.getCursor) } - def toJavaEvents[T <: JEvent]( - in: JEventStreamBatch[T]): Option[java.util.List[T]] = Option(in) match { - case None => None + def toJavaEvents[T <: JEvent](in: JEventStreamBatch[T]): Option[java.util.List[T]] = Option(in) match { + case None => None case Some(in) => Option(in.getEvents) } def toJavaClientError(error: Option[ClientError]): Optional[JClientError] = error match { case Some(ClientError(msg, httpStatusCodeOpt, exceptionOpt)) => - Optional.of( - new JClientError(msg, - toOptional(httpStatusCodeOpt), - toOptional(exceptionOpt))) + Optional.of(new JClientError(msg, toOptional(httpStatusCodeOpt), toOptional(exceptionOpt))) case None => Optional.empty() } diff --git a/client/src/main/scala/org/zalando/nakadi/client/utils/Uri.scala b/client/src/main/scala/org/zalando/nakadi/client/utils/Uri.scala index 59f2813..148be90 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/utils/Uri.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/utils/Uri.scala @@ -5,21 +5,26 @@ object Uri { final def URI_METRICS = "/metrics" /*Events*/ - final def URI_EVENT_TYPES = "/event-types" + final def URI_EVENT_TYPES = "/event-types" final def URI_EVENT_TYPE_BY_NAME = "/event-types/%s" final def getEventTypeByName(eventTypeName: String) = s"/event-types/$eventTypeName" - final def URI_EVENTS_OF_EVENT_TYPE() = "/event-types/%s/events" + final def URI_EVENTS_OF_EVENT_TYPE() = + "/event-types/%s/events" final def getEventStreamingUri(eventTypeName: String) = s"/event-types/$eventTypeName/events" /*Partitions*/ - final def URI_PARTITIONS_BY_EVENT_TYPE = "/event-types/%s/partitions" + final def URI_PARTITIONS_BY_EVENT_TYPE = + "/event-types/%s/partitions" final def getPartitions(eventTypeName: String) = s"/event-types/$eventTypeName/partitions" /*Strategies*/ - final def URI_VALIDATION_STRATEGIES = "/registry/validation-strategies" - final def URI_ENRICHMENT_STRATEGIES = "/registry/enrichment-strategies" - final def URI_PARTITIONING_STRATEGIES = "/registry/partition-strategies" + final def URI_VALIDATION_STRATEGIES = + "/registry/validation-strategies" + final def URI_ENRICHMENT_STRATEGIES = + "/registry/enrichment-strategies" + final def URI_PARTITIONING_STRATEGIES = + "/registry/partition-strategies" } diff --git a/project/Build.scala b/project/Build.scala index 604ef70..91e43af 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -67,7 +67,7 @@ lazy val client = withDefaults( scalaVersion := "2.11.8", findbugsReportType := Some(ReportType.FancyHtml), findbugsReportPath := Some(target.value / "findbugs-report.html"), - scalafmtConfig := Some(baseDirectory.value / ".scalafmt"), + scalafmtConfig := Some(baseDirectory.value / "../.scalafmt"), publishTo := whereToPublishTo(isSnapshot.value), resolvers += Resolver.mavenLocal, resolvers += "Maven Central Server" at "http://repo1.maven.org/maven2", From e6a7a3e76ff1748c85eaf272bb126e9bc1a5196a Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Fri, 8 Jul 2016 17:27:08 +0200 Subject: [PATCH 129/183] techjira:LAAS-60 EnrichmentStrategies & PartitionStrategies can now be retrieved in scala. Fixes #70 --- .../nakadi/client/java/ClientImpl.java | 15 +++-- .../nakadi/client/scala/ClientImpl.scala | 62 ++++++++++++------- .../model/ScalaJacksonJsonMarshaller.scala | 59 ++++++++++-------- .../scala/SerializerDeserializerTest.scala | 1 - .../client/scala/ClientIntegrationTest.scala | 27 ++++---- 5 files changed, 95 insertions(+), 69 deletions(-) diff --git a/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java b/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java index 204bd88..e9fd64b 100644 --- a/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java +++ b/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java @@ -80,7 +80,8 @@ public Future publishEvent(String eventTypeName, T event } @Override - public Future publishEvents(String eventTypeName, List events, Serializer> serializer) { + public Future publishEvents(String eventTypeName, List events, + Serializer> serializer) { return handler.post(Uri.getEventStreamingUri(eventTypeName), events, serializer); } @@ -111,13 +112,17 @@ public void stop() { } @Override - public Optional subscribe(String eventTypeName, StreamParameters parameters, Listener listener, Deserializer> deserializer) { - return handler.subscribe(eventTypeName, Uri.getEventStreamingUri(eventTypeName), parameters, listener, deserializer); + public Optional subscribe(String eventTypeName, StreamParameters parameters, + Listener listener, Deserializer> deserializer) { + return handler.subscribe(eventTypeName, Uri.getEventStreamingUri(eventTypeName), parameters, listener, + deserializer); } @Override - public Optional subscribe(String eventTypeName, StreamParameters parameters, Listener listener, TypeReference> typeRef) { - return handler.subscribe(eventTypeName, Uri.getEventStreamingUri(eventTypeName), parameters, listener, SerializationUtils.withCustomDeserializer(typeRef)); + public Optional subscribe(String eventTypeName, StreamParameters parameters, + Listener listener, TypeReference> typeRef) { + return handler.subscribe(eventTypeName, Uri.getEventStreamingUri(eventTypeName), parameters, listener, + SerializationUtils.withCustomDeserializer(typeRef)); } @Override diff --git a/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala index 87a9a3a..d03ee28 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala @@ -29,6 +29,7 @@ import akka.http.scaladsl.model.StatusCodes import akka.http.scaladsl.unmarshalling.Unmarshal import com.fasterxml.jackson.module.scala.JsonScalaEnumeration import org.zalando.nakadi.client.scala.model.EventEnrichmentStrategyType +import org.zalando.nakadi.client.scala.model.PartitionStrategyType class ClientImpl(connection: Connection, subscriber: SubscriptionHandler, charSet: String = "UTF-8") extends Client { import Uri._ @@ -52,7 +53,7 @@ class ClientImpl(connection: Connection, subscriber: SubscriptionHandler, charSe def getEventType(name: String): Future[Either[ClientError, Option[EventType]]] = { logFutureEither( - connection.get(URI_EVENT_TYPE_BY_NAME.format(name)).flatMap(in => mapToEither(in)(deserializer(eventTypeTR)))) + connection.get(URI_EVENT_TYPE_BY_NAME.format(name)).flatMap(in => mapToEither(in)(deserializer(eventTypeTR)))) } def updateEventType(name: String, eventType: EventType): Future[Option[ClientError]] = { @@ -68,30 +69,43 @@ class ClientImpl(connection: Connection, subscriber: SubscriptionHandler, charSe events: Seq[T], ser: Serializer[Seq[T]]): Future[Option[ClientError]] = { logFutureOption( - connection.post(URI_EVENTS_OF_EVENT_TYPE.format(eventTypeName), events).flatMap(in => mapToOption(in))) + connection.post(URI_EVENTS_OF_EVENT_TYPE.format(eventTypeName), events).flatMap(in => mapToOption(in))) } def publishEvents[T <: Event](eventTypeName: String, events: Seq[T]): Future[Option[ClientError]] = { logFutureOption( - connection.post(URI_EVENTS_OF_EVENT_TYPE.format(eventTypeName), events).flatMap(in => mapToOption(in))) + connection.post(URI_EVENTS_OF_EVENT_TYPE.format(eventTypeName), events).flatMap(in => mapToOption(in))) } def getPartitions(name: String): Future[Either[ClientError, Option[Seq[Partition]]]] = { logFutureEither( - connection - .get(URI_PARTITIONS_BY_EVENT_TYPE.format(name)) - .flatMap(in => mapToEither(in)(deserializer(listOfPartitionTR)))) + connection + .get(URI_PARTITIONS_BY_EVENT_TYPE.format(name)) + .flatMap(in => mapToEither(in)(deserializer(listOfPartitionTR)))) } - def getEnrichmentStrategies(): Future[Either[ClientError, Option[Seq[EventEnrichmentStrategy.Value]]]] = { + + def getPartitioningStrategies(): Future[Either[ClientError, Option[Seq[PartitionStrategy.Value]]]] ={ + val transformer = getTranformer(PartitionStrategy) logFutureEither( - connection - .get(URI_ENRICHMENT_STRATEGIES) - .flatMap(mapToEither(_)(deserializer(listOfEventEnrichmentStrategyTR)))) + connection.get(URI_PARTITIONING_STRATEGIES).flatMap(mapToEither(_)(deserializer(listOfStringsTR,transformer)))) } - def getPartitioningStrategies(): Future[Either[ClientError, Option[Seq[PartitionStrategy.Value]]]] = + + private def getTranformer(enum:Enumeration)={ + (in:Seq[String]) => { + in.map { x => enum.withName(x) } + } + } + + def getEnrichmentStrategies(): Future[Either[ClientError, Option[Seq[EventEnrichmentStrategy.EventEnrichmentStrategy]]]] = { + val tranformer = getTranformer(EventEnrichmentStrategy) logFutureEither( - connection.get(URI_PARTITIONING_STRATEGIES).flatMap(mapToEither(_)(deserializer(listOfPartitionStrategyTR)))) + connection + .get(URI_ENRICHMENT_STRATEGIES) + .flatMap(mapToEither(_)(deserializer(listOfStringsTR, tranformer)))) + } + + def stop(): Option[ClientError] = { materializer.shutdown() @@ -107,7 +121,7 @@ class ClientImpl(connection: Connection, subscriber: SubscriptionHandler, charSe } def subscribe[T <: Event](eventTypeName: String, params: StreamParameters, listener: Listener[T])( - implicit des: Deserializer[EventStreamBatch[T]]): Option[ClientError] = + implicit des: Deserializer[EventStreamBatch[T]]): Option[ClientError] = (eventTypeName, params, listener) match { case (_, _, listener) if listener == null => @@ -121,12 +135,12 @@ class ClientImpl(connection: Connection, subscriber: SubscriptionHandler, charSe case (eventType, StreamParameters(cursor, _, _, _, _, _, _), listener) if Option(eventType).isDefined => val url = URI_EVENTS_OF_EVENT_TYPE.format(eventType) logger.debug("Subscribing listener {} - cursor {} - parameters {} - eventType {} - url {}", - listener.id, - cursor, - params, - eventType, - url) - val finalUrl = withUrl(url, Some(params)) + listener.id, + cursor, + params, + eventType, + url) + val finalUrl = withUrl(url, Some(params)) val eventHandler: EventHandler = new ScalaEventHandlerImpl(des, listener) subscriber.subscribe(eventTypeName, finalUrl, cursor, eventHandler) } @@ -159,7 +173,7 @@ class ClientImpl(connection: Connection, subscriber: SubscriptionHandler, charSe } private def mapToEither[T](response: HttpResponse)( - implicit deserializer: Deserializer[T]): Future[Either[ClientError, Option[T]]] = { + implicit deserializer: Deserializer[T]): Future[Either[ClientError, Option[T]]] = { logger.debug("received [response={}]", response) response match { case HttpResponse(status, headers, entity, protocol) if (status.isSuccess()) => @@ -196,15 +210,17 @@ class ClientImpl(connection: Connection, subscriber: SubscriptionHandler, charSe logger.info(s"Success. http-status: ${status.intValue()}") response.entity.toStrict(10.second).map { body => logger.debug("Success - http-status: %s, body:[%s]".format(status.intValue().toString(), - body.data.decodeString(charSet))) + body.data.decodeString(charSet))) } Future.successful(None) case status if (status.isRedirection()) => val msg = s"Redirection - http-status: ${status.intValue()}, reason[${status.reason()}]" logger.info(msg) response.entity.toStrict(10.second).map { body => - logger.debug(s"Redirection - http-status: ${status.intValue().toString()}, reason[${status - .reason()}], body:[${body.data.decodeString(charSet)}]") + logger.debug(s"Redirection - http-status: ${status.intValue().toString()}, reason[${ + status + .reason() + }], body:[${body.data.decodeString(charSet)}]") } Future.successful(Option(ClientError(msg, Some(status.intValue())))) case status if (status.isFailure()) => diff --git a/client/src/main/scala/org/zalando/nakadi/client/scala/model/ScalaJacksonJsonMarshaller.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/model/ScalaJacksonJsonMarshaller.scala index 5bcf013..245d025 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/scala/model/ScalaJacksonJsonMarshaller.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/scala/model/ScalaJacksonJsonMarshaller.scala @@ -5,8 +5,8 @@ import org.zalando.nakadi.client.Deserializer import org.zalando.nakadi.client.Serializer import com.fasterxml.jackson.annotation.JsonInclude -import com.fasterxml.jackson.core.JsonFactory import com.fasterxml.jackson.core.JsonParser +import com.fasterxml.jackson.core.Version import com.fasterxml.jackson.core.`type`.TypeReference import com.fasterxml.jackson.databind.DeserializationContext import com.fasterxml.jackson.databind.DeserializationFeature @@ -15,11 +15,8 @@ import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.databind.PropertyNamingStrategy import com.fasterxml.jackson.databind.SerializationFeature import com.fasterxml.jackson.databind.deser.DeserializationProblemHandler +import com.fasterxml.jackson.databind.module.SimpleModule import com.fasterxml.jackson.module.scala.DefaultScalaModule -import com.fasterxml.jackson.module.scala.IteratorModule -import com.fasterxml.jackson.module.scala.OptionModule -import com.fasterxml.jackson.module.scala.SeqModule -import com.fasterxml.jackson.module.scala.MapModule object ScalaJacksonJsonMarshaller { val logger = LoggerFactory.getLogger(this.getClass) @@ -69,6 +66,7 @@ object ScalaJacksonJsonMarshaller { new TypeReference[Seq[EventType]] {} implicit def listOfPartitionTR: TypeReference[Seq[Partition]] = new TypeReference[Seq[Partition]] {} + implicit def listOfStringsTR: TypeReference[Seq[String]] = new TypeReference[Seq[String]] {} implicit def optionalDeserializer[T](implicit expectedType: TypeReference[T]): Deserializer[Option[T]] = new Deserializer[Option[T]] { @@ -89,30 +87,37 @@ object ScalaJacksonJsonMarshaller { def from(from: String): T = defaultObjectMapper.readValue[T](from, expectedType) } + + + def deserializer[T, B](expectedType: TypeReference[T], tranformer: T => B): Deserializer[B] = + new Deserializer[B] { + def from(from: String): B = { + tranformer(defaultObjectMapper.readValue[T](from, expectedType)) + } - lazy val defaultObjectMapper: ObjectMapper = new ObjectMapper() { - private val module = new OptionModule with MapModule with SeqModule with IteratorModule - - this.registerModule(module) + } - } // - .registerModule(new DefaultScalaModule) - .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) - .configure(DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT, true) - .configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true) - .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) - .setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE) - .setSerializationInclusion(JsonInclude.Include.NON_EMPTY) - .addHandler(new DeserializationProblemHandler() { - override def handleUnknownProperty(ctxt: DeserializationContext, - jp: JsonParser, - deserializer: JsonDeserializer[_], - beanOrClass: AnyRef, - propertyName: String): Boolean = { - logger.warn( + lazy val defaultObjectMapper: ObjectMapper = { + val scalaModule = new DefaultScalaModule + new ObjectMapper() + .registerModule(scalaModule) + .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) + .configure(DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT, true) + .configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true) + .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE) + .setSerializationInclusion(JsonInclude.Include.NON_EMPTY) + .addHandler(new DeserializationProblemHandler() { + override def handleUnknownProperty(ctxt: DeserializationContext, + jp: JsonParser, + deserializer: JsonDeserializer[_], + beanOrClass: AnyRef, + propertyName: String): Boolean = { + logger.warn( s"unknown property occurred in JSON representation: [beanOrClass=$beanOrClass, property=$propertyName]") - true - } - }) + true + } + }) + } } diff --git a/client/src/test/scala/org/zalando/nakadi/client/scala/SerializerDeserializerTest.scala b/client/src/test/scala/org/zalando/nakadi/client/scala/SerializerDeserializerTest.scala index 4cf1785..86908b6 100644 --- a/client/src/test/scala/org/zalando/nakadi/client/scala/SerializerDeserializerTest.scala +++ b/client/src/test/scala/org/zalando/nakadi/client/scala/SerializerDeserializerTest.scala @@ -22,7 +22,6 @@ import org.zalando.nakadi.client.Serializer class SerializerDeserializerTest extends WordSpec with Matchers with AkkaConfig { import ScalaJacksonJsonMarshaller._ import TestScalaEntity._ - "When an entity(scala object) is marshalled and unmarshalled it" should { val testName = "always result in the same entity" s"$testName(eventMetadata)" in { diff --git a/it/src/test/scala/org/zalando/nakadi/client/scala/ClientIntegrationTest.scala b/it/src/test/scala/org/zalando/nakadi/client/scala/ClientIntegrationTest.scala index c8dc082..e21c3c8 100644 --- a/it/src/test/scala/org/zalando/nakadi/client/scala/ClientIntegrationTest.scala +++ b/it/src/test/scala/org/zalando/nakadi/client/scala/ClientIntegrationTest.scala @@ -33,7 +33,7 @@ class ClientIntegrationTest extends WordSpec with Matchers with BeforeAndAfterAl metricsOpt.isDefined shouldBe true val Some(metrics) = metricsOpt println(metrics) - metrics.version shouldNot be (null) + metrics.version shouldNot be(null) metrics.gauges.size should be > 0 } @@ -45,18 +45,19 @@ class ClientIntegrationTest extends WordSpec with Matchers with BeforeAndAfterAl val Some(eventTypes) = eventTypesOpt eventTypes.size should (equal(0) or (be > 0)) } - -// "GET /registry/partitions-strategies" in { -// val result = Await.result(client.getPartitioningStrategies(), 10.seconds) -// result.isRight shouldBe true -// } - -// "GET /registry/enrichment-strategies" in { -// val result = Await.result(client.getEnrichmentStrategies(), 10.seconds) -// result.isRight shouldBe true -// } -// - + + "GET /registry/partitions-strategies" in { + val result = Await.result(client.getPartitioningStrategies(), 10.seconds) + result.isRight shouldBe true + } + + "GET /registry/enrichment-strategies" in { + val result = Await.result(client.getEnrichmentStrategies(), 10.seconds) + result.isRight shouldBe true + val Right(strategies)=result + strategies.isDefined shouldBe true + + } } From f5d8dc586a8ad681d8fdb983bb5a2c72576891db Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Mon, 11 Jul 2016 17:11:23 +0200 Subject: [PATCH 130/183] techjira:LAAS-60 You can now delete event-types with the Java API. Fixes #81. --- .../nakadi/client/java/ClientImpl.java | 2 +- .../client/java/JavaClientHandler.scala | 57 +++++++++-------- .../client/java/ClientIntegrationTest.java | 63 +++++++++++++++---- .../client/scala/ClientIntegrationTest.scala | 46 ++++++++++++-- 4 files changed, 124 insertions(+), 44 deletions(-) diff --git a/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java b/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java index e9fd64b..ff4fba5 100644 --- a/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java +++ b/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java @@ -66,7 +66,7 @@ public Future updateEventType(String eventTypeName, EventType eventType) { @Override public Future deleteEventType(String eventTypeName) { - throw new NotImplementedException(); + return handler.delete(Uri.getEventTypeByName(eventTypeName)); } @Override diff --git a/client/src/main/scala/org/zalando/nakadi/client/java/JavaClientHandler.scala b/client/src/main/scala/org/zalando/nakadi/client/java/JavaClientHandler.scala index a22e3a6..667d3a0 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/java/JavaClientHandler.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/java/JavaClientHandler.scala @@ -12,17 +12,17 @@ import scala.util.Try import org.slf4j.LoggerFactory import org.zalando.nakadi.client.Deserializer import org.zalando.nakadi.client.Serializer -import org.zalando.nakadi.client.java.model.{Event => JEvent} -import org.zalando.nakadi.client.scala.model.{Cursor => ScalaCursor} -import org.zalando.nakadi.client.scala.{ClientImpl => SClientImpl} -import org.zalando.nakadi.client.java.model.{EventStreamBatch => JEventStreamBatch} -import org.zalando.nakadi.client.java.{StreamParameters => JStreamParameters} -import org.zalando.nakadi.client.java.{Listener => JListener} +import org.zalando.nakadi.client.java.model.{ Event => JEvent } +import org.zalando.nakadi.client.scala.model.{ Cursor => ScalaCursor } +import org.zalando.nakadi.client.scala.{ ClientImpl => SClientImpl } +import org.zalando.nakadi.client.java.model.{ EventStreamBatch => JEventStreamBatch } +import org.zalando.nakadi.client.java.{ StreamParameters => JStreamParameters } +import org.zalando.nakadi.client.java.{ Listener => JListener } import org.zalando.nakadi.client.scala.Connection import org.zalando.nakadi.client.scala.EmptyScalaEvent import org.zalando.nakadi.client.scala.EventHandler import org.zalando.nakadi.client.scala.HttpFactory -import org.zalando.nakadi.client.scala.{StreamParameters => ScalaStreamParameters} +import org.zalando.nakadi.client.scala.{ StreamParameters => ScalaStreamParameters } import org.zalando.nakadi.client.utils.FutureConversions import org.zalando.nakadi.client.utils.ModelConverter @@ -40,8 +40,8 @@ import scala.concurrent.Await import scala.concurrent.duration.Duration /** - * Handler for mapping(Java<->Scala) and handling http calls and listener subscriptions for the Java API. - */ + * Handler for mapping(Java<->Scala) and handling http calls and listener subscriptions for the Java API. + */ trait JavaClientHandler { def deserialize[T](response: HttpResponse, des: Deserializer[T]): Future[Optional[T]] def get[T](endpoint: String, des: Deserializer[T]): java.util.concurrent.Future[Optional[T]] @@ -49,11 +49,12 @@ trait JavaClientHandler { headers: Seq[HttpHeader], des: Deserializer[T]): java.util.concurrent.Future[Optional[T]] def post[T](endpoint: String, model: T)(implicit serializer: Serializer[T]): java.util.concurrent.Future[Void] + def delete[T](endpoint: String): java.util.concurrent.Future[Void] def subscribe[T <: JEvent]( - eventTypeName: String, - endpoint: String, - parameters: JStreamParameters, - listener: JListener[T])(implicit des: Deserializer[JEventStreamBatch[T]]): Optional[ClientError] + eventTypeName: String, + endpoint: String, + parameters: JStreamParameters, + listener: JListener[T])(implicit des: Deserializer[JEventStreamBatch[T]]): Optional[ClientError] def unsubscribe[T <: JEvent](eventTypeName: String, partition: Optional[String], listener: JListener[T]) def stop(): Unit @@ -87,9 +88,9 @@ class JavaClientHandlerImpl(val connection: Connection, subscriber: Subscription headers: Seq[HttpHeader], des: Deserializer[T]): java.util.concurrent.Future[Optional[T]] = { FutureConversions.fromFuture2Future( - connection - .executeCall(withHttpRequest(endpoint, HttpMethods.GET, headers, connection.tokenProvider, None)) - .flatMap(deserialize(_, des))) + connection + .executeCall(withHttpRequest(endpoint, HttpMethods.GET, headers, connection.tokenProvider, None)) + .flatMap(deserialize(_, des))) } def post[T](endpoint: String, model: T)(implicit serializer: Serializer[T]): java.util.concurrent.Future[Void] = { val entity = serializer.to(model) @@ -100,6 +101,10 @@ class JavaClientHandlerImpl(val connection: Connection, subscriber: Subscription .flatMap(response(_)) FutureConversions.fromOption2Void(result) } + def delete[T](endpoint: String): java.util.concurrent.Future[Void]={ + val result = connection.delete(endpoint) .flatMap(response(_)) + FutureConversions.fromOption2Void(result) + } private def serialize[T](model: T)(implicit serializer: Serializer[T]): String = Try(serializer.to(model)) match { @@ -131,9 +136,9 @@ class JavaClientHandlerImpl(val connection: Connection, subscriber: Subscription listener: JListener[T])(implicit des: Deserializer[JEventStreamBatch[T]]) = { import ModelConverter._ val params: Option[ScalaStreamParameters] = toScalaStreamParameters(parameters) - val eventHandler: EventHandler = new JavaEventHandlerImpl(des, listener) - val finalUrl = withUrl(endpoint, params) - val res = subscriber.subscribe(eventTypeName, finalUrl, getCursor(params), eventHandler) + val eventHandler: EventHandler = new JavaEventHandlerImpl(des, listener) + val finalUrl = withUrl(endpoint, params) + val res = subscriber.subscribe(eventTypeName, finalUrl, getCursor(params), eventHandler) toJavaClientError(res) } @@ -149,13 +154,13 @@ class JavaClientHandlerImpl(val connection: Connection, subscriber: Subscription private def getCursor(params: Option[ScalaStreamParameters]): Option[ScalaCursor] = params match { case Some( - ScalaStreamParameters(cursor, - batchLimit, - streamLimit, - batchFlushTimeout, - streamTimeout, - streamKeepAliveLimit, - flowId)) => + ScalaStreamParameters(cursor, + batchLimit, + streamLimit, + batchFlushTimeout, + streamTimeout, + streamKeepAliveLimit, + flowId)) => cursor case None => None } diff --git a/it/src/test/java/org/zalando/client/java/ClientIntegrationTest.java b/it/src/test/java/org/zalando/client/java/ClientIntegrationTest.java index b6a6dcf..cbac659 100644 --- a/it/src/test/java/org/zalando/client/java/ClientIntegrationTest.java +++ b/it/src/test/java/org/zalando/client/java/ClientIntegrationTest.java @@ -1,11 +1,14 @@ package org.zalando.client.java; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import java.util.List; import java.util.Optional; import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; import org.junit.After; import org.junit.Test; @@ -14,6 +17,9 @@ import org.zalando.nakadi.client.java.enumerator.PartitionStrategy; import org.zalando.nakadi.client.java.model.EventType; import org.zalando.nakadi.client.java.model.Metrics; +import org.zalando.nakadi.client.java.test.event.generator.EventGenerator; +import org.zalando.nakadi.client.java.test.event.simple.MySimpleEventGenerator; +import org.zalando.nakadi.client.java.test.event.simple.SimpleEventListener; import org.zalando.nakadi.client.scala.ClientFactory; public class ClientIntegrationTest { @@ -24,6 +30,41 @@ public void shutdown() throws InterruptedException, ExecutionException { client.stop(); } + @Test + public void postGetDeleteEventTypes() throws InterruptedException, ExecutionException { + EventGenerator gen = new MySimpleEventGenerator()// + .withEventTypeId("ClientIntegrationTest-Java-postGetDeleteEventTypes").build(); + EventType originalEventType = gen.getEventType(); + + //POST + client.createEventType(originalEventType).get(); + + //GET + Optional eventTypeResult = client.getEventType(originalEventType.getName()).get(); + assertTrue("Created Event should be returned",eventTypeResult.isPresent()); + EventType eventType=eventTypeResult.get(); + + assertEquals(eventType.getCategory(), originalEventType.getCategory()); + assertEquals(eventType.getDataKeyFields(), originalEventType.getDataKeyFields()); + assertEquals(eventType.getName(), originalEventType.getName()); + assertEquals(eventType.getOwningApplication(), originalEventType.getOwningApplication()); + assertEquals(eventType.getPartitionKeyFields(), originalEventType.getPartitionKeyFields()); + assertEquals(eventType.getPartitionStrategy(), originalEventType.getPartitionStrategy()); + assertEquals(eventType.getSchema().getSchema(), originalEventType.getSchema().getSchema()); + assertEquals(eventType.getSchema().getType(), originalEventType.getSchema().getType()); + assertEquals(eventType.getStatistics(), originalEventType.getStatistics()); + + //DELETE + Void result = client.deleteEventType(originalEventType.getName()).get(); + + //GET + eventTypeResult = client.getEventType(originalEventType.getName()).get(); + assertFalse("Created Event should NOT be returned",eventTypeResult.isPresent()); + + + } + + @Test public void getMetrics() throws InterruptedException, ExecutionException { Optional result = client.getMetrics().get(); @@ -32,31 +73,29 @@ public void getMetrics() throws InterruptedException, ExecutionException { assertNotNull("Version should be available", metrics.getVersion()); assertTrue("Gauges should not be empty", metrics.getGauges().size() > 0); } - + @Test - public void getEventTypes() throws InterruptedException, ExecutionException{ + public void getEventTypes() throws InterruptedException, ExecutionException { Optional> result = client.getEventTypes().get(); List events = result.get(); assertTrue(result.isPresent()); - assertTrue(events.size()>=0); + assertTrue(events.size() >= 0); } - + @Test - public void getEnrichmentStrategies() throws InterruptedException, ExecutionException{ + public void getEnrichmentStrategies() throws InterruptedException, ExecutionException { Optional> result = client.getEnrichmentStrategies().get(); assertTrue(result.isPresent()); List enrichtmentStrategies = result.get(); - assertTrue("EventEnrichmentStrategy",enrichtmentStrategies.size() == 1); + assertTrue("EventEnrichmentStrategy", enrichtmentStrategies.size() == 1); } - + @Test - public void getPartitioningStrategies() throws InterruptedException, ExecutionException{ + public void getPartitioningStrategies() throws InterruptedException, ExecutionException { Optional> result = client.getPartitioningStrategies().get(); assertTrue(result.isPresent()); List partitioningStrategies = result.get(); - assertTrue("PartitionStrategy",partitioningStrategies.size() ==3); + assertTrue("PartitionStrategy", partitioningStrategies.size() == 3); } - - - + } diff --git a/it/src/test/scala/org/zalando/nakadi/client/scala/ClientIntegrationTest.scala b/it/src/test/scala/org/zalando/nakadi/client/scala/ClientIntegrationTest.scala index e21c3c8..2968025 100644 --- a/it/src/test/scala/org/zalando/nakadi/client/scala/ClientIntegrationTest.scala +++ b/it/src/test/scala/org/zalando/nakadi/client/scala/ClientIntegrationTest.scala @@ -19,13 +19,50 @@ class ClientIntegrationTest extends WordSpec with Matchers with BeforeAndAfterAl import MySimpleEvent._ val client = ClientFactory.getScalaClient() - val nrOfEvents = 45 - val listener = new SimpleEventListener() + val eventGenerator = new DefaultMySimpleEventGenerator() { + def eventTypeId = s"ClientIntegrationTest-Scala" + } override def afterAll { client.stop() } + "POST/GET/DELETE /event-types" in { + val eventType = eventGenerator.eventType + //POST + val creationResult = Await.result(client.createEventType(eventType), 10.seconds) + creationResult shouldBe None + + //GET + var eventClientResult = Await.result(client.getEventType(eventType.name), 10.seconds) + eventClientResult.isRight shouldBe true + var Right(eventTypeOpt) = eventClientResult + eventTypeOpt.isDefined shouldBe true + val Some(eventTypeResult) = eventTypeOpt + + // Compare EventType + eventType.category shouldBe eventTypeResult.category + eventType.dataKeyFields shouldBe List() + eventType.name shouldBe eventTypeResult.name + eventType.owningApplication shouldBe eventTypeResult.owningApplication + eventType.partitionStrategy shouldBe eventTypeResult.partitionStrategy + eventType.schema shouldBe eventTypeResult.schema + eventType.statistics shouldBe eventTypeResult.statistics + eventType.enrichmentStrategies shouldBe eventTypeResult.enrichmentStrategies + eventType.partitionKeyFields shouldBe eventTypeResult.partitionKeyFields + + //DELETE + val deletedResult = Await.result(client.deleteEventType(eventType.name), 10.seconds) + deletedResult.isEmpty shouldBe true + + //GET + eventClientResult = Await.result(client.getEventType(eventType.name), 10.seconds) + eventClientResult.isRight shouldBe true + + eventClientResult.right.get.isDefined shouldBe false + + } + "GET /metrics" in { val result = Await.result(client.getMetrics(), 10.seconds) result.isRight shouldBe true @@ -54,9 +91,8 @@ class ClientIntegrationTest extends WordSpec with Matchers with BeforeAndAfterAl "GET /registry/enrichment-strategies" in { val result = Await.result(client.getEnrichmentStrategies(), 10.seconds) result.isRight shouldBe true - val Right(strategies)=result - strategies.isDefined shouldBe true - + val Right(strategies) = result + strategies.isDefined shouldBe true } } From 831be9a72c2eca7040bf25851f414f079e399eb7 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Mon, 18 Jul 2016 13:31:28 +0200 Subject: [PATCH 131/183] techjira:LAAS-60 Both Scala & Java can now update EventTypes. fixes #89 --- .../nakadi/client/java/ClientImpl.java | 2 +- .../client/java/JavaClientHandler.scala | 20 ++- .../nakadi/client/scala/Connection.scala | 5 +- .../client/java/ClientIntegrationTest.java | 118 +++++++++++++++--- .../client/scala/ClientIntegrationTest.scala | 93 +++++++++++--- project/Dependencies.scala | 3 +- 6 files changed, 195 insertions(+), 46 deletions(-) diff --git a/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java b/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java index ff4fba5..199c127 100644 --- a/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java +++ b/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java @@ -61,7 +61,7 @@ public Future> getEventType(String eventTypeName) { @Override public Future updateEventType(String eventTypeName, EventType eventType) { - throw new NotImplementedException(); + return handler.put(Uri.getEventTypeByName(eventTypeName), eventType, eventTypeSerializer); } @Override diff --git a/client/src/main/scala/org/zalando/nakadi/client/java/JavaClientHandler.scala b/client/src/main/scala/org/zalando/nakadi/client/java/JavaClientHandler.scala index 667d3a0..e6585a6 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/java/JavaClientHandler.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/java/JavaClientHandler.scala @@ -38,6 +38,7 @@ import org.zalando.nakadi.client.scala.ScalaEventHandlerImpl import org.zalando.nakadi.client.scala.JavaEventHandlerImpl import scala.concurrent.Await import scala.concurrent.duration.Duration +import akka.http.scaladsl.model.HttpMethod /** * Handler for mapping(Java<->Scala) and handling http calls and listener subscriptions for the Java API. @@ -49,6 +50,7 @@ trait JavaClientHandler { headers: Seq[HttpHeader], des: Deserializer[T]): java.util.concurrent.Future[Optional[T]] def post[T](endpoint: String, model: T)(implicit serializer: Serializer[T]): java.util.concurrent.Future[Void] + def put[T](endpoint: String, model: T)(implicit serializer: Serializer[T]): java.util.concurrent.Future[Void] def delete[T](endpoint: String): java.util.concurrent.Future[Void] def subscribe[T <: JEvent]( eventTypeName: String, @@ -93,16 +95,22 @@ class JavaClientHandlerImpl(val connection: Connection, subscriber: Subscription .flatMap(deserialize(_, des))) } def post[T](endpoint: String, model: T)(implicit serializer: Serializer[T]): java.util.concurrent.Future[Void] = { + executeMethod(endpoint, model, HttpMethods.POST) + } + + def executeMethod[T](endpoint: String, model: T, httpMethod: HttpMethod)(implicit serializer: Serializer[T]) = { val entity = serializer.to(model) - logger.info("Posting to endpoint {}", endpoint) - logger.debug("Data to post {}", entity) + logger.info(s"Calling ${endpoint} with ${httpMethod} and ${entity}") val result = connection - .executeCall(withHttpRequestAndPayload(endpoint, serialize(model), HttpMethods.POST, connection.tokenProvider)) + .executeCall(withHttpRequestAndPayload(endpoint, serialize(model), httpMethod, connection.tokenProvider)) .flatMap(response(_)) FutureConversions.fromOption2Void(result) } - def delete[T](endpoint: String): java.util.concurrent.Future[Void]={ - val result = connection.delete(endpoint) .flatMap(response(_)) + def put[T](endpoint: String, model: T)(implicit serializer: Serializer[T]): java.util.concurrent.Future[Void] = { + executeMethod(endpoint, model, HttpMethods.PUT) + } + def delete[T](endpoint: String): java.util.concurrent.Future[Void] = { + val result = connection.delete(endpoint).flatMap(response(_)) FutureConversions.fromOption2Void(result) } @@ -116,6 +124,7 @@ class JavaClientHandlerImpl(val connection: Connection, subscriber: Subscription private def response[T](response: HttpResponse): Future[Option[String]] = response match { case HttpResponse(status, headers, entity, protocol) if (status.isSuccess()) => + logger.debug("Call succeeded: {}",response) Try(Unmarshal(entity).to[String]) match { case Success(result) => result.map(Option(_)) case Failure(error) => @@ -123,6 +132,7 @@ class JavaClientHandlerImpl(val connection: Connection, subscriber: Subscription } case HttpResponse(status, headers, entity, protocol) if (status.isFailure()) => + logger.warn("Call failed: {}",response) Unmarshal(entity).to[String].map { x => val msg = "http-stats(%s) - %s - problem: %s ".format(status.intValue(), x, status.defaultMessage()) logger.warn(msg) diff --git a/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala index 119e39b..e1c207c 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala @@ -147,16 +147,17 @@ sealed class ConnectionImpl(val host: String, def put[T](endpoint: String, model: T)(implicit serializer: Serializer[T]): Future[HttpResponse] = { val entity = serializer.to(model) logger.info(s"Put: $endpoint - Data: $entity") - executeCall(withHttpRequest(endpoint, HttpMethods.PUT, Nil, tokenProvider, None)) + executeCall(withHttpRequestAndPayload(endpoint, entity, HttpMethods.PUT, tokenProvider)) } def post[T](endpoint: String, model: T)(implicit serializer: Serializer[T]): Future[HttpResponse] = { val entity = serializer.to(model) - logger.info(s"Put: $endpoint - Data: $entity") + logger.info(s"Post: $endpoint - Data: $entity") executeCall(withHttpRequestAndPayload(endpoint, entity, HttpMethods.POST, tokenProvider)) } def executeCall(request: HttpRequest): Future[HttpResponse] = { + logger.debug("executingCall {}", request) val response: Future[HttpResponse] = Source.single(request).via(requestFlow).runWith(Sink.head)(materializer()) logError(response) response diff --git a/it/src/test/java/org/zalando/client/java/ClientIntegrationTest.java b/it/src/test/java/org/zalando/client/java/ClientIntegrationTest.java index cbac659..43e47f4 100644 --- a/it/src/test/java/org/zalando/client/java/ClientIntegrationTest.java +++ b/it/src/test/java/org/zalando/client/java/ClientIntegrationTest.java @@ -8,18 +8,19 @@ import java.util.List; import java.util.Optional; import java.util.concurrent.ExecutionException; -import java.util.concurrent.Future; import org.junit.After; import org.junit.Test; import org.zalando.nakadi.client.java.Client; import org.zalando.nakadi.client.java.enumerator.EventEnrichmentStrategy; +import org.zalando.nakadi.client.java.enumerator.EventTypeCategory; import org.zalando.nakadi.client.java.enumerator.PartitionStrategy; import org.zalando.nakadi.client.java.model.EventType; +import org.zalando.nakadi.client.java.model.EventTypeSchema; +import org.zalando.nakadi.client.java.model.EventTypeStatistics; import org.zalando.nakadi.client.java.model.Metrics; import org.zalando.nakadi.client.java.test.event.generator.EventGenerator; import org.zalando.nakadi.client.java.test.event.simple.MySimpleEventGenerator; -import org.zalando.nakadi.client.java.test.event.simple.SimpleEventListener; import org.zalando.nakadi.client.scala.ClientFactory; public class ClientIntegrationTest { @@ -31,19 +32,45 @@ public void shutdown() throws InterruptedException, ExecutionException { } @Test - public void postGetDeleteEventTypes() throws InterruptedException, ExecutionException { + public void createEventTypes() throws InterruptedException, ExecutionException { EventGenerator gen = new MySimpleEventGenerator()// - .withEventTypeId("ClientIntegrationTest-Java-postGetDeleteEventTypes").build(); + .withEventTypeId("ClientIntegrationTest-Java-postGetDeleteEventTypes").build(); EventType originalEventType = gen.getEventType(); - - //POST + + // POST + client.createEventType(originalEventType).get(); + + // GET + Optional eventTypeResult = client.getEventType(originalEventType.getName()).get(); + assertTrue("Created Event should be returned", eventTypeResult.isPresent()); + EventType eventType = eventTypeResult.get(); + + assertEquals(eventType.getCategory(), originalEventType.getCategory()); + assertEquals(eventType.getDataKeyFields(), originalEventType.getDataKeyFields()); + assertEquals(eventType.getName(), originalEventType.getName()); + assertEquals(eventType.getOwningApplication(), originalEventType.getOwningApplication()); + assertEquals(eventType.getPartitionKeyFields(), originalEventType.getPartitionKeyFields()); + assertEquals(eventType.getPartitionStrategy(), originalEventType.getPartitionStrategy()); + assertEquals(eventType.getSchema().getSchema(), originalEventType.getSchema().getSchema()); + assertEquals(eventType.getSchema().getType(), originalEventType.getSchema().getType()); + assertEquals(eventType.getStatistics(), originalEventType.getStatistics()); + + } + + @Test + public void updateEventTypes() throws InterruptedException, ExecutionException { + EventGenerator gen = new MySimpleEventGenerator()// + .withEventTypeId("ClientIntegrationTest-Java-postGetDeleteEventTypes").build(); + EventType originalEventType = gen.getEventType(); + + // POST client.createEventType(originalEventType).get(); - - //GET + + // GET Optional eventTypeResult = client.getEventType(originalEventType.getName()).get(); - assertTrue("Created Event should be returned",eventTypeResult.isPresent()); - EventType eventType=eventTypeResult.get(); - + assertTrue("Created Event should be returned", eventTypeResult.isPresent()); + EventType eventType = eventTypeResult.get(); + assertEquals(eventType.getCategory(), originalEventType.getCategory()); assertEquals(eventType.getDataKeyFields(), originalEventType.getDataKeyFields()); assertEquals(eventType.getName(), originalEventType.getName()); @@ -53,17 +80,68 @@ public void postGetDeleteEventTypes() throws InterruptedException, ExecutionExce assertEquals(eventType.getSchema().getSchema(), originalEventType.getSchema().getSchema()); assertEquals(eventType.getSchema().getType(), originalEventType.getSchema().getType()); assertEquals(eventType.getStatistics(), originalEventType.getStatistics()); - - //DELETE + + String name = eventType.getName(); + String owningApplication = "owningApplication"; + EventTypeCategory category = eventType.getCategory(); + List enrichmentStrategies = eventType.getEnrichmentStrategies(); + PartitionStrategy partitionStrategy = eventType.getPartitionStrategy(); + EventTypeSchema schema = eventType.getSchema(); + List dataKeyFields = eventType.getDataKeyFields(); + List partitionKeyFields = eventType.getPartitionKeyFields(); + EventTypeStatistics statistics = eventType.getStatistics(); + EventType changedEventType = new EventType(name, owningApplication, category, enrichmentStrategies, + partitionStrategy, schema, dataKeyFields, partitionKeyFields, statistics); + + // Update + client.updateEventType(originalEventType.getName(), changedEventType).get(); + Thread.sleep(3000); + // GET + eventTypeResult = client.getEventType(originalEventType.getName()).get(); + assertTrue("Created Event should NOT be returned", eventTypeResult.isPresent()); + assertEquals(eventTypeResult.get().getCategory(), originalEventType.getCategory()); + assertEquals(eventTypeResult.get().getDataKeyFields(), originalEventType.getDataKeyFields()); + assertEquals(eventTypeResult.get().getName(), originalEventType.getName()); + assertEquals(eventTypeResult.get().getOwningApplication(), changedEventType.getOwningApplication()); + assertEquals(eventTypeResult.get().getPartitionKeyFields(), originalEventType.getPartitionKeyFields()); + assertEquals(eventTypeResult.get().getPartitionStrategy(), originalEventType.getPartitionStrategy()); + assertEquals(eventTypeResult.get().getSchema().getSchema(), originalEventType.getSchema().getSchema()); + assertEquals(eventTypeResult.get().getSchema().getType(), originalEventType.getSchema().getType()); + assertEquals(eventTypeResult.get().getStatistics(), originalEventType.getStatistics()); + + } + + @Test + public void deleteEventTypes() throws InterruptedException, ExecutionException { + EventGenerator gen = new MySimpleEventGenerator()// + .withEventTypeId("ClientIntegrationTest-Java-postGetDeleteEventTypes").build(); + EventType originalEventType = gen.getEventType(); + + // POST + client.createEventType(originalEventType).get(); + + // GET + Optional eventTypeResult = client.getEventType(originalEventType.getName()).get(); + assertTrue("Created Event should be returned", eventTypeResult.isPresent()); + EventType eventType = eventTypeResult.get(); + + assertEquals(eventType.getCategory(), originalEventType.getCategory()); + assertEquals(eventType.getDataKeyFields(), originalEventType.getDataKeyFields()); + assertEquals(eventType.getName(), originalEventType.getName()); + assertEquals(eventType.getOwningApplication(), originalEventType.getOwningApplication()); + assertEquals(eventType.getPartitionKeyFields(), originalEventType.getPartitionKeyFields()); + assertEquals(eventType.getPartitionStrategy(), originalEventType.getPartitionStrategy()); + assertEquals(eventType.getSchema().getSchema(), originalEventType.getSchema().getSchema()); + assertEquals(eventType.getSchema().getType(), originalEventType.getSchema().getType()); + assertEquals(eventType.getStatistics(), originalEventType.getStatistics()); + + // DELETE Void result = client.deleteEventType(originalEventType.getName()).get(); - - //GET - eventTypeResult = client.getEventType(originalEventType.getName()).get(); - assertFalse("Created Event should NOT be returned",eventTypeResult.isPresent()); - - + + // GET + eventTypeResult = client.getEventType(originalEventType.getName()).get(); + assertFalse("Created Event should NOT be returned", eventTypeResult.isPresent()); } - @Test public void getMetrics() throws InterruptedException, ExecutionException { diff --git a/it/src/test/scala/org/zalando/nakadi/client/scala/ClientIntegrationTest.scala b/it/src/test/scala/org/zalando/nakadi/client/scala/ClientIntegrationTest.scala index 2968025..ebaa36e 100644 --- a/it/src/test/scala/org/zalando/nakadi/client/scala/ClientIntegrationTest.scala +++ b/it/src/test/scala/org/zalando/nakadi/client/scala/ClientIntegrationTest.scala @@ -11,6 +11,7 @@ import org.scalatest.BeforeAndAfter import scala.concurrent.Await import scala.concurrent.duration.DurationInt import org.scalatest.BeforeAndAfterAll +import scala.util.Random class ClientIntegrationTest extends WordSpec with Matchers with BeforeAndAfterAll { @@ -19,46 +20,104 @@ class ClientIntegrationTest extends WordSpec with Matchers with BeforeAndAfterAl import MySimpleEvent._ val client = ClientFactory.getScalaClient() - val eventGenerator = new DefaultMySimpleEventGenerator() { - def eventTypeId = s"ClientIntegrationTest-Scala" - } override def afterAll { client.stop() } - "POST/GET/DELETE /event-types" in { + "create and get an eventType" in { + val eventGenerator = new DefaultMySimpleEventGenerator() { + def eventTypeId = s"ClientIntegrationTest-Scala" + } val eventType = eventGenerator.eventType //POST val creationResult = Await.result(client.createEventType(eventType), 10.seconds) creationResult shouldBe None //GET - var eventClientResult = Await.result(client.getEventType(eventType.name), 10.seconds) + { + val eventClientResult = Await.result(client.getEventType(eventType.name), 10.seconds) + eventClientResult.isRight shouldBe true + val Right(eventTypeOpt) = eventClientResult + eventTypeOpt.isDefined shouldBe true + val Some(eventTypeResult) = eventTypeOpt + + // Compare EventType + eventType.category shouldBe eventTypeResult.category + eventType.dataKeyFields shouldBe List() + eventType.name shouldBe eventTypeResult.name + eventType.owningApplication shouldBe eventTypeResult.owningApplication + eventType.partitionStrategy shouldBe eventTypeResult.partitionStrategy + eventType.schema shouldBe eventTypeResult.schema + eventType.statistics shouldBe eventTypeResult.statistics + eventType.enrichmentStrategies shouldBe eventTypeResult.enrichmentStrategies + eventType.partitionKeyFields shouldBe eventTypeResult.partitionKeyFields + } + } + "update an existing eventType" in { + val eventGenerator = new DefaultMySimpleEventGenerator() { + def eventTypeId = s"ClientIntegrationTest-Scala" + } + val eventType = eventGenerator.eventType + + //POST + val creationResult = Await.result(client.createEventType(eventType), 10.seconds) + creationResult shouldBe None + + //UPDATE + val changedEventType = eventType.copy(owningApplication = "Nakadi-klients(integration-test-suite)2", enrichmentStrategies = List()) + eventType.owningApplication should not be changedEventType.owningApplication + + val updateResult = Await.result(client.updateEventType(eventType.name, changedEventType), 10.seconds) + + updateResult.isEmpty shouldBe true //Error should be empty + val eventClientResult = Await.result(client.getEventType(eventType.name), 10.seconds) eventClientResult.isRight shouldBe true - var Right(eventTypeOpt) = eventClientResult + val Right(eventTypeOpt) = eventClientResult eventTypeOpt.isDefined shouldBe true val Some(eventTypeResult) = eventTypeOpt - // Compare EventType - eventType.category shouldBe eventTypeResult.category - eventType.dataKeyFields shouldBe List() - eventType.name shouldBe eventTypeResult.name - eventType.owningApplication shouldBe eventTypeResult.owningApplication - eventType.partitionStrategy shouldBe eventTypeResult.partitionStrategy - eventType.schema shouldBe eventTypeResult.schema - eventType.statistics shouldBe eventTypeResult.statistics - eventType.enrichmentStrategies shouldBe eventTypeResult.enrichmentStrategies - eventType.partitionKeyFields shouldBe eventTypeResult.partitionKeyFields + //Check that it is not equal to original + eventType.owningApplication should not be eventTypeResult.owningApplication + + //The changed one + changedEventType.name shouldBe eventTypeResult.name + changedEventType.category shouldBe eventTypeResult.category + changedEventType.dataKeyFields shouldBe List() + changedEventType.name shouldBe eventTypeResult.name + changedEventType.owningApplication shouldBe eventTypeResult.owningApplication + changedEventType.partitionStrategy shouldBe eventTypeResult.partitionStrategy + changedEventType.schema shouldBe eventTypeResult.schema + changedEventType.statistics shouldBe eventTypeResult.statistics + changedEventType.enrichmentStrategies shouldBe eventTypeResult.enrichmentStrategies + changedEventType.partitionKeyFields shouldBe eventTypeResult.partitionKeyFields + } + + "POST & GET & DELETE /event-types/{name}" in { + val eventGenerator = new DefaultMySimpleEventGenerator() { + def eventTypeId = s"ClientIntegrationTest-Scala" + } + val eventType = eventGenerator.eventType + //POST + val creationResult = Await.result(client.createEventType(eventType), 10.seconds) + creationResult shouldBe None + + //GET + { + val eventClientResult = Await.result(client.getEventType(eventType.name), 10.seconds) + eventClientResult.isRight shouldBe true + eventClientResult.right.get.isDefined shouldBe true + } //DELETE val deletedResult = Await.result(client.deleteEventType(eventType.name), 10.seconds) deletedResult.isEmpty shouldBe true //GET - eventClientResult = Await.result(client.getEventType(eventType.name), 10.seconds) + val eventClientResult = Await.result(client.getEventType(eventType.name), 10.seconds) eventClientResult.isRight shouldBe true + //Result is empty(404) eventClientResult.right.get.isDefined shouldBe false } diff --git a/project/Dependencies.scala b/project/Dependencies.scala index 4d68118..18c0720 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -21,6 +21,7 @@ object Dependencies { "com.fasterxml.jackson.core" % "jackson-core" % jacksonVersion, "com.fasterxml.jackson.module" %% "jackson-module-scala" % jacksonVersion, "ch.qos.logback" % "logback-classic" % "1.1.7", + "ch.qos.logback" % "logback-core" % "1.1.7", "com.typesafe.akka" %% "akka-actor" % akkaVersion, "com.typesafe.akka" %% "akka-http-experimental" % akkaVersion, "com.typesafe.akka" %% "akka-stream" % akkaVersion, @@ -28,7 +29,7 @@ object Dependencies { "com.typesafe.akka" % "akka-slf4j_2.11" % "2.4.7", "com.typesafe.akka" %% "akka-testkit" % akkaVersion % "test", "org.scalatest" %% "scalatest" % scalaTestVersion % "test", - "com.google.code.findbugs" % "jsr305" % "1.3.9" % "test", + "com.google.code.findbugs" % "jsr305" % "3.0.0" % "test", "junit" % "junit" % junitVersion % "test", "org.mockito" % "mockito-core" % "1.10.19" % "test", "com.novocode" % "junit-interface" % "0.11" % "test" From 348dfdf7914cf60c2f8e69064249f2731bcf4f62 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Mon, 18 Jul 2016 17:27:02 +0200 Subject: [PATCH 132/183] techjira:LAAS-60 Added Travis file. #69 --- .travis.yml | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..1e7cd79 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,22 @@ +language: scala +scala: + - 2.11.8 + +env: + - DOCKER_IP=127.0.0.1 + +jdk: + - oraclejdk8 + +before_install: + - sudo rm /usr/local/bin/docker-compose + - curl --verbos -L https://github.com/docker/compose/releases/download/1.6.1/docker-compose-`uname -s`-`uname -m` > docker-compose + - chmod +x docker-compose + - sudo mv docker-compose /usr/local/bin + +before_script: + - sudo /etc/init.d/postgresql stop + - curl https://raw.githubusercontent.com/zalando/reactive-nakadi/master/src/it/resources/nakadi.sh | bash + +script: + - sbt clean test From 11ccb9391de84b9153a044b66f4cb60587d6f810 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Mon, 18 Jul 2016 17:49:23 +0200 Subject: [PATCH 133/183] techjira:LAAS-60 Trigger build #69 --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 1e7cd79..ccb4b85 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,6 +5,7 @@ scala: env: - DOCKER_IP=127.0.0.1 + jdk: - oraclejdk8 From f786f97f14347cefa03e927543b03d3615819242 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Mon, 18 Jul 2016 18:05:11 +0200 Subject: [PATCH 134/183] techjira:LAAS-60 Fixing docker-compose #69 --- .travis.yml | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index ccb4b85..116c0df 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,13 +5,20 @@ scala: env: - DOCKER_IP=127.0.0.1 - jdk: - - oraclejdk8 + - openjdk8 + +services: + - docker before_install: + - sudo sh -c 'echo "deb https://apt.dockerproject.org/repo ubuntu-precise main" > /etc/apt/sources.list.d/docker.list' + - sudo apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D + - sudo apt-get update + - sudo apt-key update + - sudo apt-get -qqy -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" install docker-engine=${DOCKER_VERSION} - sudo rm /usr/local/bin/docker-compose - - curl --verbos -L https://github.com/docker/compose/releases/download/1.6.1/docker-compose-`uname -s`-`uname -m` > docker-compose + - curl -L https://github.com/docker/compose/releases/download/${DOCKER_COMPOSE_VERSION}/docker-compose-`uname -s`-`uname -m` > docker-compose - chmod +x docker-compose - sudo mv docker-compose /usr/local/bin From 693a0a75def0196ec43a788f7ab8d75ab9674414 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Mon, 18 Jul 2016 18:09:37 +0200 Subject: [PATCH 135/183] techjira:LAAS-60 Triggering a simple build #69 --- .travis.yml | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/.travis.yml b/.travis.yml index 116c0df..d49fe7f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,19 +12,19 @@ services: - docker before_install: - - sudo sh -c 'echo "deb https://apt.dockerproject.org/repo ubuntu-precise main" > /etc/apt/sources.list.d/docker.list' - - sudo apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D - - sudo apt-get update - - sudo apt-key update - - sudo apt-get -qqy -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" install docker-engine=${DOCKER_VERSION} - - sudo rm /usr/local/bin/docker-compose - - curl -L https://github.com/docker/compose/releases/download/${DOCKER_COMPOSE_VERSION}/docker-compose-`uname -s`-`uname -m` > docker-compose - - chmod +x docker-compose - - sudo mv docker-compose /usr/local/bin +# - sudo sh -c 'echo "deb https://apt.dockerproject.org/repo ubuntu-precise main" > /etc/apt/sources.list.d/docker.list' +# - sudo apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D +# - sudo apt-get update +# - sudo apt-key update +# - sudo apt-get -qqy -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" install docker-engine=${DOCKER_VERSION} +# - sudo rm /usr/local/bin/docker-compose +# - curl -L https://github.com/docker/compose/releases/download/${DOCKER_COMPOSE_VERSION}/docker-compose-`uname -s`-`uname -m` > docker-compose +# - chmod +x docker-compose +# - sudo mv docker-compose /usr/local/bin before_script: - - sudo /etc/init.d/postgresql stop - - curl https://raw.githubusercontent.com/zalando/reactive-nakadi/master/src/it/resources/nakadi.sh | bash +# - sudo /etc/init.d/postgresql stop +# - curl https://raw.githubusercontent.com/zalando/reactive-nakadi/master/src/it/resources/nakadi.sh | bash script: - sbt clean test From 4196e7f920029285653a17a48e289511689050d4 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Tue, 19 Jul 2016 11:22:44 +0200 Subject: [PATCH 136/183] techjira:LAAS-60 Install docker-compose #69 --- .travis.yml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/.travis.yml b/.travis.yml index d49fe7f..0e87e24 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,15 +12,15 @@ services: - docker before_install: -# - sudo sh -c 'echo "deb https://apt.dockerproject.org/repo ubuntu-precise main" > /etc/apt/sources.list.d/docker.list' -# - sudo apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D -# - sudo apt-get update -# - sudo apt-key update -# - sudo apt-get -qqy -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" install docker-engine=${DOCKER_VERSION} -# - sudo rm /usr/local/bin/docker-compose -# - curl -L https://github.com/docker/compose/releases/download/${DOCKER_COMPOSE_VERSION}/docker-compose-`uname -s`-`uname -m` > docker-compose -# - chmod +x docker-compose -# - sudo mv docker-compose /usr/local/bin + - sudo sh -c 'echo "deb https://apt.dockerproject.org/repo ubuntu-precise main" > /etc/apt/sources.list.d/docker.list' + - sudo apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D + - sudo apt-get update + - sudo apt-key update + - sudo apt-get -qqy -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" install docker-engine=${DOCKER_VERSION} + - sudo rm /usr/local/bin/docker-compose + - curl -L https://github.com/docker/compose/releases/download/${DOCKER_COMPOSE_VERSION}/docker-compose-`uname -s`-`uname -m` > docker-compose + - chmod +x docker-compose + - sudo mv docker-compose /usr/local/bin before_script: # - sudo /etc/init.d/postgresql stop From 59000a1344f023984e5fa1d9267af8d2a8333ad0 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Tue, 19 Jul 2016 11:26:13 +0200 Subject: [PATCH 137/183] techjira:LAAS-60 Install docker-compose #69 --- .travis.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.travis.yml b/.travis.yml index 0e87e24..0f932b3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,6 +11,11 @@ jdk: services: - docker +# fix for buffer overflow https://github.com/travis-ci/travis-ci/issues/5227 +addons: + hostname: localhost + +# Travis has an old version of docker and docker-compose that is why we need to load from the net and install them. before_install: - sudo sh -c 'echo "deb https://apt.dockerproject.org/repo ubuntu-precise main" > /etc/apt/sources.list.d/docker.list' - sudo apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D From 21ab86b77b8d317d71d8f8baf7981a5c456aae7a Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Tue, 19 Jul 2016 11:30:48 +0200 Subject: [PATCH 138/183] techjira:LAAS-60 Install docker-compose #69 --- .travis.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 0f932b3..0cb358e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,9 @@ scala: - 2.11.8 env: - - DOCKER_IP=127.0.0.1 + DOCKER_IP: 127.0.0.1 + DOCKER_COMPOSE_VERSION: 1.7.1 + DOCKER_ENGINE_VERSION: 1.11.1 jdk: - openjdk8 @@ -21,7 +23,8 @@ before_install: - sudo apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D - sudo apt-get update - sudo apt-key update - - sudo apt-get -qqy -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" install docker-engine=${DOCKER_VERSION} + - sudo apt-get -qqy -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" install docker-engine=${DOCKER_ENGINE_VERSION}-0~precise + - sudo rm /usr/local/bin/docker-compose - curl -L https://github.com/docker/compose/releases/download/${DOCKER_COMPOSE_VERSION}/docker-compose-`uname -s`-`uname -m` > docker-compose - chmod +x docker-compose From d0607f885f79dbb255d2f2ae9e024ddab3d54111 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Tue, 19 Jul 2016 11:42:22 +0200 Subject: [PATCH 139/183] techjira:LAAS-60 Install docker-compose #69 --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 0cb358e..c8e34d7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -31,8 +31,8 @@ before_install: - sudo mv docker-compose /usr/local/bin before_script: -# - sudo /etc/init.d/postgresql stop -# - curl https://raw.githubusercontent.com/zalando/reactive-nakadi/master/src/it/resources/nakadi.sh | bash + - sudo /etc/init.d/postgresql stop + - curl https://raw.githubusercontent.com/zalando/reactive-nakadi/master/src/it/resources/nakadi.sh | bash script: - sbt clean test From 86ca105aa19489c6968d3fa1a7c8b31e6120a5a5 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Tue, 19 Jul 2016 11:48:51 +0200 Subject: [PATCH 140/183] techjira:LAAS-60 Start nakadi #69 --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index c8e34d7..51e7d58 100644 --- a/.travis.yml +++ b/.travis.yml @@ -32,7 +32,7 @@ before_install: before_script: - sudo /etc/init.d/postgresql stop - - curl https://raw.githubusercontent.com/zalando/reactive-nakadi/master/src/it/resources/nakadi.sh | bash + - bash <(curl -s https://raw.githubusercontent.com/zalando/reactive-nakadi/master/src/it/resources/nakadi.sh) script: - sbt clean test From f78f921f1db4c36c1bd080b8a7ef684b82d15e8e Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Tue, 19 Jul 2016 13:06:45 +0200 Subject: [PATCH 141/183] techjira:LAAS-60 2nd retry to start nakadi server #69 --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 51e7d58..6bc3049 100644 --- a/.travis.yml +++ b/.travis.yml @@ -30,9 +30,10 @@ before_install: - chmod +x docker-compose - sudo mv docker-compose /usr/local/bin + - curl -o- https://raw.githubusercontent.com/zalando/reactive-nakadi/v0.0.04/src/it/resources/nakadi.sh | bash + before_script: - sudo /etc/init.d/postgresql stop - - bash <(curl -s https://raw.githubusercontent.com/zalando/reactive-nakadi/master/src/it/resources/nakadi.sh) script: - sbt clean test From 5337225e1db5df261af4506979735aa374d3cd00 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Tue, 19 Jul 2016 17:38:31 +0200 Subject: [PATCH 142/183] techjira:LAAS-60 Actors cannot receive messages about its own terminations. --- .../org/zalando/nakadi/client/actor/ConsumingActor.scala | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/client/src/main/scala/org/zalando/nakadi/client/actor/ConsumingActor.scala b/client/src/main/scala/org/zalando/nakadi/client/actor/ConsumingActor.scala index 8322905..b29b9dd 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/actor/ConsumingActor.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/actor/ConsumingActor.scala @@ -66,11 +66,8 @@ class ConsumingActor(subscription: SubscriptionKey, handler: EventHandler) case OnComplete => log.info("onComplete - connection closed by server - cursor [{}] - [{}]", lastCursor, subscription) context.parent ! UnsubscribeMsg(subscription.eventTypeName, subscription.partition, handler.id()) - case Terminated => - log.info("Received Terminated msg - subscription [{}] with listener-id [{}] ", subscription, handler.id()) - context.stop(self) case a => - log.error("Could not handle message: [{}]", a) + log.error("Could not handle unknown msg: [{}] - subscription [{}] with listener-id [{}] ", a, subscription, handler.id()) context.stop(self) } From 07f8bce556b3d85df256c5e16a90bca142e150ba Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Tue, 19 Jul 2016 17:39:47 +0200 Subject: [PATCH 143/183] techjira:LAAS-60 Failing connections should not be removed from the pipepline. Fixes #91. --- .../org/zalando/nakadi/client/handler/SubscriptionHandler.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/main/scala/org/zalando/nakadi/client/handler/SubscriptionHandler.scala b/client/src/main/scala/org/zalando/nakadi/client/handler/SubscriptionHandler.scala index 28ef217..d2c442a 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/handler/SubscriptionHandler.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/handler/SubscriptionHandler.scala @@ -121,7 +121,7 @@ class SubscriptionHandlerImpl(val connection: Connection) extends SubscriptionHa logger.error(s"http-status: ${x.status.intValue().toString()}, reason[${x.status .reason()}], body:[${body.data.decodeString("UTF-8")}]") } - false + true // Must return true otherwise reconnection will leave Actors in the unknown... } }.map(_.entity.withSizeLimit(Long.MaxValue).dataBytes.via(delimiterFlow)) From a97dc4bf7183a1f36a9857d949870e270f36345f Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Tue, 19 Jul 2016 17:54:04 +0200 Subject: [PATCH 144/183] techjira:LAAS-60 Feedback: Documenting the status parameter --- .../nakadi/client/java/ClientError.java | 98 ++++++++++++------- 1 file changed, 63 insertions(+), 35 deletions(-) diff --git a/api/src/main/java/org/zalando/nakadi/client/java/ClientError.java b/api/src/main/java/org/zalando/nakadi/client/java/ClientError.java index bd50812..ed34d79 100644 --- a/api/src/main/java/org/zalando/nakadi/client/java/ClientError.java +++ b/api/src/main/java/org/zalando/nakadi/client/java/ClientError.java @@ -7,13 +7,42 @@ public class ClientError { private final Optional status; private final Optional exception; + /** + * @param msg + * - Message containing information about the Error + * @param status + * - An Http Status, indicating the result of a failed Http + * connection attempt. + * @param exception + * - An exception if there was one. + */ public ClientError(String msg, Optional status, Optional exception) { + this.msg = msg; this.status = status; this.exception = exception; } + /** + * @param msg + * - Message containing information about the Error + * @param status + * - An Http Status, indicating the result of a failed Http + * connection attempt. + * @param exception + * - An exception if there was one. + */ + public ClientError(String msg, Integer status, Throwable exception) { + this.msg = msg; + this.status = Optional.of(status); + this.exception = Optional.of(exception); + } + + /** + * @param msg + * - Message containing information about the Error + */ public ClientError(String msg) { this.msg = msg; this.status = Optional.empty(); @@ -32,42 +61,41 @@ public Optional getException() { return exception; } - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((msg == null) ? 0 : msg.hashCode()); - result = prime * result + ((status == null) ? 0 : status.hashCode()); - return result; - } + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((msg == null) ? 0 : msg.hashCode()); + result = prime * result + ((status == null) ? 0 : status.hashCode()); + return result; + } - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - ClientError other = (ClientError) obj; - if (msg == null) { - if (other.msg != null) - return false; - } else if (!msg.equals(other.msg)) - return false; - if (status == null) { - if (other.status != null) - return false; - } else if (!status.equals(other.status)) - return false; - return true; - } + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + ClientError other = (ClientError) obj; + if (msg == null) { + if (other.msg != null) + return false; + } else if (!msg.equals(other.msg)) + return false; + if (status == null) { + if (other.status != null) + return false; + } else if (!status.equals(other.status)) + return false; + return true; + } - @Override - public String toString() { - return "ClientError [msg=" + msg + ", status=" + status + ", exception=" + exception + "]"; - } - - + @Override + public String toString() { + return "ClientError [msg=" + msg + ", status=" + status + + ", exception=" + exception + "]"; + } } \ No newline at end of file From 11ac5a3f70a0dcb2e96f5f3c67b58712aa4e352d Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Tue, 19 Jul 2016 17:54:50 +0200 Subject: [PATCH 145/183] techjira:LAAS-60 Feedback: Documenting the status parameter --- .../java/org/zalando/nakadi/client/java/ClientError.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/api/src/main/java/org/zalando/nakadi/client/java/ClientError.java b/api/src/main/java/org/zalando/nakadi/client/java/ClientError.java index ed34d79..9051915 100644 --- a/api/src/main/java/org/zalando/nakadi/client/java/ClientError.java +++ b/api/src/main/java/org/zalando/nakadi/client/java/ClientError.java @@ -9,7 +9,7 @@ public class ClientError { /** * @param msg - * - Message containing information about the Error + * - Description of the Error * @param status * - An Http Status, indicating the result of a failed Http * connection attempt. @@ -26,7 +26,7 @@ public ClientError(String msg, Optional status, /** * @param msg - * - Message containing information about the Error + * - Description of the Error * @param status * - An Http Status, indicating the result of a failed Http * connection attempt. @@ -41,7 +41,7 @@ public ClientError(String msg, Integer status, Throwable exception) { /** * @param msg - * - Message containing information about the Error + * - Description of the Error */ public ClientError(String msg) { this.msg = msg; From cb68df7cb6a835a13c4fc743b95acc3e029f6db2 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Tue, 19 Jul 2016 17:57:23 +0200 Subject: [PATCH 146/183] techjira:LAAS-60 Feedback:Making unsubsciption synchronous --- .../main/scala/org/zalando/nakadi/client/scala/Client.scala | 3 ++- .../scala/org/zalando/nakadi/client/scala/ClientImpl.scala | 3 +-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/api/src/main/scala/org/zalando/nakadi/client/scala/Client.scala b/api/src/main/scala/org/zalando/nakadi/client/scala/Client.scala index d1c15db..743da69 100644 --- a/api/src/main/scala/org/zalando/nakadi/client/scala/Client.scala +++ b/api/src/main/scala/org/zalando/nakadi/client/scala/Client.scala @@ -7,6 +7,7 @@ import org.zalando.nakadi.client.scala.model._ import org.zalando.nakadi.client._ import com.fasterxml.jackson.core.`type`.TypeReference + case class ClientError(msg: String, status: Option[Integer] = None, exception: Option[Throwable] = None) trait Client { @@ -157,6 +158,6 @@ trait Client { */ def unsubscribe[T <: Event](eventTypeName: String, partition: Option[String], - listener: Listener[T]): Future[Option[ClientError]] + listener: Listener[T]): Option[ClientError] } diff --git a/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala index d03ee28..d81bc85 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala @@ -147,9 +147,8 @@ class ClientImpl(connection: Connection, subscriber: SubscriptionHandler, charSe def unsubscribe[T <: Event](eventTypeName: String, partition: Option[String], - listener: Listener[T]): Future[Option[ClientError]] = { + listener: Listener[T]): Option[ClientError] = { subscriber.unsubscribe(eventTypeName, partition, listener.id) - Future.successful(None) } //#################### From a0d7324b60423acec6d07fac3980b62c4cf9deb6 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Tue, 19 Jul 2016 18:07:03 +0200 Subject: [PATCH 147/183] techjira:LAAS-60 Feedback:Log LevelLevel=Debug and typo --- .../zalando/nakadi/client/actor/ConsumingActor.scala | 2 +- .../zalando/nakadi/client/actor/SupervisingActor.scala | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/client/src/main/scala/org/zalando/nakadi/client/actor/ConsumingActor.scala b/client/src/main/scala/org/zalando/nakadi/client/actor/ConsumingActor.scala index b29b9dd..489cb0e 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/actor/ConsumingActor.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/actor/ConsumingActor.scala @@ -52,7 +52,7 @@ class ConsumingActor(subscription: SubscriptionKey, handler: EventHandler) override def receive: Receive = { case OnNext(msg: ByteString) => val message = msg.utf8String - log.info("Event - prevCursor [{}] - [{}] - msg [{}]", lastCursor, subscription, message) + log.debug("Event - prevCursor [{}] - [{}] - msg [{}]", lastCursor, subscription, message) handler.handleOnReceive(subscription.toString(), message) match { case Right(cursor) => lastCursor = Some(cursor) diff --git a/client/src/main/scala/org/zalando/nakadi/client/actor/SupervisingActor.scala b/client/src/main/scala/org/zalando/nakadi/client/actor/SupervisingActor.scala index b49baab..8a5ebee 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/actor/SupervisingActor.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/actor/SupervisingActor.scala @@ -61,18 +61,18 @@ class SupervisingActor(val connection: Connection, val subscriptionHandler: Subs def receive: Receive = { case OffsetMsg(cursor, subKey) => - log.info("Received cursor [{}] - subKey [{}]", cursor, subKey) + log.debug("Received cursor [{}] - subKey [{}]", cursor, subKey) subscriptions.addCursor(subKey, Some(cursor)) - case subscrition: SubscribeMsg => + case subscription: SubscribeMsg => val before = subscriptions.size - subscribe(subscrition) + subscribe(subscription) val after = subscriptions.size - log.info(s"SubscribeMsg - nr of subscriptions before [$before] - after [$after]") + log.debug(s"SubscribeMsg - nr of subscriptions before [$before] - after [$after]") case unsubscription: UnsubscribeMsg => val before = subscriptions.size unsubscribe(unsubscription) val after = subscriptions.size - log.info(s"UnsubscribeMsg - nr of subscriptions before [$before] - after [$after]") + log.debug(s"UnsubscribeMsg - nr of subscriptions before [$before] - after [$after]") case Terminated(terminatedActor) => log.info(s"Actor [{}] terminated", terminatedActor.path.name) subscriptions.entryByActor(terminatedActor) match { From 298fd6a32b2318c5a21a593f3ee4b4b4763262a7 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Tue, 19 Jul 2016 18:07:20 +0200 Subject: [PATCH 148/183] techjira:LAAS-60 Feedback:Log LevelLevel=Debug and typo --- .../scala/org/zalando/nakadi/client/scala/Connection.scala | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala index e1c207c..c8d2815 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala @@ -56,10 +56,7 @@ trait Connection { /** * Companion object with factory methods. */ -object Connection { - val RECEIVE_BUFFER_SIZE = 10240 - val EVENT_DELIMITER = "\n" - +object Connection { /** * Creates a new SSL context for usage with connections based on the HTTPS protocol. */ From e6dcb8202917b9114c0f9c4de221b848b900e96d Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Wed, 20 Jul 2016 14:21:48 +0200 Subject: [PATCH 149/183] techjira:LAAS-60 Feedback:Call handler.handleOnError method to notify the listener --- .../client/handler/SubscriptionHandler.scala | 11 ++++++----- .../nakadi/client/scala/MessageHandler.scala | 14 +++++++------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/client/src/main/scala/org/zalando/nakadi/client/handler/SubscriptionHandler.scala b/client/src/main/scala/org/zalando/nakadi/client/handler/SubscriptionHandler.scala index d2c442a..a979fb4 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/handler/SubscriptionHandler.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/handler/SubscriptionHandler.scala @@ -81,7 +81,7 @@ class SubscriptionHandlerImpl(val connection: Connection) extends SubscriptionHa .via(requestCreator(url)) .via(connection.requestFlow()) .buffer(RECEIVE_BUFFER_SIZE, OverflowStrategy.backpressure) - .via(requestRenderer(eventHandler)) + .via(requestRenderer(url, eventHandler)) .withAttributes(ActorAttributes.supervisionStrategy(decider())) //create the pipeline @@ -94,7 +94,7 @@ class SubscriptionHandlerImpl(val connection: Connection) extends SubscriptionHa case Success(requestSource) ⇒ case Failure(e) ⇒ val msg = "An exception occurred: " + e.getMessage - eventHandler.handleOnError(url, Some(msg), e) + eventHandler.handleOnError(url, Some(msg), Some(e)) logger.error(msg + e) connection .actorSystem() @@ -111,15 +111,16 @@ class SubscriptionHandlerImpl(val connection: Connection) extends SubscriptionHa private def requestCreator(url: String): Flow[Option[Cursor], HttpRequest, NotUsed] = Flow[Option[Cursor]].map(withHttpRequest(url, _, None, connection.tokenProvider)) - private def requestRenderer(handler: EventHandler): Flow[HttpResponse, Source[ByteString, Any], NotUsed] = + private def requestRenderer(url:String, handler: EventHandler): Flow[HttpResponse, Source[ByteString, Any], NotUsed] = Flow[HttpResponse].filter { x => if (x.status.isSuccess()) { logger.info("Connection established with success!") x.status.isSuccess() } else { x.entity.toStrict(10.second).map { body => - logger.error(s"http-status: ${x.status.intValue().toString()}, reason[${x.status - .reason()}], body:[${body.data.decodeString("UTF-8")}]") + val msg = s"http-status: ${x.status.intValue().toString()}, reason[${x.status.reason()}], body:[${body.data.decodeString("UTF-8")}]" + logger.error(msg) + handler.handleOnError(null, Some(msg), None) } true // Must return true otherwise reconnection will leave Actors in the unknown... } diff --git a/client/src/main/scala/org/zalando/nakadi/client/scala/MessageHandler.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/MessageHandler.scala index e4ef42e..5a63e01 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/scala/MessageHandler.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/scala/MessageHandler.scala @@ -28,7 +28,7 @@ trait EventHandler { def id(): String def handleOnReceive(eventTypeName: String, msg: String): Either[ErrorResult, Cursor] def handleOnSubscribed(endpoint: String, cursor: Option[Cursor]): Unit - def handleOnError(eventTypeName: String, msg: Option[String], exception: Throwable) + def handleOnError(eventTypeName: String, msg: Option[String], exception: Option[Throwable]) } class ScalaEventHandlerImpl[S <: Event](des: Deserializer[EventStreamBatch[S]], listener: Listener[S]) @@ -61,9 +61,9 @@ class ScalaEventHandlerImpl[S <: Event](des: Deserializer[EventStreamBatch[S]], } - def handleOnError(eventTypeName: String, msg: Option[String], exception: Throwable) = { - val errorMsg = if (msg.isDefined) msg.get else exception.getMessage - val clientError = Some(ClientError(errorMsg, exception = Some(exception))) + def handleOnError(eventTypeName: String, msg: Option[String], exception: Option[Throwable]) = { + val errorMsg = if (msg.isDefined) msg.get else if(exception.isDefined) exception.get.getMessage else "" + val clientError = Some(ClientError(errorMsg, exception = exception)) listener.onError(errorMsg, clientError) } def handleOnSubscribed(endpoint: String, cursor: Option[Cursor]): Unit = @@ -100,9 +100,9 @@ class JavaEventHandlerImpl[J <: JEvent](des: Deserializer[JEventStreamBatch[J]], } } - def handleOnError(eventTypeName: String, msg: Option[String], exception: Throwable) = { - val errorMsg = if (msg.isDefined) msg.get else exception.getMessage - val clientError = Some(ClientError(errorMsg, exception = Some(exception))) + def handleOnError(eventTypeName: String, msg: Option[String], exception: Option[Throwable]) = { + val errorMsg = if (msg.isDefined) msg.get else if(exception.isDefined) exception.get.getMessage else "" + val clientError = Some(ClientError(errorMsg, None, exception)) listener.onError(errorMsg, toJavaClientError(clientError)) } def handleOnSubscribed(endpoint: String, cursor: Option[Cursor]): Unit = { From 29058bb6e0a43d87829355ddaf6c06e0b2005c1a Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Wed, 20 Jul 2016 15:24:44 +0200 Subject: [PATCH 150/183] techjira:LAAS-60 Feedback:Document how to unsubscribe --- .../zalando/nakadi/client/java/Client.java | 384 ++++++++++-------- 1 file changed, 209 insertions(+), 175 deletions(-) diff --git a/api/src/main/java/org/zalando/nakadi/client/java/Client.java b/api/src/main/java/org/zalando/nakadi/client/java/Client.java index 2440083..d2551c6 100644 --- a/api/src/main/java/org/zalando/nakadi/client/java/Client.java +++ b/api/src/main/java/org/zalando/nakadi/client/java/Client.java @@ -18,179 +18,213 @@ public interface Client { - /** - * Retrieves all metric data. - * - * @return metrics data - */ - Future> getMetrics(); - - /** - * Retrieves a list of all registered EventTypes. - * - * @return List of known EventTypes - */ - Future>> getEventTypes(); - - /** - * Creates a new `EventType`. - * - * @param eventType - * The EventType to create - * @return Void in case of success - */ - Future createEventType(EventType eventType); - - /** - * Retrieves the EventType identified by its name. - * - * @param eventTypeName - * The name of the EventType to retrieve. - * @return The EventType if it can be found - */ - Future> getEventType(String eventTypeName); - - /** - * Updates the eventType identified by its name. - * - * @param eventTypeName - * The name of the EventType to update. - * @param eventType - * The eventType to update. - * @return Void in case of success - */ - Future updateEventType(String eventTypeName, EventType eventType); - - /** - * Deletes an eventType identified by its name. - * - * @param eventTypeName - * The name of the EventType to delete. - * @return Void in case of success - */ - Future deleteEventType(String eventTypeName); - - /** - * Publishes a single event to the given eventType using a custom serializer.
          - * - * @param eventTypeName - * The name of the EventType target. - * @param event - * The event to publish. - * @param serializer - * The serializer to use for the serialization of the event. - * @return Void in case of success - */ - Future publishEvent(String eventTypeName, T event, Serializer> serializer); - - /** - * Publishes a single event to the given eventType identified by its name.
          - * - * @param eventTypeName - * The name of the EventType target. - * @param event - * The event to publish - * @param ref - * The Jackson TypeReference of the Event to be used by the default Jackson Marshaller. - * @return Void in case of success - */ - Future publishEvent(String eventTypeName, T event); - - /** - * Publishes a Batch(list) of events to the given eventType, identified by its name, using a custom serializer. - * - * @param eventTypeName - * The name of the EventType target. - * @param events - * The list of events to be published - * @param serializer - * The custom serializer to serialize the events. - * @return Void in case of success - */ - Future publishEvents(String eventTypeName, List events, Serializer> serializer); - - /** - * Publishes a List of events to the given eventType identified by its name. - * - * @param eventTypeName - * The name of the EventType target. - * @param event - * The event to publish - * @return Void in case of success - */ - Future publishEvents(String eventTypeName, List events); - - /** - * Retrieves the existing partitions for the given EventType. - * - * @param eventTypeName - * The name of the EventType target. - * @return list of existing partitions - */ - Future>> getPartitions(String eventTypeName); - - /** - * Retrieves a List of all enrichment strategies supported. - * - * @return list of enrichment strategies - */ - Future>> getEnrichmentStrategies(); - - /** - * Retrieves a List of all partition strategies supported. - * - * @return list of enrichment strategies - */ - Future>> getPartitioningStrategies(); - - /** - * Shuts down the communication system of the client - * - */ - void stop(); - - /** - * Subscribes a listener to the eventType, identified by its name, and start streaming events in a non-blocking - * fashion. - * - * @param eventTypeName - * The name of the EventType target. - * @param parameters - * Parameters for customizing the details of the streaming. - * @param listener - * Listener to pass the event to when it is received. - * @param deserializer - * Deserializer to use for deserializing events. - * @return ClientError in case of failure and Empty Optional in case of success. - */ - Optional subscribe(String eventTypeName, StreamParameters parameters, Listener listener, Deserializer> deserializer); - - /** - * Subscribes a listener to the eventType, identified by its name, and start streaming events in a non-blocking - * fashion. - * - * @param eventTypeName - * The name of the EventType target. - * @param parameters - * Parameters for customizing the details of the streaming. - * @param listener - * Listener to pass the event to when it is received. - * @param typeRef - * TypeReference for unmarshalling with the Jackson ObjectMapper. - * @return ClientError in case of failure and Empty Optional in case of success. - */ - Optional subscribe(String eventTypeName, StreamParameters parameters, Listener listener, TypeReference> typeRef); - - /** - * Removes the subscription of a listener, to stop streaming events from a partition. - * - * @param eventTypeName - * The name of the EventType target. - * @param partition - * The partition assigned to this listener. - * @param listener - * Listener to pass the event to when it is received. - * @return Void in case of success - */ - void unsubscribe(String eventTypeName, Optional partition, Listener listener); + /** + * Retrieves all metric data. + * + * @return metrics data + */ + Future> getMetrics(); + + /** + * Retrieves a list of all registered EventTypes. + * + * @return List of known EventTypes + */ + Future>> getEventTypes(); + + /** + * Creates a new `EventType`. + * + * @param eventType + * The EventType to create + * @return Void in case of success + */ + Future createEventType(EventType eventType); + + /** + * Retrieves the EventType identified by its name. + * + * @param eventTypeName + * The name of the EventType to retrieve. + * @return The EventType if it can be found + */ + Future> getEventType(String eventTypeName); + + /** + * Updates the eventType identified by its name. + * + * @param eventTypeName + * The name of the EventType to update. + * @param eventType + * The eventType to update. + * @return Void in case of success + */ + Future updateEventType(String eventTypeName, EventType eventType); + + /** + * Deletes an eventType identified by its name. + * + * @param eventTypeName + * The name of the EventType to delete. + * @return Void in case of success + */ + Future deleteEventType(String eventTypeName); + + /** + * Publishes a single event to the given eventType using a custom + * serializer.
          + * + * @param eventTypeName + * The name of the EventType target. + * @param event + * The event to publish. + * @param serializer + * The serializer to use for the serialization of the event. + * @return Void in case of success + */ + Future publishEvent(String eventTypeName, T event, + Serializer> serializer); + + /** + * Publishes a single event to the given eventType identified by its name.
          + * + * @param eventTypeName + * The name of the EventType target. + * @param event + * The event to publish + * @param ref + * The Jackson TypeReference of the Event to be used by the + * default Jackson Marshaller. + * @return Void in case of success + */ + Future publishEvent(String eventTypeName, T event); + + /** + * Publishes a Batch(list) of events to the given eventType, identified by + * its name, using a custom serializer. + * + * @param eventTypeName + * The name of the EventType target. + * @param events + * The list of events to be published + * @param serializer + * The custom serializer to serialize the events. + * @return Void in case of success + */ + Future publishEvents(String eventTypeName, + List events, Serializer> serializer); + + /** + * Publishes a List of events to the given eventType identified by its name. + * + * @param eventTypeName + * The name of the EventType target. + * @param event + * The event to publish + * @return Void in case of success + */ + Future publishEvents(String eventTypeName, + List events); + + /** + * Retrieves the existing partitions for the given EventType. + * + * @param eventTypeName + * The name of the EventType target. + * @return list of existing partitions + */ + Future>> getPartitions(String eventTypeName); + + /** + * Retrieves a List of all enrichment strategies supported. + * + * @return list of enrichment strategies + */ + Future>> getEnrichmentStrategies(); + + /** + * Retrieves a List of all partition strategies supported. + * + * @return list of enrichment strategies + */ + Future>> getPartitioningStrategies(); + + /** + * Shuts down the communication system of the client + * + */ + void stop(); + + /** + * Subscribes a listener to the eventType, identified by its name, and start + * streaming events in a non-blocking fashion. + * + * + * @param eventTypeName + * The name of the EventType target. + * @param parameters + * Parameters for customizing the details of the streaming. + * @param listener + * Listener to pass the event to when it is received. + * @param deserializer + * Deserializer to use for deserializing events. + * @return ClientError in case of failure and Empty Optional in case of + * success. + */ + Optional subscribe(String eventTypeName, + StreamParameters parameters, Listener listener, + Deserializer> deserializer); + + /** + * Subscribes a listener to the eventType, identified by its name and start + * streaming events in a non-blocking fashion, where the events are passed + * to the listener. There are 2 types of delivery supported by the Nakadi + * Server and this client:
          + *

          1. FAN-OUT: Stream for a distinct partition(server side fan-out)

          + * To start a stream delivery for a distinct partition, the partition + * parameter must be set(to Optional.of("PartitionNumber")) in the + * Cursor of the StreamParameters.
          + *

          2. FAN-IN: Stream for all partitions together(server side fan-in)

          + * To start a stream delivery for all partitions (fan-in), the partition + * parameter must be empty (set to Optional.empty()) in the Cursor of + * the StreamParameters. + * + * @param eventTypeName + * The name of the EventType target. + * @param parameters + * Parameters for customizing the details of the streaming. + * @param listener + * Listener to pass the event to when it is received. + * @param typeRef + * TypeReference for unmarshalling with the Jackson ObjectMapper. + * @return ClientError in case of failure and Empty Optional in case of + * success. + */ + Optional subscribe(String eventTypeName, + StreamParameters parameters, Listener listener, + TypeReference> typeRef); + + /** + * Removes the subscription of a listener to stop streaming events from a + * earlier created subscription.
          + * To unsubscribe successfully, one has to pass the exact same Partition + * parameter that was used during the subscription.
          + *

          1. Unsubscribing from FAN-IN:

          To unsubscribe from a fan-in + * subscription, the partition parameter must be empty(set to + * Optional.empty()).

          2. Unsubscribing from FAN-OUT:

          To unsubscribe + * from a fan-out subscription, the partition parameter must be set(to + * Optional.of("PartitionNumber")).
          + *

          In case the listener + partition does not match the specification, + * nothing will happen.

          + * + * @param eventTypeName + * The name of the EventType target. + * @param partition + * The partition assigned to this listener. + * @param listener + * Listener to pass the event to when it is received. + * @return Void in case of success + */ + void unsubscribe(String eventTypeName, + Optional partition, Listener listener); } \ No newline at end of file From ce9023d5422159e9096e5ae8497f6e08092d645c Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Wed, 20 Jul 2016 16:14:54 +0200 Subject: [PATCH 151/183] techjira:LAAS-60 Feedback:Check parameters to avoid establishing unnecessary communication attempts. --- .../nakadi/client/java/ClientImpl.java | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java b/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java index 199c127..916808d 100644 --- a/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java +++ b/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java @@ -20,6 +20,8 @@ import sun.reflect.generics.reflectiveObjects.NotImplementedException; import com.fasterxml.jackson.core.type.TypeReference; +import com.google.common.base.Preconditions; +import com.google.common.base.Strings; public class ClientImpl implements Client { private final JavaClientHandler handler; @@ -51,47 +53,59 @@ public Future>> getEventTypes() { @Override public Future createEventType(EventType eventType) { + checkNotNull(eventType, "EventType may not be null"); return handler.post(Uri.URI_EVENT_TYPES(), eventType, eventTypeSerializer); } @Override public Future> getEventType(String eventTypeName) { + checkStringIsNotNullOrEmpty(eventTypeName,"eventTypeName may not be null or empty"); return handler.get(Uri.getEventTypeByName(eventTypeName), eventTypeDeserializer); } @Override public Future updateEventType(String eventTypeName, EventType eventType) { + checkStringIsNotNullOrEmpty(eventTypeName,"eventTypeName may not be null or empty"); + checkNotNull(eventType, "EventType may not be null"); return handler.put(Uri.getEventTypeByName(eventTypeName), eventType, eventTypeSerializer); } @Override public Future deleteEventType(String eventTypeName) { + checkStringIsNotNullOrEmpty(eventTypeName,"eventTypeName may not be null or empty"); return handler.delete(Uri.getEventTypeByName(eventTypeName)); } @Override public Future publishEvent(String eventTypeName, T event, Serializer> serializer) { + checkNotNull(event, "Event may not be null"); return publishEvents(eventTypeName, Arrays.asList(event), serializer); } @Override public Future publishEvent(String eventTypeName, T event) { + checkStringIsNotNullOrEmpty(eventTypeName,"eventTypeName may not be null or empty"); + checkNotNull(event, "Event may not be null"); return publishEvents(eventTypeName, Arrays.asList(event)); } @Override public Future publishEvents(String eventTypeName, List events, Serializer> serializer) { + checkStringIsNotNullOrEmpty(eventTypeName,"eventTypeName may not be null or empty"); + checkListIsNotEmptyOrNull(events, "List of events must not be null or empty"); return handler.post(Uri.getEventStreamingUri(eventTypeName), events, serializer); } @Override public Future publishEvents(String eventTypeName, List events) { + checkListIsNotEmptyOrNull(events, "List of events must not be null or empty"); return publishEvents(eventTypeName, events, SerializationUtils.defaultSerializer()); } @Override public Future>> getPartitions(String eventTypeName) { + checkStringIsNotNullOrEmpty(eventTypeName,"eventTypeName may not be null or empty"); return handler.get(Uri.getPartitions(eventTypeName), seqOfPartitionDeserializer); } @@ -114,6 +128,7 @@ public void stop() { @Override public Optional subscribe(String eventTypeName, StreamParameters parameters, Listener listener, Deserializer> deserializer) { + checkStringIsNotNullOrEmpty(eventTypeName,"eventTypeName may not be null or empty"); return handler.subscribe(eventTypeName, Uri.getEventStreamingUri(eventTypeName), parameters, listener, deserializer); } @@ -121,13 +136,28 @@ public Optional subscribe(String eventTypeName, S @Override public Optional subscribe(String eventTypeName, StreamParameters parameters, Listener listener, TypeReference> typeRef) { + checkStringIsNotNullOrEmpty(eventTypeName,"eventTypeName may not be null or empty"); + checkNotNull(listener, "Listener may not be null"); return handler.subscribe(eventTypeName, Uri.getEventStreamingUri(eventTypeName), parameters, listener, SerializationUtils.withCustomDeserializer(typeRef)); } @Override public void unsubscribe(String eventTypeName, Optional partition, Listener listener) { + checkStringIsNotNullOrEmpty(eventTypeName,"eventTypeName may not be null or empty"); + checkNotNull(listener, "Listener may not be null"); handler.unsubscribe(eventTypeName, partition, listener); } + + private void checkListIsNotEmptyOrNull(List l, String message){ + Preconditions.checkArgument(l!=null&&!l.isEmpty(), message); + + } + private void checkStringIsNotNullOrEmpty(String in, String message){ + Preconditions.checkArgument(!Strings.isNullOrEmpty(in), message); + } + private void checkNotNull(T obj, String message){ + Preconditions.checkNotNull(obj,message); + } } \ No newline at end of file From 8e17b7960544556915bbd97b0cdc31e53c2a8b84 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Wed, 20 Jul 2016 17:13:47 +0200 Subject: [PATCH 152/183] techjira:LAAS-60 Updating the documentation #62. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bba936c..b4511a1 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ Please note that the client provides a Scala as well as a Java interface. ## Note -* The new client (version >= '2.0.0') is still in development and not production ready. +* The new client has pre-released versions that can be found [here](https://github.com/zalando/nakadi-klients/releases). * The new client documentation can be found in the [wiki](https://github.com/zalando/nakadi-klients/wiki). From 44f5c96e2302ed321757bd822c005ea791f4c421 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Wed, 20 Jul 2016 17:22:05 +0200 Subject: [PATCH 153/183] techjira:LAAS-60 Development release 2.0.0-pre-alpha.19 --- project/Build.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/Build.scala b/project/Build.scala index 91e43af..dbb8a5b 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -62,7 +62,7 @@ lazy val client = withDefaults( .settings( name := projectName, organization := "org.zalando.nakadi.client", - version := "2.0.0-pre-alpha.18", + version := "2.0.0-pre-alpha.19", crossPaths := false, scalaVersion := "2.11.8", findbugsReportType := Some(ReportType.FancyHtml), From f8f9a81753956ab7a11044d90c6673e0637804b3 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Thu, 21 Jul 2016 07:38:24 +0200 Subject: [PATCH 154/183] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b4511a1..3fbf808 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -Nakadi Klients (IN DEVELOPMENT) +Nakadi Klients ============== Implementation of a non blocking client accessing the low level API of the [Nakadi event bus](https://github.com/zalando/nakadi). Internally, it uses [Akka](http://akka.io/) and [Akka Http](http://doc.akka.io/docs/akka-stream-and-http-experimental/2.0.2/scala/http/) to implement its communication tasks. From cd53bd61aef281d99bbf134fb473ca23fbb81543 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Thu, 21 Jul 2016 13:08:33 +0200 Subject: [PATCH 155/183] techjira:LAAS-60 added script to start nakadi for integration tests --- it/src/main/resources/nakadi.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 it/src/main/resources/nakadi.sh diff --git a/it/src/main/resources/nakadi.sh b/it/src/main/resources/nakadi.sh new file mode 100644 index 0000000..e69de29 From be28273ea2935c8600bb6c568284f75d6bf05b2b Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Thu, 21 Jul 2016 13:11:19 +0200 Subject: [PATCH 156/183] techjira:LAAS-60 Added execution of nakadi startup script to Travis. #69 --- .travis.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 6bc3049..04c863a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -24,16 +24,14 @@ before_install: - sudo apt-get update - sudo apt-key update - sudo apt-get -qqy -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" install docker-engine=${DOCKER_ENGINE_VERSION}-0~precise - - sudo rm /usr/local/bin/docker-compose - curl -L https://github.com/docker/compose/releases/download/${DOCKER_COMPOSE_VERSION}/docker-compose-`uname -s`-`uname -m` > docker-compose - chmod +x docker-compose - sudo mv docker-compose /usr/local/bin - - curl -o- https://raw.githubusercontent.com/zalando/reactive-nakadi/v0.0.04/src/it/resources/nakadi.sh | bash - before_script: - sudo /etc/init.d/postgresql stop + - bash .it/src/main/resources/nakadi.sh script: - sbt clean test From f225ada9797a09ba8538a45b53c7af852db8f61c Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Thu, 21 Jul 2016 14:23:17 +0200 Subject: [PATCH 157/183] techjira:LAAS-60 Removed unused imports. #95 --- .../main/java/org/zalando/nakadi/client/java/ClientImpl.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java b/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java index 916808d..8486bab 100644 --- a/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java +++ b/client/src/main/java/org/zalando/nakadi/client/java/ClientImpl.java @@ -16,9 +16,6 @@ import org.zalando.nakadi.client.java.model.Partition; import org.zalando.nakadi.client.java.utils.SerializationUtils; import org.zalando.nakadi.client.utils.Uri; - -import sun.reflect.generics.reflectiveObjects.NotImplementedException; - import com.fasterxml.jackson.core.type.TypeReference; import com.google.common.base.Preconditions; import com.google.common.base.Strings; From 222d393604feca4e0dd77fc0f8fd3950e995e192 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Thu, 21 Jul 2016 14:28:19 +0200 Subject: [PATCH 158/183] techjira:LAAS-60 added script to start nakadi for integration tests --- it/src/main/resources/nakadi.sh | 94 +++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) diff --git a/it/src/main/resources/nakadi.sh b/it/src/main/resources/nakadi.sh index e69de29..d84a342 100644 --- a/it/src/main/resources/nakadi.sh +++ b/it/src/main/resources/nakadi.sh @@ -0,0 +1,94 @@ +#!/bin/bash + +# colors +OK="\033[32m" +FAIL="\033[31m" +RESET="\033[0m" + +GIT=`which git` +DIRECTORY=/tmp/nakadi +REPO="https://github.com/zalando/nakadi" +NAKADI_PORT="8080" +NAKADI_ALIVE_TIMEOUT=120 + +function validate { + + [ -z "$DOCKER_IP" ] && { + echo -e "You need to set DOCKER_IP env variable ${FAIL}✗${RESET}" + exit 1; + } + + [ "$GIT" = "" ] && { + echo "Git can't be found in your system" + echo -ne " ${OK}suggestion${RESET}: run '" + [ x$(uname) = "xLinux" ] && { + [ x$(which apt-get) != "x" ] && { + echo "apt-get install git' to install it" + } || { + echo "yum install git' to install it" + } + } || { + echo "brew install git' to install it" + } + echo "" + exit 1 + } +} + +function start_nakadi { + echo -n "About to clone Nakadi... " + rm -rf $DIRECTORY/ + $GIT clone $REPO $DIRECTORY + echo -e "Cloned Nakadi to ${DIRECTORY} ${OK}✔${RESET}" + + echo -n "Editing config $DIRECTORY/src/main/resources/application.yml... " + sed -i "" "s/localhost:5432/$DOCKER_IP:5432/g" $DIRECTORY/src/main/resources/application.yml + echo -e "Done! ${OK}✔${RESET}" + + echo -n "Building Nakadi... " + export PUBLISH_NAKADI_PORT="-p $NAKADI_PORT:$NAKADI_PORT" + cd $DIRECTORY/ + ./gradlew startDockerContainer + cd - + + echo -n "Waiting on Nakadi to start (Polling http://$DOCKER_IP:8080/health) " + poll_counter=0 + until $(curl --silent --output /dev/null http://$DOCKER_IP:8080/health); do + sleep 1 + echo -n ". " + poll_counter=$((poll_counter+1)) + [ "$poll_counter" -eq "$NAKADI_ALIVE_TIMEOUT" ] && { + echo -e "Nakadi wait timeout reached ${FAIL}✗${RESET}" + exit 1; + } + done; + echo -e "Nakadi started ${OK}✔${RESET}" +} + +function stop_nakadi { + echo -n "Stopping Nakadi... " + cd $DIRECTORY/ + ./gradlew stopAndRemoveDockerContainer + cd - + rm -rf $DIRECTORY/ + echo -e "Nakadi stopped ${OK}✔${RESET}" +} + +function echo_usage { + echo "Nakadi Docker helper script" + echo $"Usage $0 {build-nakadi|remove-nakadi}" + exit 1 +} + +case $1 in + stop) + validate + stop_nakadi + ;; + start) + validate + start_nakadi + ;; + *) + echo_usage +esac From 5df218b0bb3be3687ed2c6fd5f99931022c1d66f Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Thu, 21 Jul 2016 14:44:14 +0200 Subject: [PATCH 159/183] techjira:LAAS-60 added script to start nakadi for integration tests --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 04c863a..6033743 100644 --- a/.travis.yml +++ b/.travis.yml @@ -31,7 +31,7 @@ before_install: before_script: - sudo /etc/init.d/postgresql stop - - bash .it/src/main/resources/nakadi.sh + - bash /it/src/main/resources/nakadi.sh script: - sbt clean test From 0ffecfb2cf4fc428b2c5c148f049b606b36cff33 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Thu, 21 Jul 2016 14:54:14 +0200 Subject: [PATCH 160/183] techjira:LAAS-60 added script to start nakadi for integration tests --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 6033743..83b9889 100644 --- a/.travis.yml +++ b/.travis.yml @@ -31,7 +31,7 @@ before_install: before_script: - sudo /etc/init.d/postgresql stop - - bash /it/src/main/resources/nakadi.sh + - bash ./it/src/main/resources/nakadi.sh script: - sbt clean test From fb8e91beed87f03824d8768057a51df9b802aac3 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Thu, 21 Jul 2016 15:08:06 +0200 Subject: [PATCH 161/183] techjira:LAAS-60 Script needs a parameter --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 83b9889..30bfed1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -31,7 +31,7 @@ before_install: before_script: - sudo /etc/init.d/postgresql stop - - bash ./it/src/main/resources/nakadi.sh + - bash ./it/src/main/resources/nakadi.sh start script: - sbt clean test From 6d837682c3b076cc795f4850c48bbf5dafa5c790 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Thu, 21 Jul 2016 15:14:49 +0200 Subject: [PATCH 162/183] techjira:LAAS-60 Added stop command and matrix --- .travis.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.travis.yml b/.travis.yml index 30bfed1..a5d12b9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,6 +6,8 @@ env: DOCKER_IP: 127.0.0.1 DOCKER_COMPOSE_VERSION: 1.7.1 DOCKER_ENGINE_VERSION: 1.11.1 + matrix: + - DOCKER_IP=127.0.0.1 jdk: - openjdk8 @@ -35,3 +37,6 @@ before_script: script: - sbt clean test + +after_script: + - bash ./it/src/main/resources/nakadi.sh stop From 09cbc511ff224abcbf3e967a17ec7b3d2bc1bef1 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Thu, 21 Jul 2016 15:24:43 +0200 Subject: [PATCH 163/183] techjira:LAAS-60 Starting nakadi needs a different command --- it/src/main/resources/nakadi.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/it/src/main/resources/nakadi.sh b/it/src/main/resources/nakadi.sh index d84a342..c39e309 100644 --- a/it/src/main/resources/nakadi.sh +++ b/it/src/main/resources/nakadi.sh @@ -48,7 +48,7 @@ function start_nakadi { echo -n "Building Nakadi... " export PUBLISH_NAKADI_PORT="-p $NAKADI_PORT:$NAKADI_PORT" cd $DIRECTORY/ - ./gradlew startDockerContainer + ./gradlew startNakadi cd - echo -n "Waiting on Nakadi to start (Polling http://$DOCKER_IP:8080/health) " From 30a2491a821e62ba02bd1d3482cf1260ea9cf1d2 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Thu, 21 Jul 2016 15:32:14 +0200 Subject: [PATCH 164/183] techjira:LAAS-60 Added the right script now.... --- it/src/main/resources/nakadi.sh | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/it/src/main/resources/nakadi.sh b/it/src/main/resources/nakadi.sh index c39e309..45702f6 100644 --- a/it/src/main/resources/nakadi.sh +++ b/it/src/main/resources/nakadi.sh @@ -6,10 +6,11 @@ FAIL="\033[31m" RESET="\033[0m" GIT=`which git` +DOCKER_COMPOSE=`which docker-compose` DIRECTORY=/tmp/nakadi REPO="https://github.com/zalando/nakadi" NAKADI_PORT="8080" -NAKADI_ALIVE_TIMEOUT=120 +NAKADI_ALIVE_TIMEOUT=240 function validate { @@ -18,6 +19,11 @@ function validate { exit 1; } + [ "$DOCKER_COMPOSE" = "" ] && { + echo -e "You need to install docker-compose ${FAIL}✗${RESET}" + exit 1; + } + [ "$GIT" = "" ] && { echo "Git can't be found in your system" echo -ne " ${OK}suggestion${RESET}: run '" @@ -41,14 +47,16 @@ function start_nakadi { $GIT clone $REPO $DIRECTORY echo -e "Cloned Nakadi to ${DIRECTORY} ${OK}✔${RESET}" - echo -n "Editing config $DIRECTORY/src/main/resources/application.yml... " - sed -i "" "s/localhost:5432/$DOCKER_IP:5432/g" $DIRECTORY/src/main/resources/application.yml + echo -n "Editing some configs in $DIRECTORY... " + sed -i "" "s/localhost/$DOCKER_IP/g" $DIRECTORY/build.gradle + sed -i "" "s/localhost/$DOCKER_IP/g" $DIRECTORY/docker-compose.yml + sed -i "" "s/localhost/$DOCKER_IP/g" $DIRECTORY/src/main/resources/application.yml echo -e "Done! ${OK}✔${RESET}" echo -n "Building Nakadi... " - export PUBLISH_NAKADI_PORT="-p $NAKADI_PORT:$NAKADI_PORT" cd $DIRECTORY/ - ./gradlew startNakadi + ./gradlew assemble + $DOCKER_COMPOSE up -d cd - echo -n "Waiting on Nakadi to start (Polling http://$DOCKER_IP:8080/health) " @@ -62,13 +70,14 @@ function start_nakadi { exit 1; } done; + echo -e "Nakadi started ${OK}✔${RESET}" } function stop_nakadi { echo -n "Stopping Nakadi... " cd $DIRECTORY/ - ./gradlew stopAndRemoveDockerContainer + $DOCKER_COMPOSE down cd - rm -rf $DIRECTORY/ echo -e "Nakadi stopped ${OK}✔${RESET}" From 54566790da94cc52c476eb24d5cd036f836cc965 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Thu, 21 Jul 2016 15:36:51 +0200 Subject: [PATCH 165/183] techjira:LAAS-60 Travis from reactive-nakadi as well! --- .travis.yml | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/.travis.yml b/.travis.yml index a5d12b9..0366b04 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,16 +1,11 @@ language: scala -scala: - - 2.11.8 - -env: - DOCKER_IP: 127.0.0.1 - DOCKER_COMPOSE_VERSION: 1.7.1 - DOCKER_ENGINE_VERSION: 1.11.1 - matrix: - - DOCKER_IP=127.0.0.1 +sudo: required jdk: - - openjdk8 + - oraclejdk8 + +scala: + - 2.11.7 services: - docker @@ -19,15 +14,22 @@ services: addons: hostname: localhost -# Travis has an old version of docker and docker-compose that is why we need to load from the net and install them. +env: + matrix: + - DOCKER_IP=127.0.0.1 + + global: + - secure: pucnGYKmiGHmjIx7yf3HUhsvzjAJwE2vhvFcOvPYwy8PoIG51nvDSpgv5criK5RCaE2/t06hZjvjvRQ04FA/vrgex+NxBJNgEZ33Lmjdx2ncYCnvVDmWY43cJva3fMznFny0wifPvs9e73Yp75tTiZctCY0iiMDhyd9tCHOhRYs2Dwr1Sg5QBF616rgZRiY9iYWyo0TR1IEB8diBvTX5WFWJQtMcAF2U3BhP8SS44ds7W5Abz/vWcQJI27lnqUQstfEGkZiWMTLEGakmhG4NApBEwPT8tFIEJnF8UmHF/pO1hNM22Ge7kaVAN/X2Q/wvXdmsB5bAMVFWpgI52ZfMNQwcQc0gXHA7+aQ+Hz5Fe0tbyWOxldx3Vr8ZZLUl1Ag0yFfaY/BKIcrDKgrpSmYeCaN5QyFDgv4itUTNL03qtYg3PZDS8lljI2+WOZQ86EpN+tFz2DObBB7bNMVMxSibM1+Tdy46Rft8h17KX/jla9StAYY78DlwRlAQbVBM+eGybNtQSp7QYAyOpSPdcGyQUOweBP+Ht57wPE2VNuJToYn00t+2XCyMLU9Bx5UTJx+FwhaLfkMW+3S8cMRgbNXWzz9SA/rSNsbc5AwfO2OrLnFqSgo1OAK3K/GBXIiFnlBgrMAvk6PH05aHf0HExBXIR+X1x2mT63zN9M1gL7vt9h4= + - secure: RcTCf1WRsZfw3lEuUE+WS96cUZ0kuYxdgFy0CVrCB7oR7GZd6jxupJgJsbpG/29XlAWyhwIthmWof/tU/3lS+OKFKKXw7qjpc/nUQwKwC6RvDnHt1w+4GbUh1NiNcuIlMMnquWFlAmQ9ubs86nIfez0WP+RVWy4KK8i9H1R1wGdw5DD+dlZ5Ckm0qSaBPrpBC7ML/17/JD5T9UDPAB5G3qYJ6w9tmvMLx9xEsGG7b79plIu9xGpVWVZqoidOQCN9fb1WmmdGkC0EwWb+b7cZ1JgTB+/wdOyEJpaabKVj87jS/B1kAZWw5LhMXQPcee4TxKlIBBbIme3aoxrqqJ+4qz8GCuvgRdqi9DyJL3i3EVfoDeje4gjzka9IetV8cJ4n1tYnEdt6Ft2zwMlTa6SggJR4dc142IuG7W/nmlHbLOnZuy8XYgk2GHlnUcEpOI3FQ7ln59EPPKBKla+YO8BiNGTum4DHHaMcvhv5976Ho3Z8d7+vgT0IpJChvBBuGOrcjFHTStABFt9S78xqjXmR2aOXe9SNYUi4bQX4DWHAb+3sDjofVb+xnRCCkrxZ4+YUgmj2pJhrAnDTsgFCvDwl4UaqAyhcjdSQukTIg0+O15/Oj/lSMpRarFgVezyootP6wzoVGR/lc/B0w243xTW4NsX5nKb4AgMBpsDYB3YCDug= + before_install: - sudo sh -c 'echo "deb https://apt.dockerproject.org/repo ubuntu-precise main" > /etc/apt/sources.list.d/docker.list' - sudo apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D - sudo apt-get update - sudo apt-key update - - sudo apt-get -qqy -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" install docker-engine=${DOCKER_ENGINE_VERSION}-0~precise - - sudo rm /usr/local/bin/docker-compose - - curl -L https://github.com/docker/compose/releases/download/${DOCKER_COMPOSE_VERSION}/docker-compose-`uname -s`-`uname -m` > docker-compose + - sudo apt-get -qqy -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" install docker-engine=1.11.1-0~precise + - sudo rm -f /usr/local/bin/docker-compose + - curl --verbos -L https://github.com/docker/compose/releases/download/1.7.1/docker-compose-`uname -s`-`uname -m` > docker-compose - chmod +x docker-compose - sudo mv docker-compose /usr/local/bin From 25b91ae4d43efa366d97589f290baef7379c21aa Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Thu, 21 Jul 2016 15:48:25 +0200 Subject: [PATCH 166/183] techjira:LAAS-60 Enabling it-integration by default. #69 --- project/Build.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/Build.scala b/project/Build.scala index dbb8a5b..40b295e 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -39,7 +39,7 @@ val defaultOptions= Seq( lazy val root = project.in(file(".")) .settings(publishTo := whereToPublishTo(isSnapshot.value)) - .aggregate(api, client) + .aggregate(api, client, it) lazy val api = withDefaults( "nakadi-klients-api", From c935a9655b077ae18a5edbf5baf6fe1dfd4e95d1 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Fri, 22 Jul 2016 13:07:04 +0200 Subject: [PATCH 167/183] tech-jira:LAAS-60 WIP --- client/src/main/resources/reference.conf | 4 +- .../zalando/nakadi/client/utils/Conf.scala | 53 +++++++++++++++++++ 2 files changed, 56 insertions(+), 1 deletion(-) create mode 100644 client/src/main/scala/org/zalando/nakadi/client/utils/Conf.scala diff --git a/client/src/main/resources/reference.conf b/client/src/main/resources/reference.conf index 4dc06a2..c6676b5 100644 --- a/client/src/main/resources/reference.conf +++ b/client/src/main/resources/reference.conf @@ -9,11 +9,13 @@ akka { nakadi.client { + receiveBufferSize = 1024 bytes + retryTimeout = 10 // tbd. better names & organization into groups, maybe? // noListenerReconnectDelay = 10 seconds // pollParallelism = 100 - // receiveBufferSize = 1024 bytes + // // // defaultBatchFlushTimeout = 5 seconds // was client.DEFAULT_BATCH_FLUSH_TIMEOUT_IN_SECONDS // defaultBatchLimit = 1 // was client.DEFAULT_BATCH_LIMIT diff --git a/client/src/main/scala/org/zalando/nakadi/client/utils/Conf.scala b/client/src/main/scala/org/zalando/nakadi/client/utils/Conf.scala new file mode 100644 index 0000000..c2d7c8c --- /dev/null +++ b/client/src/main/scala/org/zalando/nakadi/client/utils/Conf.scala @@ -0,0 +1,53 @@ +package org.zalando.nakadi.client.utils + +import com.typesafe.config.ConfigFactory +import com.typesafe.config.Config +import akka.util.Timeout +import scala.concurrent.duration.Duration +import scala.language.implicitConversions + +/* +* Access to the configuration in a typed fashion. +* +* Also ensures that the configuration is healthy during the process run. Any parsing errors or inconsistencies will +* be found during launch. +* +* Note: We're exposing durations as 'scala.concurrent.duration.Duration' (instead of 'java.time.Duration') for example +* because the Scala version has '.toSeconds' but Java version doesn't. AKa110216 +*/ +object Conf { + private + val root = ConfigFactory.load.getConfig("nakadi.client") + + // Typesafe Config is Java/Scala compatible and thus stores durations as 'java.time.Duration'. + // See -> http://stackoverflow.com/questions/32076311/converting-java-to-scala-durations + // + private implicit + def convDuration(v: java.time.Duration) = scala.concurrent.duration.Duration.fromNanos(v.toNanos) + + val noListenerReconnectDelay: Duration = root.getDuration("noListenerReconnectDelay") + val pollParallelism = root.getInt("pollParallelism") + val receiveBufferSize = root.getMemorySize("receiveBufferSize") + + + val defaultBatchFlushTimeout: Duration = root.getDuration("defaultBatchFlushTimeout") + val defaultBatchLimit = root.getInt("defaultBatchLimit") + val defaultStreamLimit = root.getInt("defaultStreamLimit") + + class cSupervisor(cfg: Config) { + val maxNrOfRetries= cfg.getInt("maxNrOfRetries") + val withinTimeRange: Duration = cfg.getDuration("withinTimeRange") + + val resolveActorTimeout = Timeout( cfg.getDuration("resolveActorTimeout") ) + } + val supervisor= new cSupervisor( root.getConfig("supervisor") ) + + // ^^^ new entries above, please ^^^ + + // Note: If we need to check config value ranges or consistency between multiple values, here's the place. + // Just throw an exception if something's not right. + // + if (false) { + throw new RuntimeException( "Bad config: ..." ) + } +} \ No newline at end of file From 34b4147e894cde5e4d4eba83a7c48ba791e92df0 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Mon, 25 Jul 2016 09:07:23 +0200 Subject: [PATCH 168/183] techjira:LAAS-60 ReceiveBufferSize and RetryTimeout are now configurable. #55 --- client/src/main/resources/reference.conf | 29 +----------- .../client/handler/SubscriptionHandler.scala | 8 ++-- .../zalando/nakadi/client/utils/Conf.scala | 46 ++++++++----------- .../nakadi/client/utils/ConfTest.scala | 21 +++++++++ it/src/test/resources/reference.conf | 21 --------- 5 files changed, 44 insertions(+), 81 deletions(-) create mode 100644 client/src/test/scala/org/zalando/nakadi/client/utils/ConfTest.scala delete mode 100644 it/src/test/resources/reference.conf diff --git a/client/src/main/resources/reference.conf b/client/src/main/resources/reference.conf index c6676b5..0d1900c 100644 --- a/client/src/main/resources/reference.conf +++ b/client/src/main/resources/reference.conf @@ -1,7 +1,3 @@ -// -// reference.conf -// - akka { loggers = ["akka.event.slf4j.Slf4jLogger"] loglevel = "DEBUG" @@ -9,30 +5,7 @@ akka { nakadi.client { - receiveBufferSize = 1024 bytes + receiveBufferSize = 40960 retryTimeout = 10 - // tbd. better names & organization into groups, maybe? - - // noListenerReconnectDelay = 10 seconds - // pollParallelism = 100 - // - // - // defaultBatchFlushTimeout = 5 seconds // was client.DEFAULT_BATCH_FLUSH_TIMEOUT_IN_SECONDS - // defaultBatchLimit = 1 // was client.DEFAULT_BATCH_LIMIT - // defaultStreamLimit = 0 // was client.DEFAULT_STREAM_LIMIT - // - // scoopListener { - // selectorField = "id" - // } - // - // supervisor { - // // note: Supervisor strategy parameter names are from the Akka - keep them like this - // maxNrOfRetries = 100 - // withinTimeRange = 5 minutes - // - // resolveActorTimeout = 1 second - // } - } -akka.cluster.metrics.enabled=false diff --git a/client/src/main/scala/org/zalando/nakadi/client/handler/SubscriptionHandler.scala b/client/src/main/scala/org/zalando/nakadi/client/handler/SubscriptionHandler.scala index a979fb4..5c668e6 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/handler/SubscriptionHandler.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/handler/SubscriptionHandler.scala @@ -31,6 +31,7 @@ import akka.stream.scaladsl.Framing import akka.stream.scaladsl.Sink import akka.stream.scaladsl.Source import akka.util.ByteString +import org.zalando.nakadi.client.utils.Conf trait SubscriptionHandler { @@ -54,7 +55,6 @@ class SubscriptionHandlerImpl(val connection: Connection) extends SubscriptionHa private val supervisingActor = connection.actorSystem .actorOf(Props(classOf[SupervisingActor], connection, this), "SupervisingActor" + System.currentTimeMillis()) - private val RECEIVE_BUFFER_SIZE = 40960 private val EVENT_DELIMITER = "\n" val logger = LoggerFactory.getLogger(this.getClass) @@ -80,7 +80,7 @@ class SubscriptionHandlerImpl(val connection: Connection) extends SubscriptionHa val requestFlow = Flow[Option[Cursor]] .via(requestCreator(url)) .via(connection.requestFlow()) - .buffer(RECEIVE_BUFFER_SIZE, OverflowStrategy.backpressure) + .buffer(Conf.receiveBufferSize, OverflowStrategy.backpressure) .via(requestRenderer(url, eventHandler)) .withAttributes(ActorAttributes.supervisionStrategy(decider())) @@ -99,7 +99,7 @@ class SubscriptionHandlerImpl(val connection: Connection) extends SubscriptionHa connection .actorSystem() .scheduler - .scheduleOnce(5.seconds)(stopActor(consumingActor)) //TODO: Make it configurable + .scheduleOnce(Conf.retryTimeout.seconds)(stopActor(consumingActor)) //TODO: Make it configurable } } @@ -129,5 +129,5 @@ class SubscriptionHandlerImpl(val connection: Connection) extends SubscriptionHa private def delimiterFlow = Flow[ByteString].via( Framing - .delimiter(ByteString(EVENT_DELIMITER), maximumFrameLength = RECEIVE_BUFFER_SIZE, allowTruncation = true)) + .delimiter(ByteString(EVENT_DELIMITER), maximumFrameLength = Conf.receiveBufferSize, allowTruncation = true)) } diff --git a/client/src/main/scala/org/zalando/nakadi/client/utils/Conf.scala b/client/src/main/scala/org/zalando/nakadi/client/utils/Conf.scala index c2d7c8c..4e87080 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/utils/Conf.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/utils/Conf.scala @@ -16,38 +16,28 @@ import scala.language.implicitConversions * because the Scala version has '.toSeconds' but Java version doesn't. AKa110216 */ object Conf { - private - val root = ConfigFactory.load.getConfig("nakadi.client") + private val root = ConfigFactory.load.getConfig("nakadi.client") // Typesafe Config is Java/Scala compatible and thus stores durations as 'java.time.Duration'. // See -> http://stackoverflow.com/questions/32076311/converting-java-to-scala-durations // - private implicit - def convDuration(v: java.time.Duration) = scala.concurrent.duration.Duration.fromNanos(v.toNanos) - - val noListenerReconnectDelay: Duration = root.getDuration("noListenerReconnectDelay") - val pollParallelism = root.getInt("pollParallelism") - val receiveBufferSize = root.getMemorySize("receiveBufferSize") - - - val defaultBatchFlushTimeout: Duration = root.getDuration("defaultBatchFlushTimeout") - val defaultBatchLimit = root.getInt("defaultBatchLimit") - val defaultStreamLimit = root.getInt("defaultStreamLimit") - - class cSupervisor(cfg: Config) { - val maxNrOfRetries= cfg.getInt("maxNrOfRetries") - val withinTimeRange: Duration = cfg.getDuration("withinTimeRange") - - val resolveActorTimeout = Timeout( cfg.getDuration("resolveActorTimeout") ) + private implicit def convDuration(v: java.time.Duration) = scala.concurrent.duration.Duration.fromNanos(v.toNanos) + + + val receiveBufferSize = root.getInt("receiveBufferSize") + val retryTimeout = root.getInt("retryTimeout") + + def validateConfigurations(){ + val msg = "%s must be configured in reference.conf file !" + if(receiveBufferSize<0){ + throw new IllegalStateException(receiveBufferSize.formatted("Size of receive buffer(receiveBufferSize)")); + } + if(retryTimeout<0){ + throw new IllegalStateException(receiveBufferSize.formatted("Retry timeout(retryTimeout)")); + } + } - val supervisor= new cSupervisor( root.getConfig("supervisor") ) - - // ^^^ new entries above, please ^^^ + + validateConfigurations() - // Note: If we need to check config value ranges or consistency between multiple values, here's the place. - // Just throw an exception if something's not right. - // - if (false) { - throw new RuntimeException( "Bad config: ..." ) - } } \ No newline at end of file diff --git a/client/src/test/scala/org/zalando/nakadi/client/utils/ConfTest.scala b/client/src/test/scala/org/zalando/nakadi/client/utils/ConfTest.scala new file mode 100644 index 0000000..5b57874 --- /dev/null +++ b/client/src/test/scala/org/zalando/nakadi/client/utils/ConfTest.scala @@ -0,0 +1,21 @@ +package org.zalando.nakadi.client.utils + +import java.util.Optional + +import org.mockito.Mockito.reset +import org.scalatest.BeforeAndAfter +import org.scalatest.FlatSpec +import org.scalatest.Matchers +import org.scalatest.WordSpec +import org.scalatest.mock.MockitoSugar + +class ConfTest extends FlatSpec with Matchers with MockitoSugar { + + "Conf.retryTimeout" should "return 10 (integer)" in { + assert(Conf.retryTimeout == 10) + } + "Conf.receiveBufferSize" should "return 40960 (integer)" in { + assert(Conf.receiveBufferSize == 40960) + } + +} \ No newline at end of file diff --git a/it/src/test/resources/reference.conf b/it/src/test/resources/reference.conf deleted file mode 100644 index 0faa71a..0000000 --- a/it/src/test/resources/reference.conf +++ /dev/null @@ -1,21 +0,0 @@ -akka { - loglevel = "INFO" - stdout-loglevel = "INFO" - - actor { - debug { - # enable DEBUG logging of actor lifecycle changes - lifecycle = on - unhandled = on - } - } - -} - - -# Enable metrics extension in akka-cluster-metrics. -#akka.extensions=["akka.cluster.metrics.ClusterMetricsExtension"] - -# Sigar native library extract location during tests. -# Note: use per-jvm-instance folder when running multiple jvm on one host. -#akka.cluster.metrics.native-library-extract-folder=${user.dir}/target/native From e88e1f75b7748fcb4884e9b91513ec66db6b4f4d Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Mon, 25 Jul 2016 10:56:43 +0200 Subject: [PATCH 169/183] techjira:LAAS-60 Remove TODO. #55 --- .../org/zalando/nakadi/client/handler/SubscriptionHandler.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/main/scala/org/zalando/nakadi/client/handler/SubscriptionHandler.scala b/client/src/main/scala/org/zalando/nakadi/client/handler/SubscriptionHandler.scala index 5c668e6..e5ae191 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/handler/SubscriptionHandler.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/handler/SubscriptionHandler.scala @@ -99,7 +99,7 @@ class SubscriptionHandlerImpl(val connection: Connection) extends SubscriptionHa connection .actorSystem() .scheduler - .scheduleOnce(Conf.retryTimeout.seconds)(stopActor(consumingActor)) //TODO: Make it configurable + .scheduleOnce(Conf.retryTimeout.seconds)(stopActor(consumingActor)) } } From 519c0e51519ab1c8f36613b770cc21339ddd9ea0 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Mon, 25 Jul 2016 15:21:39 +0200 Subject: [PATCH 170/183] techjira:LAAS-60 Removing logback hard dependency. #82 --- project/Dependencies.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/project/Dependencies.scala b/project/Dependencies.scala index 18c0720..63a96d9 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -20,13 +20,13 @@ object Dependencies { "com.google.guava" % "guava" % "19.0", "com.fasterxml.jackson.core" % "jackson-core" % jacksonVersion, "com.fasterxml.jackson.module" %% "jackson-module-scala" % jacksonVersion, - "ch.qos.logback" % "logback-classic" % "1.1.7", - "ch.qos.logback" % "logback-core" % "1.1.7", "com.typesafe.akka" %% "akka-actor" % akkaVersion, "com.typesafe.akka" %% "akka-http-experimental" % akkaVersion, "com.typesafe.akka" %% "akka-stream" % akkaVersion, "org.slf4j" % "slf4j-api" % "1.7.21", "com.typesafe.akka" % "akka-slf4j_2.11" % "2.4.7", + "ch.qos.logback" % "logback-classic" % "1.1.7" % "test", + "ch.qos.logback" % "logback-core" % "1.1.7" % "test", "com.typesafe.akka" %% "akka-testkit" % akkaVersion % "test", "org.scalatest" %% "scalatest" % scalaTestVersion % "test", "com.google.code.findbugs" % "jsr305" % "3.0.0" % "test", From 92fae93dff9c2cee0e32d865d77d7d2969ec3d03 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Mon, 25 Jul 2016 15:22:41 +0200 Subject: [PATCH 171/183] techjira:LAAS-60 Development release 2.0.0-pre-alpha.20 --- project/Build.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/Build.scala b/project/Build.scala index 40b295e..f702d55 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -62,7 +62,7 @@ lazy val client = withDefaults( .settings( name := projectName, organization := "org.zalando.nakadi.client", - version := "2.0.0-pre-alpha.19", + version := "2.0.0-pre-alpha.20", crossPaths := false, scalaVersion := "2.11.8", findbugsReportType := Some(ReportType.FancyHtml), From b7fadcba09c33d4081b1f0d263a7be0f70224e03 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Tue, 26 Jul 2016 07:37:48 +0200 Subject: [PATCH 172/183] techjira:LAAS-60 Alpha-release 2.0.0-alpha.20 --- project/Build.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/Build.scala b/project/Build.scala index f702d55..db85a70 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -62,7 +62,7 @@ lazy val client = withDefaults( .settings( name := projectName, organization := "org.zalando.nakadi.client", - version := "2.0.0-pre-alpha.20", + version := "2.0.0-alpha.20", crossPaths := false, scalaVersion := "2.11.8", findbugsReportType := Some(ReportType.FancyHtml), From 14e4f23d5a8a2d4651d9f29a8a221e01d7266a33 Mon Sep 17 00:00:00 2001 From: Benjamin Friedrich Date: Tue, 9 Aug 2016 18:01:13 +0200 Subject: [PATCH 173/183] #102 added contribution guidelines --- CONTRIBUTING.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..f42d75e --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,13 @@ +# Contributing + +## Pull requests only + +**DON'T** push to the master branch directly. Always use feature branches and let people discuss changes in pull requests. +Pull requests should only be merged after all discussions have been concluded and at least 1 reviewer has given their +**approval**. + +## Guidelines + +- **every change** needs a test +- 100% code coverage +- keep the current code style From 43a08906496740be9ed4e57a8fa652cbf4021fac Mon Sep 17 00:00:00 2001 From: Younes CHAHBI Date: Thu, 22 Sep 2016 19:55:03 +0200 Subject: [PATCH 174/183] issue-112 Correcting the bug of not using the serializer during the publishing of the event. --- .../nakadi/client/scala/ClientImpl.scala | 2 +- .../nakadi/client/scala/ScalaClientTest.scala | 17 ++++++++++++++++- .../client/scala/model/ModelFactory.scala | 3 ++- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala index d81bc85..ba7a144 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/scala/ClientImpl.scala @@ -69,7 +69,7 @@ class ClientImpl(connection: Connection, subscriber: SubscriptionHandler, charSe events: Seq[T], ser: Serializer[Seq[T]]): Future[Option[ClientError]] = { logFutureOption( - connection.post(URI_EVENTS_OF_EVENT_TYPE.format(eventTypeName), events).flatMap(in => mapToOption(in))) + connection.post(URI_EVENTS_OF_EVENT_TYPE.format(eventTypeName), events)(ser).flatMap(in => mapToOption(in))) } def publishEvents[T <: Event](eventTypeName: String, events: Seq[T]): Future[Option[ClientError]] = { logFutureOption( diff --git a/client/src/test/scala/org/zalando/nakadi/client/scala/ScalaClientTest.scala b/client/src/test/scala/org/zalando/nakadi/client/scala/ScalaClientTest.scala index bc7b1a9..9932800 100644 --- a/client/src/test/scala/org/zalando/nakadi/client/scala/ScalaClientTest.scala +++ b/client/src/test/scala/org/zalando/nakadi/client/scala/ScalaClientTest.scala @@ -1,5 +1,6 @@ package org.zalando.nakadi.client.scala + import scala.concurrent.Future import scala.concurrent.Await import scala.concurrent.duration.DurationInt @@ -28,7 +29,7 @@ import org.zalando.nakadi.client.utils.Uri import org.zalando.nakadi.client.handler.SubscriptionHandlerImpl import org.zalando.nakadi.client.handler.SubscriptionHandler import org.mockito.ArgumentCaptor -import org.zalando.nakadi.client.Deserializer +import org.zalando.nakadi.client.{Serializer, Deserializer} class ScalaClientTest extends WordSpec with Matchers with MockitoSugar with BeforeAndAfter { import ScalaJacksonJsonMarshaller._ @@ -54,6 +55,20 @@ class ScalaClientTest extends WordSpec with Matchers with MockitoSugar with Befo val result = Await.result(client.getEventType(eventTypeName), 5.seconds) result shouldBe Right(None) } + "Serializer work when publishing events" in { + val headers = Nil + val entity = HttpEntity(ContentTypes.`application/json`, "{}") + val response = new HttpResponse(StatusCodes.OK, headers, entity, HttpProtocols.`HTTP/1.1`) + val futureResponse = Future.successful(response) + val serializer = new Serializer[Seq[MySimpleEvent]] { + override def to(from: Seq[MySimpleEvent]): String = from.map(x => + s"""{"order_number":"${x.orderId}}""".stripMargin).mkString("[",",","]") + } + val event = MySimpleEvent("test") + when(connection.post("/event-types/EventTypeName/events",Seq(event))(serializer)).thenReturn(futureResponse) + val result = Await.result(client.publishEvents(eventTypeName,Seq(event),serializer), 5.seconds) + result shouldBe None + } "Pass the streamParameters correctly to the subscriptionHandler" in { val partition = "partition" diff --git a/client/src/test/scala/org/zalando/nakadi/client/scala/model/ModelFactory.scala b/client/src/test/scala/org/zalando/nakadi/client/scala/model/ModelFactory.scala index dd8123c..6a6ecb5 100644 --- a/client/src/test/scala/org/zalando/nakadi/client/scala/model/ModelFactory.scala +++ b/client/src/test/scala/org/zalando/nakadi/client/scala/model/ModelFactory.scala @@ -69,4 +69,5 @@ object ModelFactory { List(randomUUID(), randomUUID()), //dataKeyFields Option(newEventTypeStatistics)) -} \ No newline at end of file +} +case class MySimpleEvent(orderId: String) extends Event From 7b242cf29447da07b21ba03a22e8506db98ef1db Mon Sep 17 00:00:00 2001 From: Younes CHAHBI Date: Fri, 23 Sep 2016 14:03:32 +0200 Subject: [PATCH 175/183] issue-111 Ask the nakadi client to resubscribe when an error of 500 is getting inside the listening channel. --- .../scala/org/zalando/nakadi/client/actor/ConsumingActor.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/client/src/main/scala/org/zalando/nakadi/client/actor/ConsumingActor.scala b/client/src/main/scala/org/zalando/nakadi/client/actor/ConsumingActor.scala index 489cb0e..d078ccc 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/actor/ConsumingActor.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/actor/ConsumingActor.scala @@ -59,6 +59,7 @@ class ConsumingActor(subscription: SubscriptionKey, handler: EventHandler) context.parent ! OffsetMsg(cursor, subscription) case Left(error) => log.error(error.error.getMessage) + context.stop(self) } case OnError(err: Throwable) => log.error("onError - cursor [{}] - [{}] - error [{}]", lastCursor, subscription, err.getMessage) From 4f4b08361df967890ed6bdc0195736084e1962c8 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Mon, 26 Sep 2016 08:48:13 +0200 Subject: [PATCH 176/183] Update Build.scala --- project/Build.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/Build.scala b/project/Build.scala index db85a70..7a6fe74 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -62,7 +62,7 @@ lazy val client = withDefaults( .settings( name := projectName, organization := "org.zalando.nakadi.client", - version := "2.0.0-alpha.20", + version := "2.0", crossPaths := false, scalaVersion := "2.11.8", findbugsReportType := Some(ReportType.FancyHtml), From 3367d15e14f38ffbca265438cdc052ccd5fbd83c Mon Sep 17 00:00:00 2001 From: Lars Fischer Date: Mon, 26 Sep 2016 11:45:40 +0200 Subject: [PATCH 177/183] #114 moved logback.xml from client to it module to prevent confusing logback with multiple config files in dependent applications --- {client => it}/src/main/resources/logback.xml | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {client => it}/src/main/resources/logback.xml (100%) diff --git a/client/src/main/resources/logback.xml b/it/src/main/resources/logback.xml similarity index 100% rename from client/src/main/resources/logback.xml rename to it/src/main/resources/logback.xml From c88a5f2e28af01bb6a5b72c16ea9a0dfc2460dfc Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Mon, 26 Sep 2016 13:05:57 +0200 Subject: [PATCH 178/183] Release 2.0.1 --- project/Build.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/Build.scala b/project/Build.scala index 7a6fe74..82cf7ea 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -62,7 +62,7 @@ lazy val client = withDefaults( .settings( name := projectName, organization := "org.zalando.nakadi.client", - version := "2.0", + version := "2.0.1", crossPaths := false, scalaVersion := "2.11.8", findbugsReportType := Some(ReportType.FancyHtml), From c23168505c4808fdcfc20250ee65eb3c864fe5d6 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Thu, 27 Oct 2016 13:55:08 +0200 Subject: [PATCH 179/183] Fixes #118 1. Renames the json default_statiscs property to default_statistic. --- .../java/org/zalando/nakadi/client/java/model/EventType.java | 4 ++-- .../scala/org/zalando/nakadi/client/scala/model/Model.scala | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/api/src/main/java/org/zalando/nakadi/client/java/model/EventType.java b/api/src/main/java/org/zalando/nakadi/client/java/model/EventType.java index 4889660..b2ffc2c 100644 --- a/api/src/main/java/org/zalando/nakadi/client/java/model/EventType.java +++ b/api/src/main/java/org/zalando/nakadi/client/java/model/EventType.java @@ -56,7 +56,7 @@ public class EventType { public EventType(@JsonProperty("name") String name, @JsonProperty("owning_application") String owningApplication, @JsonProperty("category") EventTypeCategory category, @JsonProperty("enrichment_strategies") List enrichmentStrategies, @JsonProperty("partition_strategy") PartitionStrategy partitionStrategy, @JsonProperty("schema") EventTypeSchema schema, @JsonProperty("data_key_fields") List dataKeyFields, - @JsonProperty("partition_key_fields") List partitionKeyFields, @JsonProperty("default_statistics") EventTypeStatistics statistics) { + @JsonProperty("partition_key_fields") List partitionKeyFields, @JsonProperty("default_statistic") EventTypeStatistics statistics) { this.name = name; this.owningApplication = owningApplication; this.category = category; @@ -109,7 +109,7 @@ public List getDataKeyFields() { public List getPartitionKeyFields() { return unmodifiableList(partitionKeyFields); } - + @JsonProperty("default_statistic") public EventTypeStatistics getStatistics() { return statistics; } diff --git a/api/src/main/scala/org/zalando/nakadi/client/scala/model/Model.scala b/api/src/main/scala/org/zalando/nakadi/client/scala/model/Model.scala index d01b346..fd95b7d 100644 --- a/api/src/main/scala/org/zalando/nakadi/client/scala/model/Model.scala +++ b/api/src/main/scala/org/zalando/nakadi/client/scala/model/Model.scala @@ -135,7 +135,7 @@ case class EventType( schema: EventTypeSchema, dataKeyFields: Seq[String], partitionKeyFields: Seq[String], - @JsonProperty("default_statistics") statistics: Option[EventTypeStatistics]) + @JsonProperty("default_statistic") statistics: Option[EventTypeStatistics]) /** * The schema for an EventType, expected to be a json schema in yaml From 83932e91c949122630f72c5f8053d9c8b1fb543f Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Thu, 27 Oct 2016 13:57:57 +0200 Subject: [PATCH 180/183] #118 Small fixes to get the integration tests going again. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. owning_application field must start with ‘stups_’. 2. Refactoring of the ClientFactory to read config from Env. 3. Added integration tests that check the created statistics in the eventType. --- .../generator/EventGeneratorBuilder.java | 2 +- .../nakadi/client/scala/ClientFactory.scala | 37 ++++----------- .../scala/test/factory/EventGenerator.scala | 2 +- .../zalando/client/java/SimpleEventTest.java | 47 +++++++++++++++++-- .../nakadi/client/scala/SimpleEventTest.scala | 43 +++++++++++++++-- 5 files changed, 93 insertions(+), 38 deletions(-) diff --git a/it/src/main/java/org/zalando/nakadi/client/java/test/event/generator/EventGeneratorBuilder.java b/it/src/main/java/org/zalando/nakadi/client/java/test/event/generator/EventGeneratorBuilder.java index b0e9a8d..5fae512 100644 --- a/it/src/main/java/org/zalando/nakadi/client/java/test/event/generator/EventGeneratorBuilder.java +++ b/it/src/main/java/org/zalando/nakadi/client/java/test/event/generator/EventGeneratorBuilder.java @@ -19,7 +19,7 @@ public abstract class EventGeneratorBuilder { private Event newEvent; private String schemaDefinition; private EventType eventType = null; - private String owner = "Nakadi-klients(java-integration-test-suite)"; + private String owner = "stups_Nakadi-klients(java-integration-test-suite)"; private EventTypeCategory category = EventTypeCategory.UNDEFINED; private List enrichmentStrategies = Collections.emptyList(); private PartitionStrategy partitionStrategy = PartitionStrategy.RANDOM; diff --git a/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala b/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala index 9985279..4bb18d0 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/scala/ClientFactory.scala @@ -1,39 +1,22 @@ package org.zalando.nakadi.client.scala import org.zalando.nakadi.client.utils.ClientBuilder -import java.util.function.Supplier object ClientFactory { - import sys.process._ - import scala.language.postfixOps - def OAuth2Token(): Option[() => String] = - Option(() => "*") - def getJavaClient() = - builder().buildJavaClient(); + def OAuth2Token(): Option[() => String] = Option(() => System.getProperty("OAUTH2_ACCESS_TOKENS", null)); + def getJavaClient() = builder().buildJavaClient(); + def host() = System.getProperty("NAKADI_HOST", "localhost") + def port() = System.getProperty("NAKADI_PORT", "8080").toInt + def verifySSLCertificate() = System.getProperty("NAKADI_VERIFY_SSL_CERTIFICATE", "false").toBoolean + def securedConnection() = System.getProperty("NAKADI_SECURED_CONNECTION", "false").toBoolean def getScalaClient() = builder().build() private def builder() = { - // useSandbox() - // useStaging() - useLocal() - } - private def useLocal() = { new ClientBuilder() // - .withHost("localhost") // - .withPort(8080) // - .withSecuredConnection(false) // s - .withVerifiedSslCertificate(false) // s - } - private def useSandbox() = { - ClientBuilder() - .withHost("nakadi-sandbox.aruha-test.zalan.do") - .withPort(443) - .withSecuredConnection(true) //s - .withVerifiedSslCertificate(false) //s - .withTokenProvider(ClientFactory.OAuth2Token()) - } - private def useStaging() = { - useSandbox().withHost("nakadi-staging.aruha-test.zalan.do") + .withHost(host()) // + .withPort(port()) // + .withSecuredConnection(securedConnection()) // s + .withVerifiedSslCertificate(verifySSLCertificate()) // s } } diff --git a/it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/EventGenerator.scala b/it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/EventGenerator.scala index f2046f5..aa4ebfb 100644 --- a/it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/EventGenerator.scala +++ b/it/src/main/scala/org/zalando/nakadi/client/scala/test/factory/EventGenerator.scala @@ -63,7 +63,7 @@ trait EventGenerator { /** * Returns the owningApplication value. Default = "Nakadi-klients(integration-test-suite)" */ - def owner: String = "Nakadi-klients(integration-test-suite)" + def owner: String = "stups_Nakadi-klients(integration-test-suite)" /** * Returns the category value. Default = UNDEFINED diff --git a/it/src/test/java/org/zalando/client/java/SimpleEventTest.java b/it/src/test/java/org/zalando/client/java/SimpleEventTest.java index f37cf4e..966918c 100644 --- a/it/src/test/java/org/zalando/client/java/SimpleEventTest.java +++ b/it/src/test/java/org/zalando/client/java/SimpleEventTest.java @@ -1,7 +1,6 @@ package org.zalando.client.java; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; import java.util.List; import java.util.Optional; @@ -16,6 +15,7 @@ import org.zalando.nakadi.client.java.model.Event; import org.zalando.nakadi.client.java.model.EventStreamBatch; import org.zalando.nakadi.client.java.model.EventType; +import org.zalando.nakadi.client.java.model.EventTypeStatistics; import org.zalando.nakadi.client.java.test.event.generator.EventGenerator; import org.zalando.nakadi.client.java.test.event.generator.EventIntegrationHelper; import org.zalando.nakadi.client.java.test.event.simple.MySimpleEvent; @@ -58,7 +58,7 @@ public void validatePublishedNrOfEvents() throws InterruptedException, Execution .withEventTypeId("SimpleEventTest-validatePublishedNrOfEvents").build(); EventIntegrationHelper it = new EventIntegrationHelper(gen, client); assertTrue("EventType should be created", it.createEventType()); - Thread.sleep(1000);// Creation can take time. + Thread.sleep(5000);// Creation can take time. Optional eventTypeOpt = it.getEventType(); assertTrue("Did not return the eventType", eventTypeOpt.isPresent()); List createdEvents = it.publishEvents(nrOfEvents); @@ -133,7 +133,46 @@ public void validateCreatedEventType() throws InterruptedException { assertEquals(eventType.getPartitionStrategy(), originalEventType.getPartitionStrategy()); assertEquals(eventType.getSchema().getSchema(), originalEventType.getSchema().getSchema()); assertEquals(eventType.getSchema().getType(), originalEventType.getSchema().getType()); - assertEquals(eventType.getStatistics(), originalEventType.getStatistics()); + assertNull(eventType.getStatistics()); + } + @Test + public void validateCreatedEventTypeWithStatistics() throws InterruptedException { + Integer messagesPerMinute = 2400; + Integer messageSize = 20240; + Integer readParallelism = 8; + Integer writeParallelism = 7; + EventGenerator gen = new MySimpleEventGenerator(){ + @Override + public EventTypeStatistics getStatistics() { + return new EventTypeStatistics(messagesPerMinute, messageSize, readParallelism, writeParallelism); + } + } + .withEventTypeId("SimpleEventTest-validateCreatedEventType").build(); + EventIntegrationHelper it = new EventIntegrationHelper(gen, client); + assertTrue("EventType should be created", it.createEventType()); + Thread.sleep(1000);// Creation can take time. + Optional eventTypeOpt = it.getEventType(); + assertTrue("Did not return the eventType", eventTypeOpt.isPresent()); + + EventType originalEventType = it.getEventType().get(); + EventType eventType = eventTypeOpt.get(); + assertEquals(eventType.getCategory(), originalEventType.getCategory()); + assertEquals(eventType.getDataKeyFields(), originalEventType.getDataKeyFields()); + assertEquals(eventType.getName(), originalEventType.getName()); + assertEquals(eventType.getOwningApplication(), originalEventType.getOwningApplication()); + assertEquals(eventType.getPartitionKeyFields(), originalEventType.getPartitionKeyFields()); + assertEquals(eventType.getPartitionStrategy(), originalEventType.getPartitionStrategy()); + assertEquals(eventType.getSchema().getSchema(), originalEventType.getSchema().getSchema()); + assertEquals(eventType.getSchema().getType(), originalEventType.getSchema().getType()); + assertNotNull(eventType.getStatistics()); + assertEquals(messageSize, originalEventType.getStatistics().getMessageSize()); + assertEquals(messagesPerMinute, originalEventType.getStatistics().getMessagesPerMinute()); + assertEquals(readParallelism, originalEventType.getStatistics().getReadParallelism()); + assertEquals(writeParallelism, originalEventType.getStatistics().getWriteParallelism()); + assertEquals(eventType.getStatistics().getMessageSize(), originalEventType.getStatistics().getMessageSize()); + assertEquals(eventType.getStatistics().getMessagesPerMinute(), originalEventType.getStatistics().getMessagesPerMinute()); + assertEquals(eventType.getStatistics().getReadParallelism(), originalEventType.getStatistics().getReadParallelism()); + assertEquals(eventType.getStatistics().getWriteParallelism(), originalEventType.getStatistics().getWriteParallelism()); } @Test diff --git a/it/src/test/scala/org/zalando/nakadi/client/scala/SimpleEventTest.scala b/it/src/test/scala/org/zalando/nakadi/client/scala/SimpleEventTest.scala index 1a680f5..83d89b7 100644 --- a/it/src/test/scala/org/zalando/nakadi/client/scala/SimpleEventTest.scala +++ b/it/src/test/scala/org/zalando/nakadi/client/scala/SimpleEventTest.scala @@ -1,20 +1,20 @@ package org.zalando.nakadi.client.scala +import org.scalatest.BeforeAndAfterAll import org.scalatest.Matchers import org.scalatest.WordSpec import org.zalando.nakadi.client.scala.model.Cursor -import org.zalando.nakadi.client.scala.model.ScalaJacksonJsonMarshaller +import org.zalando.nakadi.client.scala.model.EventEnrichmentStrategy +import org.zalando.nakadi.client.scala.model.PartitionStrategy +import org.zalando.nakadi.client.scala.model.EventTypeStatistics import org.zalando.nakadi.client.scala.model.PartitionStrategyType +import org.zalando.nakadi.client.scala.model.ScalaJacksonJsonMarshaller import org.zalando.nakadi.client.scala.test.factory.EventIntegrationHelper import org.zalando.nakadi.client.scala.test.factory.events.MySimpleEvent import org.zalando.nakadi.client.scala.test.factory.events.SimpleEventListener -import org.zalando.nakadi.client.scala.model.PartitionStrategy -import org.scalatest.BeforeAndAfterAll -import org.zalando.nakadi.client.scala.model.EventEnrichmentStrategy class SimpleEventTest extends WordSpec with Matchers with BeforeAndAfterAll { - import org.scalatest.Matchers._ import ClientFactory._ import ScalaJacksonJsonMarshaller._ import MySimpleEvent._ @@ -48,6 +48,7 @@ class SimpleEventTest extends WordSpec with Matchers with BeforeAndAfterAll { val listener = new SimpleEventListener() it.createEventType() shouldBe true + Thread.sleep(2000) val events = it.publishEvents(nrOfEvents) client.subscribe(eventGenerator.eventTypeName, StreamParameters(cursor = cursor), listener) val receivedEvents = listener.waitToReceive(nrOfEvents) @@ -121,6 +122,38 @@ class SimpleEventTest extends WordSpec with Matchers with BeforeAndAfterAll { eventType.enrichmentStrategies shouldBe it.eventType.enrichmentStrategies eventType.partitionKeyFields shouldBe it.eventType.partitionKeyFields } + "Validate created EventType wiht Statistics" in { + val eventGenerator = new DefaultMySimpleEventGenerator() { + def eventTypeId = s"SimpleEventIntegrationTest-Validate-Created-EventType" + override def statistics: Option[EventTypeStatistics] = Option(EventTypeStatistics(2400, 20240, 4, 4)) + } + val it = new EventIntegrationHelper(eventGenerator, client) + + it.createEventType() shouldBe true + + Thread.sleep(5000) + + val optionalOfCreatedEventType = it.getEventType() + + optionalOfCreatedEventType.isDefined shouldBe true + + val Some(eventType) = optionalOfCreatedEventType + eventType.category shouldBe it.eventType.category + eventType.dataKeyFields shouldBe null + eventType.name shouldBe it.eventType.name + eventType.owningApplication shouldBe it.eventType.owningApplication + eventType.partitionStrategy shouldBe it.eventType.partitionStrategy + eventType.schema shouldBe it.eventType.schema + eventType.statistics shouldBe it.eventType.statistics + eventType.enrichmentStrategies shouldBe it.eventType.enrichmentStrategies + eventType.partitionKeyFields shouldBe it.eventType.partitionKeyFields + eventType.statistics should not be null + eventType.statistics.get.messageSize shouldBe it.eventType.statistics.get.messageSize + eventType.statistics.get.messagesPerMinute shouldBe it.eventType.statistics.get.messagesPerMinute + eventType.statistics.get.readParallelism shouldBe it.eventType.statistics.get.readParallelism + eventType.statistics.get.writeParallelism shouldBe it.eventType.statistics.get.writeParallelism + + } "Validate nr of partitions after Creation of EventType" in { val eventGenerator = new DefaultMySimpleEventGenerator() { From 45d2cfd937281a6d029b91eba7c9ea3b4af746e4 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Thu, 27 Oct 2016 14:16:21 +0200 Subject: [PATCH 181/183] Debug will not expose OAuth tokens anymore. Fixes #109 --- .../main/scala/org/zalando/nakadi/client/scala/Connection.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala b/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala index c8d2815..c0651b8 100644 --- a/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala +++ b/client/src/main/scala/org/zalando/nakadi/client/scala/Connection.scala @@ -154,7 +154,7 @@ sealed class ConnectionImpl(val host: String, } def executeCall(request: HttpRequest): Future[HttpResponse] = { - logger.debug("executingCall {}", request) + logger.debug("executingCall {}", request.method) val response: Future[HttpResponse] = Source.single(request).via(requestFlow).runWith(Sink.head)(materializer()) logError(response) response From bec73808ef558dd9cd99ae505e753b07e0437737 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Thu, 27 Oct 2016 14:16:42 +0200 Subject: [PATCH 182/183] #118 New release 2.0.2 --- project/Build.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/Build.scala b/project/Build.scala index 82cf7ea..f3c0101 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -62,7 +62,7 @@ lazy val client = withDefaults( .settings( name := projectName, organization := "org.zalando.nakadi.client", - version := "2.0.1", + version := "2.0.2", crossPaths := false, scalaVersion := "2.11.8", findbugsReportType := Some(ReportType.FancyHtml), From 07d7dfbeb5109b43d0727e708df18ea6c0a8d981 Mon Sep 17 00:00:00 2001 From: Fernando Benjamin Date: Thu, 27 Oct 2016 14:35:12 +0200 Subject: [PATCH 183/183] Generate the JSON property event_type instead of event_type_name. Fixes #108 --- .../java/org/zalando/nakadi/client/java/model/EventMetadata.java | 1 + 1 file changed, 1 insertion(+) diff --git a/api/src/main/java/org/zalando/nakadi/client/java/model/EventMetadata.java b/api/src/main/java/org/zalando/nakadi/client/java/model/EventMetadata.java index 19909c1..f48dde1 100644 --- a/api/src/main/java/org/zalando/nakadi/client/java/model/EventMetadata.java +++ b/api/src/main/java/org/zalando/nakadi/client/java/model/EventMetadata.java @@ -77,6 +77,7 @@ public String getEid() { return eid; } + @JsonProperty("event_type") public String getEventTypeName() { return eventTypeName; }

        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%0DKW+ta*XnU?-;_marKCU0SHUf6zr*#FqD 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≶B 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%0DKW+ta*XnU?-;_marKCU0SHUf6zr*#FqD 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≶B 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